feat: add reasoning format processing to LLMNode for <think> tag handling (#23313)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
taewoong Kim 2025-09-05 19:15:35 +09:00 • committed by GitHub
commit edf4a1b652
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 366 additions and 5 deletions

View file

@ -156,6 +156,7 @@ class LLMResult(BaseModel):
message: AssistantPromptMessage message: AssistantPromptMessage
usage: LLMUsage usage: LLMUsage
system_fingerprint: Optional[str] = None system_fingerprint: Optional[str] = None
reasoning_content: Optional[str] = None
class LLMStructuredOutput(BaseModel): class LLMStructuredOutput(BaseModel):

View file

@ -30,6 +30,7 @@ class ModelInvokeCompletedEvent(BaseModel):
text: str text: str
usage: LLMUsage usage: LLMUsage
finish_reason: str | None = None finish_reason: str | None = None
reasoning_content: str | None = None
class RunRetryEvent(BaseModel): class RunRetryEvent(BaseModel):

View file

@ -1,5 +1,5 @@
from collections.abc import Mapping, Sequence from collections.abc import Mapping, Sequence
from typing import Any, Optional from typing import Any, Literal, Optional
from pydantic import BaseModel, Field, field_validator from pydantic import BaseModel, Field, field_validator
@ -68,6 +68,23 @@ class LLMNodeData(BaseNodeData):
structured_output: Mapping[str, Any] | None = None structured_output: Mapping[str, Any] | None = None
# We used 'structured_output_enabled' in the past, but it's not a good name. # 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") 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 <think> 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 <think> 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") @field_validator("prompt_config", mode="before")
@classmethod @classmethod

View file

@ -2,8 +2,9 @@ import base64
import io import io
import json import json
import logging import logging
import re
from collections.abc import Generator, Mapping, Sequence 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.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity
from core.file import FileType, file_manager from core.file import FileType, file_manager
@ -99,6 +100,9 @@ class LLMNode(BaseNode):
_node_data: LLMNodeData _node_data: LLMNodeData
# Compiled regex for extracting <think> blocks (with compatibility for attributes)
_THINK_PATTERN = re.compile(r"<think[^>]*>(.*?)</think>", re.IGNORECASE | re.DOTALL)
# Instance attributes specific to LLMNode. # Instance attributes specific to LLMNode.
# Output variable for file # Output variable for file
_file_outputs: list["File"] _file_outputs: list["File"]
@ -167,6 +171,7 @@ class LLMNode(BaseNode):
result_text = "" result_text = ""
usage = LLMUsage.empty_usage() usage = LLMUsage.empty_usage()
finish_reason = None finish_reason = None
reasoning_content = None
variable_pool = self.graph_runtime_state.variable_pool variable_pool = self.graph_runtime_state.variable_pool
try: try:
@ -256,6 +261,7 @@ class LLMNode(BaseNode):
file_saver=self._llm_file_saver, file_saver=self._llm_file_saver,
file_outputs=self._file_outputs, file_outputs=self._file_outputs,
node_id=self.node_id, node_id=self.node_id,
reasoning_format=self._node_data.reasoning_format,
) )
structured_output: LLMStructuredOutput | None = None structured_output: LLMStructuredOutput | None = None
@ -264,9 +270,20 @@ class LLMNode(BaseNode):
if isinstance(event, RunStreamChunkEvent): if isinstance(event, RunStreamChunkEvent):
yield event yield event
elif isinstance(event, ModelInvokeCompletedEvent): elif isinstance(event, ModelInvokeCompletedEvent):
# Raw text
result_text = event.text result_text = event.text
usage = event.usage usage = event.usage
finish_reason = event.finish_reason 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 <think> tags for backward compatibility
clean_text = result_text
else:
# Extract clean text from <think> tags
clean_text, _ = LLMNode._split_reasoning(result_text, self._node_data.reasoning_format)
# deduct quota # deduct quota
llm_utils.deduct_llm_quota(tenant_id=self.tenant_id, model_instance=model_instance, usage=usage) llm_utils.deduct_llm_quota(tenant_id=self.tenant_id, model_instance=model_instance, usage=usage)
break break
@ -284,7 +301,12 @@ class LLMNode(BaseNode):
"model_name": model_config.model, "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: if structured_output:
outputs["structured_output"] = structured_output.structured_output outputs["structured_output"] = structured_output.structured_output
if self._file_outputs is not None: if self._file_outputs is not None:
@ -338,6 +360,7 @@ class LLMNode(BaseNode):
file_saver: LLMFileSaver, file_saver: LLMFileSaver,
file_outputs: list["File"], file_outputs: list["File"],
node_id: str, node_id: str,
reasoning_format: Literal["separated", "tagged"] = "tagged",
) -> Generator[NodeEvent | LLMStructuredOutput, None, None]: ) -> Generator[NodeEvent | LLMStructuredOutput, None, None]:
model_schema = model_instance.model_type_instance.get_model_schema( model_schema = model_instance.model_type_instance.get_model_schema(
node_data_model.name, model_instance.credentials node_data_model.name, model_instance.credentials
@ -374,6 +397,7 @@ class LLMNode(BaseNode):
file_saver=file_saver, file_saver=file_saver,
file_outputs=file_outputs, file_outputs=file_outputs,
node_id=node_id, node_id=node_id,
reasoning_format=reasoning_format,
) )
@staticmethod @staticmethod
@ -383,6 +407,7 @@ class LLMNode(BaseNode):
file_saver: LLMFileSaver, file_saver: LLMFileSaver,
file_outputs: list["File"], file_outputs: list["File"],
node_id: str, node_id: str,
reasoning_format: Literal["separated", "tagged"] = "tagged",
) -> Generator[NodeEvent | LLMStructuredOutput, None, None]: ) -> Generator[NodeEvent | LLMStructuredOutput, None, None]:
# For blocking mode # For blocking mode
if isinstance(invoke_result, LLMResult): if isinstance(invoke_result, LLMResult):
@ -390,6 +415,7 @@ class LLMNode(BaseNode):
invoke_result=invoke_result, invoke_result=invoke_result,
saver=file_saver, saver=file_saver,
file_outputs=file_outputs, file_outputs=file_outputs,
reasoning_format=reasoning_format,
) )
yield event yield event
return return
@ -430,13 +456,66 @@ class LLMNode(BaseNode):
except OutputParserError as e: except OutputParserError as e:
raise LLMNodeError(f"Failed to parse structured output: {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 <think> tags in the main text
full_text = full_text_buffer.getvalue()
if reasoning_format == "tagged":
# Keep <think> tags in text for backward compatibility
clean_text = full_text
reasoning_content = ""
else:
# Extract clean text and reasoning from <think> 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 @staticmethod
def _image_file_to_markdown(file: "File", /): def _image_file_to_markdown(file: "File", /):
text_chunk = f"![]({file.generate_url()})" text_chunk = f"![]({file.generate_url()})"
return text_chunk 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 <think> blocks
reasoning_format: Strategy for handling reasoning content
- "separated": Remove <think> tags and return clean text + reasoning_content field
- "tagged": Keep <think> tags in text, return empty reasoning_content
Returns:
tuple of (clean_text, reasoning_content)
"""
if reasoning_format == "tagged":
return text, ""
# Find all <think>...</think> blocks (case-insensitive)
matches = cls._THINK_PATTERN.findall(text)
# Extract reasoning content from all <think> blocks
reasoning_content = "\n".join(match.strip() for match in matches) if matches else ""
# Remove all <think>...</think> 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( def _transform_chat_messages(
self, messages: Sequence[LLMNodeChatModelMessage] | LLMNodeCompletionModelPromptTemplate, / self, messages: Sequence[LLMNodeChatModelMessage] | LLMNodeCompletionModelPromptTemplate, /
) -> Sequence[LLMNodeChatModelMessage] | LLMNodeCompletionModelPromptTemplate: ) -> Sequence[LLMNodeChatModelMessage] | LLMNodeCompletionModelPromptTemplate:
@ -964,6 +1043,7 @@ class LLMNode(BaseNode):
invoke_result: LLMResult, invoke_result: LLMResult,
saver: LLMFileSaver, saver: LLMFileSaver,
file_outputs: list["File"], file_outputs: list["File"],
reasoning_format: Literal["separated", "tagged"] = "tagged",
) -> ModelInvokeCompletedEvent: ) -> ModelInvokeCompletedEvent:
buffer = io.StringIO() buffer = io.StringIO()
for text_part in LLMNode._save_multimodal_output_and_convert_result_to_markdown( for text_part in LLMNode._save_multimodal_output_and_convert_result_to_markdown(
@ -973,10 +1053,24 @@ class LLMNode(BaseNode):
): ):
buffer.write(text_part) buffer.write(text_part)
# Extract reasoning content from <think> tags in the main text
full_text = buffer.getvalue()
if reasoning_format == "tagged":
# Keep <think> tags in text for backward compatibility
clean_text = full_text
reasoning_content = ""
else:
# Extract clean text and reasoning from <think> tags
clean_text, reasoning_content = LLMNode._split_reasoning(full_text, reasoning_format)
return ModelInvokeCompletedEvent( 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, usage=invoke_result.usage,
finish_reason=None, finish_reason=None,
# Reasoning content for workflow variables and downstream nodes
reasoning_content=reasoning_content,
) )
@staticmethod @staticmethod

View file

@ -69,6 +69,7 @@ def llm_node_data() -> LLMNodeData:
detail=ImagePromptMessageContent.DETAIL.HIGH, detail=ImagePromptMessageContent.DETAIL.HIGH,
), ),
), ),
reasoning_format="tagged",
) )
@ -689,3 +690,66 @@ class TestSaveMultimodalOutputAndConvertResultToMarkdown:
assert list(gen) == [] assert list(gen) == []
mock_file_saver.save_binary_string.assert_not_called() mock_file_saver.save_binary_string.assert_not_called()
mock_file_saver.save_remote_url.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 = """
<think>I need to explain what Dify is. It's an open source AI platform.
</think>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 = """
<think>I need to explain what Dify is. It's an open source AI platform.
</think>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 <think> 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 = """
<think>I need to explain what Dify is. It's an open source AI platform.
</think>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 == ""

View file

@ -479,6 +479,10 @@ export const LLM_OUTPUT_STRUCT: Var[] = [
variable: 'text', variable: 'text',
type: VarType.string, type: VarType.string,
}, },
{
variable: 'reasoning_content',
type: VarType.string,
},
{ {
variable: 'usage', variable: 'usage',
type: VarType.object, type: VarType.object,

View file

@ -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<ReasoningFormatConfigProps> = ({
value = 'tagged',
onChange,
readonly = false,
}) => {
const { t } = useTranslation()
return (
<Field
title={t('workflow.nodes.llm.reasoningFormat.title')}
tooltip={t('workflow.nodes.llm.reasoningFormat.tooltip')}
operations={
// ON = separated, OFF = tagged
<Switch
defaultValue={value === 'separated'}
onChange={enabled => onChange(enabled ? 'separated' : 'tagged')}
size='md'
disabled={readonly}
key={value}
/>
}
>
<div />
</Field>
)
}
export default ReasoningFormatConfig

View file

@ -17,6 +17,7 @@ import type { NodePanelProps } from '@/app/components/workflow/types'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
import StructureOutput from './components/structure-output' import StructureOutput from './components/structure-output'
import ReasoningFormatConfig from './components/reasoning-format-config'
import Switch from '@/app/components/base/switch' import Switch from '@/app/components/base/switch'
import { RiAlertFill, RiQuestionLine } from '@remixicon/react' import { RiAlertFill, RiQuestionLine } from '@remixicon/react'
import { fetchAndMergeValidCompletionParams } from '@/utils/completion-params' import { fetchAndMergeValidCompletionParams } from '@/utils/completion-params'
@ -61,6 +62,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
handleStructureOutputEnableChange, handleStructureOutputEnableChange,
handleStructureOutputChange, handleStructureOutputChange,
filterJinja2InputVar, filterJinja2InputVar,
handleReasoningFormatChange,
} = useConfig(id, data) } = useConfig(id, data)
const model = inputs.model const model = inputs.model
@ -239,6 +241,14 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
config={inputs.vision?.configs} config={inputs.vision?.configs}
onConfigChange={handleVisionResolutionChange} onConfigChange={handleVisionResolutionChange}
/> />
{/* Reasoning Format */}
<ReasoningFormatConfig
// Default to tagged for backward compatibility
value={inputs.reasoning_format || 'tagged'}
onChange={handleReasoningFormatChange}
readonly={readOnly}
/>
</div> </div>
<Split /> <Split />
<OutputVars <OutputVars

