mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-13 02:49:02 +08:00
reasoning model unified think tag is <think></think> (#13392)
Co-authored-by: crazywoola <427733928@qq.com>
This commit is contained in:
parent
78708eb5d5
commit
286cdc41ab
@ -30,11 +30,6 @@ from core.model_runtime.model_providers.__base.ai_model import AIModel
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
HTML_THINKING_TAG = (
|
|
||||||
'<details style="color:gray;background-color: #f8f8f8;padding: 8px;border-radius: 4px;" open> '
|
|
||||||
"<summary> Thinking... </summary>"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class LargeLanguageModel(AIModel):
|
class LargeLanguageModel(AIModel):
|
||||||
"""
|
"""
|
||||||
@ -408,7 +403,7 @@ if you are not sure about the structure.
|
|||||||
def _wrap_thinking_by_reasoning_content(self, delta: dict, is_reasoning: bool) -> tuple[str, bool]:
|
def _wrap_thinking_by_reasoning_content(self, delta: dict, is_reasoning: bool) -> tuple[str, bool]:
|
||||||
"""
|
"""
|
||||||
If the reasoning response is from delta.get("reasoning_content"), we wrap
|
If the reasoning response is from delta.get("reasoning_content"), we wrap
|
||||||
it with HTML details tag.
|
it with HTML think tag.
|
||||||
|
|
||||||
:param delta: delta dictionary from LLM streaming response
|
:param delta: delta dictionary from LLM streaming response
|
||||||
:param is_reasoning: is reasoning
|
:param is_reasoning: is reasoning
|
||||||
@ -420,25 +415,15 @@ if you are not sure about the structure.
|
|||||||
|
|
||||||
if reasoning_content:
|
if reasoning_content:
|
||||||
if not is_reasoning:
|
if not is_reasoning:
|
||||||
content = HTML_THINKING_TAG + reasoning_content
|
content = "<think>\n" + reasoning_content
|
||||||
is_reasoning = True
|
is_reasoning = True
|
||||||
else:
|
else:
|
||||||
content = reasoning_content
|
content = reasoning_content
|
||||||
elif is_reasoning:
|
elif is_reasoning:
|
||||||
content = "</details>" + content
|
content = "\n</think>" + content
|
||||||
is_reasoning = False
|
is_reasoning = False
|
||||||
return content, is_reasoning
|
return content, is_reasoning
|
||||||
|
|
||||||
def _wrap_thinking_by_tag(self, content: str) -> str:
|
|
||||||
"""
|
|
||||||
if the reasoning response is a <think>...</think> block from delta.get("content"),
|
|
||||||
we replace <think> to <detail>.
|
|
||||||
|
|
||||||
:param content: delta.get("content")
|
|
||||||
:return: processed_content
|
|
||||||
"""
|
|
||||||
return content.replace("<think>", HTML_THINKING_TAG).replace("</think>", "</details>")
|
|
||||||
|
|
||||||
def _invoke_result_generator(
|
def _invoke_result_generator(
|
||||||
self,
|
self,
|
||||||
model: str,
|
model: str,
|
||||||
|
@ -367,7 +367,6 @@ class OllamaLargeLanguageModel(LargeLanguageModel):
|
|||||||
|
|
||||||
# transform assistant message to prompt message
|
# transform assistant message to prompt message
|
||||||
text = chunk_json["response"]
|
text = chunk_json["response"]
|
||||||
text = self._wrap_thinking_by_tag(text)
|
|
||||||
|
|
||||||
assistant_prompt_message = AssistantPromptMessage(content=text)
|
assistant_prompt_message = AssistantPromptMessage(content=text)
|
||||||
|
|
||||||
|
@ -528,7 +528,6 @@ class OAIAPICompatLargeLanguageModel(_CommonOaiApiCompat, LargeLanguageModel):
|
|||||||
delta_content, is_reasoning_started = self._wrap_thinking_by_reasoning_content(
|
delta_content, is_reasoning_started = self._wrap_thinking_by_reasoning_content(
|
||||||
delta, is_reasoning_started
|
delta, is_reasoning_started
|
||||||
)
|
)
|
||||||
delta_content = self._wrap_thinking_by_tag(delta_content)
|
|
||||||
|
|
||||||
assistant_message_tool_calls = None
|
assistant_message_tool_calls = None
|
||||||
|
|
||||||
|
@ -654,7 +654,6 @@ class XinferenceAILargeLanguageModel(LargeLanguageModel):
|
|||||||
if function_call:
|
if function_call:
|
||||||
assistant_message_tool_calls += [self._extract_response_function_call(function_call)]
|
assistant_message_tool_calls += [self._extract_response_function_call(function_call)]
|
||||||
|
|
||||||
delta_content = self._wrap_thinking_by_tag(delta_content)
|
|
||||||
# transform assistant message to prompt message
|
# transform assistant message to prompt message
|
||||||
assistant_prompt_message = AssistantPromptMessage(
|
assistant_prompt_message = AssistantPromptMessage(
|
||||||
content=delta_content or "", tool_calls=assistant_message_tool_calls
|
content=delta_content or "", tool_calls=assistant_message_tool_calls
|
||||||
|
@ -10,6 +10,7 @@ import SyntaxHighlighter from 'react-syntax-highlighter'
|
|||||||
import { atelierHeathLight } from 'react-syntax-highlighter/dist/esm/styles/hljs'
|
import { atelierHeathLight } from 'react-syntax-highlighter/dist/esm/styles/hljs'
|
||||||
import { Component, memo, useMemo, useRef, useState } from 'react'
|
import { Component, memo, useMemo, useRef, useState } from 'react'
|
||||||
import type { CodeComponent } from 'react-markdown/lib/ast-to-react'
|
import type { CodeComponent } from 'react-markdown/lib/ast-to-react'
|
||||||
|
import { flow } from 'lodash/fp'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import CopyBtn from '@/app/components/base/copy-btn'
|
import CopyBtn from '@/app/components/base/copy-btn'
|
||||||
import SVGBtn from '@/app/components/base/svg'
|
import SVGBtn from '@/app/components/base/svg'
|
||||||
@ -58,9 +59,24 @@ const getCorrectCapitalizationLanguageName = (language: string) => {
|
|||||||
const preprocessLaTeX = (content: string) => {
|
const preprocessLaTeX = (content: string) => {
|
||||||
if (typeof content !== 'string')
|
if (typeof content !== 'string')
|
||||||
return content
|
return content
|
||||||
return content.replace(/\\\[(.*?)\\\]/g, (_, equation) => `$$${equation}$$`)
|
|
||||||
.replace(/\\\((.*?)\\\)/g, (_, equation) => `$$${equation}$$`)
|
return flow([
|
||||||
.replace(/(^|[^\\])\$(.+?)\$/g, (_, prefix, equation) => `${prefix}$${equation}$`)
|
(str: string) => str.replace(/\\\[(.*?)\\\]/g, (_, equation) => `$$${equation}$$`),
|
||||||
|
(str: string) => str.replace(/\\\((.*?)\\\)/g, (_, equation) => `$$${equation}$$`),
|
||||||
|
(str: string) => str.replace(/(^|[^\\])\$(.+?)\$/g, (_, prefix, equation) => `${prefix}$${equation}$`),
|
||||||
|
])(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
const preprocessThinkTag = (content: string) => {
|
||||||
|
if (!content.trim().startsWith('<think>\n'))
|
||||||
|
return content
|
||||||
|
|
||||||
|
return flow([
|
||||||
|
(str: string) => str.replace('<think>\n', '<details open><summary class="text-gray-500 font-bold">Thinking</summary><div class="text-gray-500 p-3 ml-2 bg-gray-50 border-l border-gray-300">\n'),
|
||||||
|
(str: string) => str.includes('\n</think>')
|
||||||
|
? str.replace('\n</think>', '\n</div></details>')
|
||||||
|
: `${str}\n</div></details>`,
|
||||||
|
])(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PreCode(props: { children: any }) {
|
export function PreCode(props: { children: any }) {
|
||||||
@ -225,7 +241,10 @@ const Link = ({ node, ...props }: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Markdown(props: { content: string; className?: string }) {
|
export function Markdown(props: { content: string; className?: string }) {
|
||||||
const latexContent = preprocessLaTeX(props.content)
|
const latexContent = flow([
|
||||||
|
preprocessThinkTag,
|
||||||
|
preprocessLaTeX,
|
||||||
|
])(props.content)
|
||||||
return (
|
return (
|
||||||
<div className={cn(props.className, 'markdown-body')}>
|
<div className={cn(props.className, 'markdown-body')}>
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
|
Loading…
x
Reference in New Issue
Block a user