From edf4a1b652ab01a3a93e805b099e9cde6a70421e Mon Sep 17 00:00:00 2001 From: taewoong Kim <116135174+ultramancode@users.noreply.github.com> Date: Fri, 5 Sep 2025 19:15:35 +0900 Subject: [PATCH] feat: add reasoning format processing to LLMNode for tag handling (#23313) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../model_runtime/entities/llm_entities.py | 1 + api/core/workflow/nodes/event/event.py | 1 + api/core/workflow/nodes/llm/entities.py | 19 +++- api/core/workflow/nodes/llm/node.py | 102 +++++++++++++++++- .../core/workflow/nodes/llm/test_node.py | 64 +++++++++++ web/app/components/workflow/constants.ts | 4 + .../components/reasoning-format-config.tsx | 40 +++++++ .../components/workflow/nodes/llm/panel.tsx | 10 ++ .../components/workflow/nodes/llm/types.ts | 1 + .../workflow/nodes/llm/use-config.ts | 9 ++ web/i18n/de-DE/workflow.ts | 6 ++ web/i18n/en-US/workflow.ts | 6 ++ web/i18n/es-ES/workflow.ts | 6 ++ web/i18n/fa-IR/workflow.ts | 6 ++ web/i18n/fr-FR/workflow.ts | 6 ++ web/i18n/hi-IN/workflow.ts | 6 ++ web/i18n/it-IT/workflow.ts | 6 ++ web/i18n/ja-JP/workflow.ts | 6 ++ web/i18n/ko-KR/workflow.ts | 6 ++ web/i18n/pl-PL/workflow.ts | 6 ++ web/i18n/pt-BR/workflow.ts | 6 ++ web/i18n/ro-RO/workflow.ts | 6 ++ web/i18n/ru-RU/workflow.ts | 6 ++ web/i18n/sl-SI/workflow.ts | 6 ++ web/i18n/th-TH/workflow.ts | 6 ++ web/i18n/tr-TR/workflow.ts | 6 ++ web/i18n/uk-UA/workflow.ts | 6 ++ web/i18n/vi-VN/workflow.ts | 6 ++ web/i18n/zh-Hans/workflow.ts | 6 ++ web/i18n/zh-Hant/workflow.ts | 6 ++ 30 files changed, 366 insertions(+), 5 deletions(-) create mode 100644 web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx diff --git a/api/core/model_runtime/entities/llm_entities.py b/api/core/model_runtime/entities/llm_entities.py index dc6032e40..d5caddb7a 100644 --- a/api/core/model_runtime/entities/llm_entities.py +++ b/api/core/model_runtime/entities/llm_entities.py @@ -156,6 +156,7 @@ class LLMResult(BaseModel): message: AssistantPromptMessage usage: LLMUsage system_fingerprint: Optional[str] = None + reasoning_content: Optional[str] = None class LLMStructuredOutput(BaseModel): diff --git a/api/core/workflow/nodes/event/event.py b/api/core/workflow/nodes/event/event.py index 3ebe80f24..e33efbe50 100644 --- a/api/core/workflow/nodes/event/event.py +++ b/api/core/workflow/nodes/event/event.py @@ -30,6 +30,7 @@ class ModelInvokeCompletedEvent(BaseModel): text: str usage: LLMUsage finish_reason: str | None = None + reasoning_content: str | None = None class RunRetryEvent(BaseModel): diff --git a/api/core/workflow/nodes/llm/entities.py b/api/core/workflow/nodes/llm/entities.py index e6f8abeba..222914351 100644 --- a/api/core/workflow/nodes/llm/entities.py +++ b/api/core/workflow/nodes/llm/entities.py @@ -1,5 +1,5 @@ from collections.abc import Mapping, Sequence -from typing import Any, Optional +from typing import Any, Literal, Optional from pydantic import BaseModel, Field, field_validator @@ -68,6 +68,23 @@ class LLMNodeData(BaseNodeData): structured_output: Mapping[str, Any] | None = None # We used 'structured_output_enabled' in the past, but it's not a good name. structured_output_switch_on: bool = Field(False, alias="structured_output_enabled") + reasoning_format: Literal["separated", "tagged"] = Field( + # Keep tagged as default for backward compatibility + default="tagged", + description=( + """ + Strategy for handling model reasoning output. + + separated: Return clean text (without tags) + reasoning_content field. + Recommended for new workflows. Enables safe downstream parsing and + workflow variable access: {{#node_id.reasoning_content#}} + + tagged : Return original text (with tags) + reasoning_content field. + Maintains full backward compatibility while still providing reasoning_content + for workflow automation. Frontend thinking panels work as before. + """ + ), + ) @field_validator("prompt_config", mode="before") @classmethod diff --git a/api/core/workflow/nodes/llm/node.py b/api/core/workflow/nodes/llm/node.py index 10059fdcb..37c4ecfd6 100644 --- a/api/core/workflow/nodes/llm/node.py +++ b/api/core/workflow/nodes/llm/node.py @@ -2,8 +2,9 @@ import base64 import io import json import logging +import re from collections.abc import Generator, Mapping, Sequence -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Any, Literal, Optional, Union from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.file import FileType, file_manager @@ -99,6 +100,9 @@ class LLMNode(BaseNode): _node_data: LLMNodeData + # Compiled regex for extracting blocks (with compatibility for attributes) + _THINK_PATTERN = re.compile(r"]*>(.*?)", re.IGNORECASE | re.DOTALL) + # Instance attributes specific to LLMNode. # Output variable for file _file_outputs: list["File"] @@ -167,6 +171,7 @@ class LLMNode(BaseNode): result_text = "" usage = LLMUsage.empty_usage() finish_reason = None + reasoning_content = None variable_pool = self.graph_runtime_state.variable_pool try: @@ -256,6 +261,7 @@ class LLMNode(BaseNode): file_saver=self._llm_file_saver, file_outputs=self._file_outputs, node_id=self.node_id, + reasoning_format=self._node_data.reasoning_format, ) structured_output: LLMStructuredOutput | None = None @@ -264,9 +270,20 @@ class LLMNode(BaseNode): if isinstance(event, RunStreamChunkEvent): yield event elif isinstance(event, ModelInvokeCompletedEvent): + # Raw text result_text = event.text usage = event.usage finish_reason = event.finish_reason + reasoning_content = event.reasoning_content or "" + + # For downstream nodes, determine clean text based on reasoning_format + if self._node_data.reasoning_format == "tagged": + # Keep tags for backward compatibility + clean_text = result_text + else: + # Extract clean text from tags + clean_text, _ = LLMNode._split_reasoning(result_text, self._node_data.reasoning_format) + # deduct quota llm_utils.deduct_llm_quota(tenant_id=self.tenant_id, model_instance=model_instance, usage=usage) break @@ -284,7 +301,12 @@ class LLMNode(BaseNode): "model_name": model_config.model, } - outputs = {"text": result_text, "usage": jsonable_encoder(usage), "finish_reason": finish_reason} + outputs = { + "text": clean_text, + "reasoning_content": reasoning_content, + "usage": jsonable_encoder(usage), + "finish_reason": finish_reason, + } if structured_output: outputs["structured_output"] = structured_output.structured_output if self._file_outputs is not None: @@ -338,6 +360,7 @@ class LLMNode(BaseNode): file_saver: LLMFileSaver, file_outputs: list["File"], node_id: str, + reasoning_format: Literal["separated", "tagged"] = "tagged", ) -> Generator[NodeEvent | LLMStructuredOutput, None, None]: model_schema = model_instance.model_type_instance.get_model_schema( node_data_model.name, model_instance.credentials @@ -374,6 +397,7 @@ class LLMNode(BaseNode): file_saver=file_saver, file_outputs=file_outputs, node_id=node_id, + reasoning_format=reasoning_format, ) @staticmethod @@ -383,6 +407,7 @@ class LLMNode(BaseNode): file_saver: LLMFileSaver, file_outputs: list["File"], node_id: str, + reasoning_format: Literal["separated", "tagged"] = "tagged", ) -> Generator[NodeEvent | LLMStructuredOutput, None, None]: # For blocking mode if isinstance(invoke_result, LLMResult): @@ -390,6 +415,7 @@ class LLMNode(BaseNode): invoke_result=invoke_result, saver=file_saver, file_outputs=file_outputs, + reasoning_format=reasoning_format, ) yield event return @@ -430,13 +456,66 @@ class LLMNode(BaseNode): except OutputParserError as e: raise LLMNodeError(f"Failed to parse structured output: {e}") - yield ModelInvokeCompletedEvent(text=full_text_buffer.getvalue(), usage=usage, finish_reason=finish_reason) + # Extract reasoning content from tags in the main text + full_text = full_text_buffer.getvalue() + + if reasoning_format == "tagged": + # Keep tags in text for backward compatibility + clean_text = full_text + reasoning_content = "" + else: + # Extract clean text and reasoning from tags + clean_text, reasoning_content = LLMNode._split_reasoning(full_text, reasoning_format) + + yield ModelInvokeCompletedEvent( + # Use clean_text for separated mode, full_text for tagged mode + text=clean_text if reasoning_format == "separated" else full_text, + usage=usage, + finish_reason=finish_reason, + # Reasoning content for workflow variables and downstream nodes + reasoning_content=reasoning_content, + ) @staticmethod def _image_file_to_markdown(file: "File", /): text_chunk = f"![]({file.generate_url()})" return text_chunk + @classmethod + def _split_reasoning( + cls, text: str, reasoning_format: Literal["separated", "tagged"] = "tagged" + ) -> tuple[str, str]: + """ + Split reasoning content from text based on reasoning_format strategy. + + Args: + text: Full text that may contain blocks + reasoning_format: Strategy for handling reasoning content + - "separated": Remove tags and return clean text + reasoning_content field + - "tagged": Keep tags in text, return empty reasoning_content + + Returns: + tuple of (clean_text, reasoning_content) + """ + + if reasoning_format == "tagged": + return text, "" + + # Find all ... blocks (case-insensitive) + matches = cls._THINK_PATTERN.findall(text) + + # Extract reasoning content from all blocks + reasoning_content = "\n".join(match.strip() for match in matches) if matches else "" + + # Remove all ... blocks from original text + clean_text = cls._THINK_PATTERN.sub("", text) + + # Clean up extra whitespace + clean_text = re.sub(r"\n\s*\n", "\n\n", clean_text).strip() + + # Separated mode: always return clean text and reasoning_content + return clean_text, reasoning_content or "" + def _transform_chat_messages( self, messages: Sequence[LLMNodeChatModelMessage] | LLMNodeCompletionModelPromptTemplate, / ) -> Sequence[LLMNodeChatModelMessage] | LLMNodeCompletionModelPromptTemplate: @@ -964,6 +1043,7 @@ class LLMNode(BaseNode): invoke_result: LLMResult, saver: LLMFileSaver, file_outputs: list["File"], + reasoning_format: Literal["separated", "tagged"] = "tagged", ) -> ModelInvokeCompletedEvent: buffer = io.StringIO() for text_part in LLMNode._save_multimodal_output_and_convert_result_to_markdown( @@ -973,10 +1053,24 @@ class LLMNode(BaseNode): ): buffer.write(text_part) + # Extract reasoning content from tags in the main text + full_text = buffer.getvalue() + + if reasoning_format == "tagged": + # Keep tags in text for backward compatibility + clean_text = full_text + reasoning_content = "" + else: + # Extract clean text and reasoning from tags + clean_text, reasoning_content = LLMNode._split_reasoning(full_text, reasoning_format) + return ModelInvokeCompletedEvent( - text=buffer.getvalue(), + # Use clean_text for separated mode, full_text for tagged mode + text=clean_text if reasoning_format == "separated" else full_text, usage=invoke_result.usage, finish_reason=None, + # Reasoning content for workflow variables and downstream nodes + reasoning_content=reasoning_content, ) @staticmethod diff --git a/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py b/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py index 23a7fab7c..ea8a88692 100644 --- a/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py @@ -69,6 +69,7 @@ def llm_node_data() -> LLMNodeData: detail=ImagePromptMessageContent.DETAIL.HIGH, ), ), + reasoning_format="tagged", ) @@ -689,3 +690,66 @@ class TestSaveMultimodalOutputAndConvertResultToMarkdown: assert list(gen) == [] mock_file_saver.save_binary_string.assert_not_called() mock_file_saver.save_remote_url.assert_not_called() + + +class TestReasoningFormat: + """Test cases for reasoning_format functionality""" + + def test_split_reasoning_separated_mode(self): + """Test separated mode: tags are removed and content is extracted""" + + text_with_think = """ + I need to explain what Dify is. It's an open source AI platform. + Dify is an open source AI platform. + """ + + clean_text, reasoning_content = LLMNode._split_reasoning(text_with_think, "separated") + + assert clean_text == "Dify is an open source AI platform." + assert reasoning_content == "I need to explain what Dify is. It's an open source AI platform." + + def test_split_reasoning_tagged_mode(self): + """Test tagged mode: original text is preserved""" + + text_with_think = """ + I need to explain what Dify is. It's an open source AI platform. + Dify is an open source AI platform. + """ + + clean_text, reasoning_content = LLMNode._split_reasoning(text_with_think, "tagged") + + # Original text unchanged + assert clean_text == text_with_think + # Empty reasoning content in tagged mode + assert reasoning_content == "" + + def test_split_reasoning_no_think_blocks(self): + """Test behavior when no tags are present""" + + text_without_think = "This is a simple answer without any thinking blocks." + + clean_text, reasoning_content = LLMNode._split_reasoning(text_without_think, "separated") + + assert clean_text == text_without_think + assert reasoning_content == "" + + def test_reasoning_format_default_value(self): + """Test that reasoning_format defaults to 'tagged' for backward compatibility""" + + node_data = LLMNodeData( + title="Test LLM", + model=ModelConfig(provider="openai", name="gpt-3.5-turbo", mode="chat", completion_params={}), + prompt_template=[], + context=ContextConfig(enabled=False), + ) + + assert node_data.reasoning_format == "tagged" + + text_with_think = """ + I need to explain what Dify is. It's an open source AI platform. + Dify is an open source AI platform. + """ + clean_text, reasoning_content = LLMNode._split_reasoning(text_with_think, node_data.reasoning_format) + + assert clean_text == text_with_think + assert reasoning_content == "" diff --git a/web/app/components/workflow/constants.ts b/web/app/components/workflow/constants.ts index a17961620..04c2b7c68 100644 --- a/web/app/components/workflow/constants.ts +++ b/web/app/components/workflow/constants.ts @@ -479,6 +479,10 @@ export const LLM_OUTPUT_STRUCT: Var[] = [ variable: 'text', type: VarType.string, }, + { + variable: 'reasoning_content', + type: VarType.string, + }, { variable: 'usage', type: VarType.object, diff --git a/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx b/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx new file mode 100644 index 000000000..49425ff64 --- /dev/null +++ b/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx @@ -0,0 +1,40 @@ +import type { FC } from 'react' +import React from 'react' +import { useTranslation } from 'react-i18next' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import Switch from '@/app/components/base/switch' + +type ReasoningFormatConfigProps = { + value?: 'tagged' | 'separated' + onChange: (value: 'tagged' | 'separated') => void + readonly?: boolean +} + +const ReasoningFormatConfig: FC = ({ + value = 'tagged', + onChange, + readonly = false, +}) => { + const { t } = useTranslation() + + return ( + onChange(enabled ? 'separated' : 'tagged')} + size='md' + disabled={readonly} + key={value} + /> + } + > +
+ + ) +} + +export default ReasoningFormatConfig diff --git a/web/app/components/workflow/nodes/llm/panel.tsx b/web/app/components/workflow/nodes/llm/panel.tsx index 52bbf48b7..f5f5997ac 100644 --- a/web/app/components/workflow/nodes/llm/panel.tsx +++ b/web/app/components/workflow/nodes/llm/panel.tsx @@ -17,6 +17,7 @@ import type { NodePanelProps } from '@/app/components/workflow/types' import Tooltip from '@/app/components/base/tooltip' import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' import StructureOutput from './components/structure-output' +import ReasoningFormatConfig from './components/reasoning-format-config' import Switch from '@/app/components/base/switch' import { RiAlertFill, RiQuestionLine } from '@remixicon/react' import { fetchAndMergeValidCompletionParams } from '@/utils/completion-params' @@ -61,6 +62,7 @@ const Panel: FC> = ({ handleStructureOutputEnableChange, handleStructureOutputChange, filterJinja2InputVar, + handleReasoningFormatChange, } = useConfig(id, data) const model = inputs.model @@ -239,6 +241,14 @@ const Panel: FC> = ({ config={inputs.vision?.configs} onConfigChange={handleVisionResolutionChange} /> + + {/* Reasoning Format */} +
{ return [VarType.arrayObject, VarType.array, VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber, VarType.file, VarType.arrayFile].includes(varPayload.type) }, []) + // reasoning format + const handleReasoningFormatChange = useCallback((reasoningFormat: 'tagged' | 'separated') => { + const newInputs = produce(inputs, (draft) => { + draft.reasoning_format = reasoningFormat + }) + setInputs(newInputs) + }, [inputs, setInputs]) + const { availableVars, availableNodesWithParent, @@ -355,6 +363,7 @@ const useConfig = (id: string, payload: LLMNodeType) => { setStructuredOutputCollapsed, handleStructureOutputEnableChange, filterJinja2InputVar, + handleReasoningFormatChange, } } diff --git a/web/i18n/de-DE/workflow.ts b/web/i18n/de-DE/workflow.ts index 0050986d3..576afc2af 100644 --- a/web/i18n/de-DE/workflow.ts +++ b/web/i18n/de-DE/workflow.ts @@ -470,6 +470,12 @@ const translation = { instruction: 'Anleitung', regenerate: 'Regenerieren', }, + reasoningFormat: { + tooltip: 'Inhalte aus Denk-Tags extrahieren und im Feld reasoning_content speichern.', + separated: 'Separate Denk tags', + title: 'Aktivieren Sie die Trennung von Argumentations-Tags', + tagged: 'Behalte die Denk-Tags', + }, }, knowledgeRetrieval: { queryVariable: 'Abfragevariable', diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 14bd3d129..eae63e9c2 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -449,6 +449,12 @@ const translation = { variable: 'Variable', }, sysQueryInUser: 'sys.query in user message is required', + reasoningFormat: { + title: 'Enable reasoning tag separation', + tagged: 'Keep think tags', + separated: 'Separate think tags', + tooltip: 'Extract content from think tags and store it in the reasoning_content field.', + }, jsonSchema: { title: 'Structured Output Schema', instruction: 'Instruction', diff --git a/web/i18n/es-ES/workflow.ts b/web/i18n/es-ES/workflow.ts index fcab9c273..238eb016a 100644 --- a/web/i18n/es-ES/workflow.ts +++ b/web/i18n/es-ES/workflow.ts @@ -470,6 +470,12 @@ const translation = { import: 'Importar desde JSON', resetDefaults: 'Restablecer', }, + reasoningFormat: { + tagged: 'Mantén las etiquetas de pensamiento', + separated: 'Separar etiquetas de pensamiento', + title: 'Habilitar la separación de etiquetas de razonamiento', + tooltip: 'Extraer contenido de las etiquetas de pensamiento y almacenarlo en el campo reasoning_content.', + }, }, knowledgeRetrieval: { queryVariable: 'Variable de consulta', diff --git a/web/i18n/fa-IR/workflow.ts b/web/i18n/fa-IR/workflow.ts index 567f70cd1..1a2d9aa22 100644 --- a/web/i18n/fa-IR/workflow.ts +++ b/web/i18n/fa-IR/workflow.ts @@ -470,6 +470,12 @@ const translation = { fieldNamePlaceholder: 'نام میدان', generationTip: 'شما می‌توانید از زبان طبیعی برای ایجاد سریع یک طرح‌واره JSON استفاده کنید.', }, + reasoningFormat: { + separated: 'تگ‌های تفکر جداگانه', + title: 'فعال‌سازی جداسازی برچسب‌های استدلال', + tagged: 'به فکر برچسب‌ها باشید', + tooltip: 'محتوا را از تگ‌های تفکر استخراج کرده و در فیلد reasoning_content ذخیره کنید.', + }, }, knowledgeRetrieval: { queryVariable: 'متغیر جستجو', diff --git a/web/i18n/fr-FR/workflow.ts b/web/i18n/fr-FR/workflow.ts index 3874ff674..c2eb05619 100644 --- a/web/i18n/fr-FR/workflow.ts +++ b/web/i18n/fr-FR/workflow.ts @@ -470,6 +470,12 @@ const translation = { generateJsonSchema: 'Générer un schéma JSON', resultTip: 'Voici le résultat généré. Si vous n\'êtes pas satisfait, vous pouvez revenir en arrière et modifier votre demande.', }, + reasoningFormat: { + title: 'Activer la séparation des balises de raisonnement', + tagged: 'Gardez les étiquettes de pensée', + separated: 'Séparer les balises de réflexion', + tooltip: 'Extraire le contenu des balises think et le stocker dans le champ reasoning_content.', + }, }, knowledgeRetrieval: { queryVariable: 'Variable de requête', diff --git a/web/i18n/hi-IN/workflow.ts b/web/i18n/hi-IN/workflow.ts index b04d232e3..8df3e4b74 100644 --- a/web/i18n/hi-IN/workflow.ts +++ b/web/i18n/hi-IN/workflow.ts @@ -483,6 +483,12 @@ const translation = { required: 'आवश्यक', addChildField: 'बच्चे का क्षेत्र जोड़ें', }, + reasoningFormat: { + title: 'कारण संबंध टैग विभाजन सक्षम करें', + separated: 'अलग सोच टैग', + tagged: 'टैग्स के बारे में सोचते रहें', + tooltip: 'थिंक टैग से सामग्री निकाले और इसे reasoning_content क्षेत्र में संग्रहित करें।', + }, }, knowledgeRetrieval: { queryVariable: 'प्रश्न वेरिएबल', diff --git a/web/i18n/it-IT/workflow.ts b/web/i18n/it-IT/workflow.ts index 80c695cc6..821e7544c 100644 --- a/web/i18n/it-IT/workflow.ts +++ b/web/i18n/it-IT/workflow.ts @@ -487,6 +487,12 @@ const translation = { generating: 'Generazione dello schema JSON...', generatedResult: 'Risultato generato', }, + reasoningFormat: { + title: 'Abilita la separazione dei tag di ragionamento', + tagged: 'Continua a pensare ai tag', + separated: 'Tag di pensiero separati', + tooltip: 'Estrai il contenuto dai tag think e conservalo nel campo reasoning_content.', + }, }, knowledgeRetrieval: { queryVariable: 'Variabile Query', diff --git a/web/i18n/ja-JP/workflow.ts b/web/i18n/ja-JP/workflow.ts index 06535a852..2a3ee304f 100644 --- a/web/i18n/ja-JP/workflow.ts +++ b/web/i18n/ja-JP/workflow.ts @@ -477,6 +477,12 @@ const translation = { saveSchema: '編集中のフィールドを確定してから保存してください。', }, }, + reasoningFormat: { + tagged: 'タグを考え続けてください', + separated: '思考タグを分ける', + title: '推論タグの分離を有効にする', + tooltip: 'thinkタグから内容を抽出し、それをreasoning_contentフィールドに保存します。', + }, }, knowledgeRetrieval: { queryVariable: '検索変数', diff --git a/web/i18n/ko-KR/workflow.ts b/web/i18n/ko-KR/workflow.ts index 7b2fb7798..bc73e67e6 100644 --- a/web/i18n/ko-KR/workflow.ts +++ b/web/i18n/ko-KR/workflow.ts @@ -497,6 +497,12 @@ const translation = { doc: '구조화된 출력에 대해 더 알아보세요.', import: 'JSON 에서 가져오기', }, + reasoningFormat: { + title: '추론 태그 분리 활성화', + separated: '추론 태그 분리', + tooltip: '추론 태그에서 내용을 추출하고 이를 reasoning_content 필드에 저장합니다', + tagged: '추론 태그 유지', + }, }, knowledgeRetrieval: { queryVariable: '쿼리 변수', diff --git a/web/i18n/pl-PL/workflow.ts b/web/i18n/pl-PL/workflow.ts index 9560865e1..b5cd95d24 100644 --- a/web/i18n/pl-PL/workflow.ts +++ b/web/i18n/pl-PL/workflow.ts @@ -470,6 +470,12 @@ const translation = { back: 'Tył', addField: 'Dodaj pole', }, + reasoningFormat: { + tooltip: 'Wyodrębnij treść z tagów think i przechowaj ją w polu reasoning_content.', + separated: 'Oddziel tagi myślenia', + tagged: 'Zachowaj myśl tagi', + title: 'Włącz separację tagów uzasadnienia', + }, }, knowledgeRetrieval: { queryVariable: 'Zmienna zapytania', diff --git a/web/i18n/pt-BR/workflow.ts b/web/i18n/pt-BR/workflow.ts index 9e4b2dd44..a7ece8417 100644 --- a/web/i18n/pt-BR/workflow.ts +++ b/web/i18n/pt-BR/workflow.ts @@ -470,6 +470,12 @@ const translation = { apply: 'Aplicar', required: 'obrigatório', }, + reasoningFormat: { + tagged: 'Mantenha as tags de pensamento', + title: 'Ativar separação de tags de raciocínio', + separated: 'Separe as tags de pensamento', + tooltip: 'Extraia o conteúdo das tags de pensamento e armazene-o no campo reasoning_content.', + }, }, knowledgeRetrieval: { queryVariable: 'Variável de consulta', diff --git a/web/i18n/ro-RO/workflow.ts b/web/i18n/ro-RO/workflow.ts index 3bda159d4..ce393406d 100644 --- a/web/i18n/ro-RO/workflow.ts +++ b/web/i18n/ro-RO/workflow.ts @@ -470,6 +470,12 @@ const translation = { back: 'Înapoi', promptPlaceholder: 'Descrie schema ta JSON...', }, + reasoningFormat: { + tagged: 'Ține minte etichetele', + separated: 'Etichete de gândire separate', + title: 'Activează separarea etichetelor de raționare', + tooltip: 'Extrage conținutul din etichetele think și stochează-l în câmpul reasoning_content.', + }, }, knowledgeRetrieval: { queryVariable: 'Variabilă de interogare', diff --git a/web/i18n/ru-RU/workflow.ts b/web/i18n/ru-RU/workflow.ts index bd86004cb..1290f7e6b 100644 --- a/web/i18n/ru-RU/workflow.ts +++ b/web/i18n/ru-RU/workflow.ts @@ -470,6 +470,12 @@ const translation = { generating: 'Генерация схемы JSON...', promptTooltip: 'Преобразуйте текстовое описание в стандартизированную структуру JSON Schema.', }, + reasoningFormat: { + tagged: 'Продолжайте думать о тегах', + title: 'Включите разделение тегов на основе логики', + tooltip: 'Извлечь содержимое из тегов think и сохранить его в поле reasoning_content.', + separated: 'Отдельные теги для мышления', + }, }, knowledgeRetrieval: { queryVariable: 'Переменная запроса', diff --git a/web/i18n/sl-SI/workflow.ts b/web/i18n/sl-SI/workflow.ts index 9d57db334..57b9fa5ed 100644 --- a/web/i18n/sl-SI/workflow.ts +++ b/web/i18n/sl-SI/workflow.ts @@ -477,6 +477,12 @@ const translation = { context: 'kontekst', addMessage: 'Dodaj sporočilo', vision: 'vizija', + reasoningFormat: { + tagged: 'Ohranite oznake za razmišljanje', + title: 'Omogoči ločevanje oznak za razsojanje', + tooltip: 'Izvleći vsebino iz miselnih oznak in jo shraniti v polje reasoning_content.', + separated: 'Ločite oznake za razmišljanje', + }, }, knowledgeRetrieval: { outputVars: { diff --git a/web/i18n/th-TH/workflow.ts b/web/i18n/th-TH/workflow.ts index 653adbe0b..7d6e89217 100644 --- a/web/i18n/th-TH/workflow.ts +++ b/web/i18n/th-TH/workflow.ts @@ -470,6 +470,12 @@ const translation = { stringValidations: 'การตรวจสอบสตริง', required: 'จำเป็นต้องใช้', }, + reasoningFormat: { + tagged: 'รักษาความคิดเกี่ยวกับแท็ก', + separated: 'แยกแท็กความคิดเห็น', + tooltip: 'ดึงเนื้อหาจากแท็กคิดและเก็บไว้ในฟิลด์ reasoning_content.', + title: 'เปิดใช้งานการแยกแท็กการเหตุผล', + }, }, knowledgeRetrieval: { queryVariable: 'ตัวแปรแบบสอบถาม', diff --git a/web/i18n/tr-TR/workflow.ts b/web/i18n/tr-TR/workflow.ts index 903705a65..cda742fb6 100644 --- a/web/i18n/tr-TR/workflow.ts +++ b/web/i18n/tr-TR/workflow.ts @@ -470,6 +470,12 @@ const translation = { addChildField: 'Çocuk Alanı Ekle', resultTip: 'İşte oluşturulan sonuç. Eğer memnun değilseniz, geri dönüp isteminizi değiştirebilirsiniz.', }, + reasoningFormat: { + separated: 'Ayrı düşünce etiketleri', + title: 'Akıl yürütme etiket ayrımını etkinleştir', + tagged: 'Etiketleri düşünmeye devam et', + tooltip: 'Düşünce etiketlerinden içeriği çıkarın ve bunu reasoning_content alanında saklayın.', + }, }, knowledgeRetrieval: { queryVariable: 'Sorgu Değişkeni', diff --git a/web/i18n/uk-UA/workflow.ts b/web/i18n/uk-UA/workflow.ts index f051ab990..999d1bfb3 100644 --- a/web/i18n/uk-UA/workflow.ts +++ b/web/i18n/uk-UA/workflow.ts @@ -470,6 +470,12 @@ const translation = { title: 'Структурована схема виходу', doc: 'Дізнайтеся більше про структурований вихід', }, + reasoningFormat: { + separated: 'Окремі теги для думок', + tagged: 'Продовжуйте думати про мітки', + title: 'Увімкніть розділення тегів для міркування', + tooltip: 'Витягніть вміст з тегів think і зберігайте його в полі reasoning_content.', + }, }, knowledgeRetrieval: { queryVariable: 'Змінна запиту', diff --git a/web/i18n/vi-VN/workflow.ts b/web/i18n/vi-VN/workflow.ts index 0b2e2e875..2f8e20d08 100644 --- a/web/i18n/vi-VN/workflow.ts +++ b/web/i18n/vi-VN/workflow.ts @@ -470,6 +470,12 @@ const translation = { addChildField: 'Thêm trường trẻ em', title: 'Sơ đồ đầu ra có cấu trúc', }, + reasoningFormat: { + tagged: 'Giữ lại thẻ suy nghĩ', + tooltip: 'Trích xuất nội dung từ các thẻ think và lưu nó vào trường reasoning_content.', + separated: 'Tách biệt các thẻ suy nghĩ', + title: 'Bật chế độ phân tách nhãn lý luận', + }, }, knowledgeRetrieval: { queryVariable: 'Biến truy vấn', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index daaba921f..4573fa7bd 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -477,6 +477,12 @@ const translation = { saveSchema: '请先完成当前字段的编辑', }, }, + reasoningFormat: { + tooltip: '从think标签中提取内容,并将其存储在reasoning_content字段中。', + title: '启用推理标签分离', + tagged: '保持思考标签', + separated: '分开思考标签', + }, }, knowledgeRetrieval: { queryVariable: '查询变量', diff --git a/web/i18n/zh-Hant/workflow.ts b/web/i18n/zh-Hant/workflow.ts index 1105800a7..6f79177d1 100644 --- a/web/i18n/zh-Hant/workflow.ts +++ b/web/i18n/zh-Hant/workflow.ts @@ -470,6 +470,12 @@ const translation = { required: '必需的', resultTip: '這是生成的結果。如果您不滿意,可以回去修改您的提示。', }, + reasoningFormat: { + title: '啟用推理標籤分離', + tooltip: '從 think 標籤中提取內容並將其存儲在 reasoning_content 欄位中。', + tagged: '保持思考標籤', + separated: '分開思考標籤', + }, }, knowledgeRetrieval: { queryVariable: '查詢變量',