View file

@ -17,6 +17,7 @@ export type LLMNodeType = CommonNodeType & {
} }
structured_output_enabled?: boolean structured_output_enabled?: boolean
structured_output?: StructuredOutput structured_output?: StructuredOutput
reasoning_format?: 'tagged' | 'separated'
} }
export enum Type { export enum Type {

View file

@ -315,6 +315,14 @@ const useConfig = (id: string, payload: LLMNodeType) => {
return [VarType.arrayObject, VarType.array, VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber, VarType.file, VarType.arrayFile].includes(varPayload.type) 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 { const {
availableVars, availableVars,
availableNodesWithParent, availableNodesWithParent,
@ -355,6 +363,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
setStructuredOutputCollapsed, setStructuredOutputCollapsed,
handleStructureOutputEnableChange, handleStructureOutputEnableChange,
filterJinja2InputVar, filterJinja2InputVar,
handleReasoningFormatChange,
} }
} }

View file

@ -470,6 +470,12 @@ const translation = {
instruction: 'Anleitung', instruction: 'Anleitung',
regenerate: 'Regenerieren', 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: { knowledgeRetrieval: {
queryVariable: 'Abfragevariable', queryVariable: 'Abfragevariable',

View file

@ -449,6 +449,12 @@ const translation = {
variable: 'Variable', variable: 'Variable',
}, },
sysQueryInUser: 'sys.query in user message is required', 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: { jsonSchema: {
title: 'Structured Output Schema', title: 'Structured Output Schema',
instruction: 'Instruction', instruction: 'Instruction',

View file

@ -470,6 +470,12 @@ const translation = {
import: 'Importar desde JSON', import: 'Importar desde JSON',
resetDefaults: 'Restablecer', 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: { knowledgeRetrieval: {
queryVariable: 'Variable de consulta', queryVariable: 'Variable de consulta',

View file

@ -470,6 +470,12 @@ const translation = {
fieldNamePlaceholder: 'نام میدان', fieldNamePlaceholder: 'نام میدان',
generationTip: 'شما می‌توانید از زبان طبیعی برای ایجاد سریع یک طرح‌واره JSON استفاده کنید.', generationTip: 'شما می‌توانید از زبان طبیعی برای ایجاد سریع یک طرح‌واره JSON استفاده کنید.',
}, },
reasoningFormat: {
separated: 'تگ‌های تفکر جداگانه',
title: 'فعال‌سازی جداسازی برچسب‌های استدلال',
tagged: 'به فکر برچسب‌ها باشید',
tooltip: 'محتوا را از تگ‌های تفکر استخراج کرده و در فیلد reasoning_content ذخیره کنید.',
},
}, },
knowledgeRetrieval: { knowledgeRetrieval: {
queryVariable: 'متغیر جستجو', queryVariable: 'متغیر جستجو',

View file

@ -470,6 +470,12 @@ const translation = {
generateJsonSchema: 'Générer un schéma JSON', 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.', 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: { knowledgeRetrieval: {
queryVariable: 'Variable de requête', queryVariable: 'Variable de requête',

View file

@ -483,6 +483,12 @@ const translation = {
required: 'आवश्यक', required: 'आवश्यक',
addChildField: 'बच्चे का क्षेत्र जोड़ें', addChildField: 'बच्चे का क्षेत्र जोड़ें',
}, },
reasoningFormat: {
title: 'कारण संबंध टैग विभाजन सक्षम करें',
separated: 'अलग सोच टैग',
tagged: 'टैग्स के बारे में सोचते रहें',
tooltip: 'थिंक टैग से सामग्री निकाले और इसे reasoning_content क्षेत्र में संग्रहित करें।',
},
}, },
knowledgeRetrieval: { knowledgeRetrieval: {
queryVariable: 'प्रश्न वेरिएबल', queryVariable: 'प्रश्न वेरिएबल',

View file

@ -487,6 +487,12 @@ const translation = {
generating: 'Generazione dello schema JSON...', generating: 'Generazione dello schema JSON...',
generatedResult: 'Risultato generato', 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: { knowledgeRetrieval: {
queryVariable: 'Variabile Query', queryVariable: 'Variabile Query',

View file

@ -477,6 +477,12 @@ const translation = {
saveSchema: '編集中のフィールドを確定してから保存してください。', saveSchema: '編集中のフィールドを確定してから保存してください。',
}, },
}, },
reasoningFormat: {
tagged: 'タグを考え続けてください',
separated: '思考タグを分ける',
title: '推論タグの分離を有効にする',
tooltip: 'thinkタグから内容を抽出し、それをreasoning_contentフィールドに保存します。',
},
}, },
knowledgeRetrieval: { knowledgeRetrieval: {
queryVariable: '検索変数', queryVariable: '検索変数',

View file

@ -497,6 +497,12 @@ const translation = {
doc: '구조화된 출력에 대해 더 알아보세요.', doc: '구조화된 출력에 대해 더 알아보세요.',
import: 'JSON 에서 가져오기', import: 'JSON 에서 가져오기',
}, },
reasoningFormat: {
title: '추론 태그 분리 활성화',
separated: '추론 태그 분리',
tooltip: '추론 태그에서 내용을 추출하고 이를 reasoning_content 필드에 저장합니다',
tagged: '추론 태그 유지',
},
}, },
knowledgeRetrieval: { knowledgeRetrieval: {
queryVariable: '쿼리 변수', queryVariable: '쿼리 변수',

View file

@ -470,6 +470,12 @@ const translation = {
back: 'Tył', back: 'Tył',
addField: 'Dodaj pole', 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: { knowledgeRetrieval: {
queryVariable: 'Zmienna zapytania', queryVariable: 'Zmienna zapytania',

View file

@ -470,6 +470,12 @@ const translation = {
apply: 'Aplicar', apply: 'Aplicar',
required: 'obrigatório', 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: { knowledgeRetrieval: {
queryVariable: 'Variável de consulta', queryVariable: 'Variável de consulta',

View file

@ -470,6 +470,12 @@ const translation = {
back: 'Înapoi', back: 'Înapoi',
promptPlaceholder: 'Descrie schema ta JSON...', 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: { knowledgeRetrieval: {
queryVariable: 'Variabilă de interogare', queryVariable: 'Variabilă de interogare',

View file

@ -470,6 +470,12 @@ const translation = {
generating: 'Генерация схемы JSON...', generating: 'Генерация схемы JSON...',
promptTooltip: 'Преобразуйте текстовое описание в стандартизированную структуру JSON Schema.', promptTooltip: 'Преобразуйте текстовое описание в стандартизированную структуру JSON Schema.',
}, },
reasoningFormat: {
tagged: 'Продолжайте думать о тегах',
title: 'Включите разделение тегов на основе логики',
tooltip: 'Извлечь содержимое из тегов think и сохранить его в поле reasoning_content.',
separated: 'Отдельные теги для мышления',
},
}, },
knowledgeRetrieval: { knowledgeRetrieval: {
queryVariable: 'Переменная запроса', queryVariable: 'Переменная запроса',

View file

@ -477,6 +477,12 @@ const translation = {
context: 'kontekst', context: 'kontekst',
addMessage: 'Dodaj sporočilo', addMessage: 'Dodaj sporočilo',
vision: 'vizija', 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: { knowledgeRetrieval: {
outputVars: { outputVars: {

View file

@ -470,6 +470,12 @@ const translation = {
stringValidations: 'การตรวจสอบสตริง', stringValidations: 'การตรวจสอบสตริง',
required: 'จำเป็นต้องใช้', required: 'จำเป็นต้องใช้',
}, },
reasoningFormat: {
tagged: 'รักษาความคิดเกี่ยวกับแท็ก',
separated: 'แยกแท็กความคิดเห็น',
tooltip: 'ดึงเนื้อหาจากแท็กคิดและเก็บไว้ในฟิลด์ reasoning_content.',
title: 'เปิดใช้งานการแยกแท็กการเหตุผล',
},
}, },
knowledgeRetrieval: { knowledgeRetrieval: {
queryVariable: 'ตัวแปรแบบสอบถาม', queryVariable: 'ตัวแปรแบบสอบถาม',

View file

@ -470,6 +470,12 @@ const translation = {
addChildField: 'Çocuk Alanı Ekle', addChildField: 'Çocuk Alanı Ekle',
resultTip: 'İşte oluşturulan sonuç. Eğer memnun değilseniz, geri dönüp isteminizi değiştirebilirsiniz.', 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: { knowledgeRetrieval: {
queryVariable: 'Sorgu Değişkeni', queryVariable: 'Sorgu Değişkeni',

View file

@ -470,6 +470,12 @@ const translation = {
title: 'Структурована схема виходу', title: 'Структурована схема виходу',
doc: 'Дізнайтеся більше про структурований вихід', doc: 'Дізнайтеся більше про структурований вихід',
}, },
reasoningFormat: {
separated: 'Окремі теги для думок',
tagged: 'Продовжуйте думати про мітки',
title: 'Увімкніть розділення тегів для міркування',
tooltip: 'Витягніть вміст з тегів think і зберігайте його в полі reasoning_content.',
},
}, },
knowledgeRetrieval: { knowledgeRetrieval: {
queryVariable: 'Змінна запиту', queryVariable: 'Змінна запиту',

View file

@ -470,6 +470,12 @@ const translation = {
addChildField: 'Thêm trường trẻ em', addChildField: 'Thêm trường trẻ em',
title: 'Sơ đồ đầu ra có cấu trúc', 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: { knowledgeRetrieval: {
queryVariable: 'Biến truy vấn', queryVariable: 'Biến truy vấn',

View file

@ -477,6 +477,12 @@ const translation = {
saveSchema: '请先完成当前字段的编辑', saveSchema: '请先完成当前字段的编辑',
}, },
}, },
reasoningFormat: {
tooltip: '从think标签中提取内容,并将其存储在reasoning_content字段中。',
title: '启用推理标签分离',
tagged: '保持思考标签',
separated: '分开思考标签',
},
}, },
knowledgeRetrieval: { knowledgeRetrieval: {
queryVariable: '查询变量', queryVariable: '查询变量',

View file

@ -470,6 +470,12 @@ const translation = {
required: '必需的', required: '必需的',
resultTip: '這是生成的結果。如果您不滿意,可以回去修改您的提示。', resultTip: '這是生成的結果。如果您不滿意,可以回去修改您的提示。',
}, },
reasoningFormat: {
title: '啟用推理標籤分離',
tooltip: '從 think 標籤中提取內容並將其存儲在 reasoning_content 欄位中。',
tagged: '保持思考標籤',
separated: '分開思考標籤',
},
}, },
knowledgeRetrieval: { knowledgeRetrieval: {
queryVariable: '查詢變量', queryVariable: '查詢變量',