diff --git a/src/web-ui/src/flow_chat/components/modern/FlowChatContext.tsx b/src/web-ui/src/flow_chat/components/modern/FlowChatContext.tsx
index ef1a5b9c..f6a4b081 100644
--- a/src/web-ui/src/flow_chat/components/modern/FlowChatContext.tsx
+++ b/src/web-ui/src/flow_chat/components/modern/FlowChatContext.tsx
@@ -37,6 +37,11 @@ export interface FlowChatContextValue {
*/
onExploreGroupToggle?: (groupId: string) => void;
+ /**
+ * Expand the specified explore group.
+ */
+ onExpandGroup?: (groupId: string) => void;
+
/**
* Expand all explore groups within a turn.
*/
diff --git a/src/web-ui/src/flow_chat/components/modern/ModernFlowChatContainer.tsx b/src/web-ui/src/flow_chat/components/modern/ModernFlowChatContainer.tsx
index 6d506a77..5e9f4cd0 100644
--- a/src/web-ui/src/flow_chat/components/modern/ModernFlowChatContainer.tsx
+++ b/src/web-ui/src/flow_chat/components/modern/ModernFlowChatContainer.tsx
@@ -51,6 +51,7 @@ export const ModernFlowChatContainer: React.FC = (
const {
exploreGroupStates,
onExploreGroupToggle: handleExploreGroupToggle,
+ onExpandGroup: handleExpandGroup,
onExpandAllInTurn: handleExpandAllInTurn,
onCollapseGroup: handleCollapseGroup,
} = useExploreGroupState(virtualItems);
@@ -89,6 +90,7 @@ export const ModernFlowChatContainer: React.FC = (
},
exploreGroupStates,
onExploreGroupToggle: handleExploreGroupToggle,
+ onExpandGroup: handleExpandGroup,
onExpandAllInTurn: handleExpandAllInTurn,
onCollapseGroup: handleCollapseGroup,
}), [
@@ -102,6 +104,7 @@ export const ModernFlowChatContainer: React.FC = (
config,
exploreGroupStates,
handleExploreGroupToggle,
+ handleExpandGroup,
handleExpandAllInTurn,
handleCollapseGroup,
]);
diff --git a/src/web-ui/src/flow_chat/components/modern/useExploreGroupState.ts b/src/web-ui/src/flow_chat/components/modern/useExploreGroupState.ts
index 9db53a8b..398a08b8 100644
--- a/src/web-ui/src/flow_chat/components/modern/useExploreGroupState.ts
+++ b/src/web-ui/src/flow_chat/components/modern/useExploreGroupState.ts
@@ -14,6 +14,7 @@ interface UseExploreGroupStateResult {
*/
exploreGroupStates: Map;
onExploreGroupToggle: (groupId: string) => void;
+ onExpandGroup: (groupId: string) => void;
onExpandAllInTurn: (turnId: string) => void;
onCollapseGroup: (groupId: string) => void;
}
@@ -32,6 +33,17 @@ export function useExploreGroupState(
});
}, []);
+ const onExpandGroup = useCallback((groupId: string) => {
+ setExploreGroupStates(prev => {
+ if (prev.get(groupId) === true) {
+ return prev;
+ }
+ const next = new Map(prev);
+ next.set(groupId, true);
+ return next;
+ });
+ }, []);
+
const onExpandAllInTurn = useCallback((turnId: string) => {
const groupIds = virtualItems
.filter((item): item is ExploreGroupVirtualItem => (
@@ -57,6 +69,7 @@ export function useExploreGroupState(
return {
exploreGroupStates,
onExploreGroupToggle,
+ onExpandGroup,
onExpandAllInTurn,
onCollapseGroup,
};
diff --git a/src/web-ui/src/infrastructure/config/components/AIModelConfig.tsx b/src/web-ui/src/infrastructure/config/components/AIModelConfig.tsx
index 5f67bf94..01c73868 100644
--- a/src/web-ui/src/infrastructure/config/components/AIModelConfig.tsx
+++ b/src/web-ui/src/infrastructure/config/components/AIModelConfig.tsx
@@ -407,6 +407,7 @@ const AIModelConfig: React.FC = () => {
metadata: config.metadata || {},
enable_thinking_process: config.enable_thinking_process ?? false,
support_preserved_thinking: config.support_preserved_thinking ?? false,
+ inline_think_in_text: config.inline_think_in_text ?? false,
reasoning_effort: config.reasoning_effort,
custom_headers: config.custom_headers,
custom_headers_mode: config.custom_headers_mode,
@@ -420,6 +421,7 @@ const AIModelConfig: React.FC = () => {
base_url: config.base_url,
api_key: config.api_key,
model_name: config.model_name,
+ inline_think_in_text: config.inline_think_in_text ?? false,
skip_ssl_verify: config.skip_ssl_verify ?? false,
custom_headers_mode: config.custom_headers_mode || null,
custom_headers: config.custom_headers || null,
@@ -527,7 +529,8 @@ const AIModelConfig: React.FC = () => {
category: 'general_chat',
capabilities: ['text_chat', 'function_calling'],
recommended_for: [],
- metadata: {}
+ metadata: {},
+ inline_think_in_text: false,
});
setSelectedModelDrafts(
configuredProviderModels.length > 0
@@ -563,7 +566,8 @@ const AIModelConfig: React.FC = () => {
category: 'general_chat',
capabilities: ['text_chat'],
recommended_for: [],
- metadata: {}
+ metadata: {},
+ inline_think_in_text: false,
});
setSelectedModelDrafts([]);
setShowAdvancedSettings(false);
@@ -597,6 +601,7 @@ const AIModelConfig: React.FC = () => {
metadata: config.metadata || {},
enable_thinking_process: config.enable_thinking_process ?? false,
support_preserved_thinking: config.support_preserved_thinking ?? false,
+ inline_think_in_text: config.inline_think_in_text ?? false,
reasoning_effort: config.reasoning_effort,
custom_headers: config.custom_headers,
custom_headers_mode: config.custom_headers_mode,
@@ -605,6 +610,7 @@ const AIModelConfig: React.FC = () => {
});
setSelectedModelDrafts(createDraftsFromConfigs(configuredProviderModels));
setShowAdvancedSettings(
+ !!config.inline_think_in_text ||
!!config.skip_ssl_verify ||
(!!config.custom_request_body && config.custom_request_body.trim() !== '') ||
(!!config.custom_headers && Object.keys(config.custom_headers).length > 0)
@@ -628,7 +634,12 @@ const AIModelConfig: React.FC = () => {
const hasCustomHeaders = !!config.custom_headers && Object.keys(config.custom_headers).length > 0;
const hasCustomBody = !!config.custom_request_body && config.custom_request_body.trim() !== '';
- setShowAdvancedSettings(hasCustomHeaders || hasCustomBody || !!config.skip_ssl_verify);
+ setShowAdvancedSettings(
+ hasCustomHeaders ||
+ hasCustomBody ||
+ !!config.skip_ssl_verify ||
+ !!config.inline_think_in_text
+ );
setIsEditing(true);
};
@@ -680,6 +691,7 @@ const AIModelConfig: React.FC = () => {
metadata: editingConfig.metadata,
enable_thinking_process: draft.enableThinking,
support_preserved_thinking: editingConfig.support_preserved_thinking ?? false,
+ inline_think_in_text: editingConfig.inline_think_in_text ?? false,
reasoning_effort: editingConfig.reasoning_effort,
custom_headers: editingConfig.custom_headers,
custom_headers_mode: editingConfig.custom_headers_mode,
@@ -1378,6 +1390,7 @@ const AIModelConfig: React.FC = () => {
...prev,
provider,
request_url: resolveRequestUrl(prev?.base_url || '', provider, prev?.model_name || ''),
+ inline_think_in_text: provider === 'openai' ? (prev?.inline_think_in_text ?? false) : false,
reasoning_effort: isResponsesProvider(provider) ? (prev?.reasoning_effort || 'medium') : undefined,
}));
}} placeholder={t('form.providerPlaceholder')} options={requestFormatOptions} size="small" />
@@ -1458,9 +1471,13 @@ const AIModelConfig: React.FC = () => {
{showAdvancedSettings && (
<>
- {editingConfig.enable_thinking_process && (
-
- setEditingConfig(prev => ({ ...prev, support_preserved_thinking: e.target.checked }))} size="small" />
+ {editingConfig.provider === 'openai' && (
+
+ setEditingConfig(prev => ({ ...prev, inline_think_in_text: e.target.checked }))}
+ size="small"
+ />
)}
diff --git a/src/web-ui/src/infrastructure/config/schemas/ai-models.json b/src/web-ui/src/infrastructure/config/schemas/ai-models.json
index 87b216ab..b9c10026 100644
--- a/src/web-ui/src/infrastructure/config/schemas/ai-models.json
+++ b/src/web-ui/src/infrastructure/config/schemas/ai-models.json
@@ -391,6 +391,20 @@
"title": "高级设置",
"icon": "cog",
"fields": [
+ {
+ "type": "switch",
+ "dataType": "boolean",
+ "key": "inline_think_in_text",
+ "title": "解析文本中的 标签",
+ "description": "将 OpenAI 兼容文本流中的 ... 实时拆分为 reasoning 内容,默认关闭",
+ "default": false,
+ "conditional": {
+ "when": "provider",
+ "operator": "equals",
+ "value": "openai",
+ "show": true
+ }
+ },
{
"type": "number",
"dataType": "number",
diff --git a/src/web-ui/src/infrastructure/config/types/index.ts b/src/web-ui/src/infrastructure/config/types/index.ts
index afde50ea..c78e57ec 100644
--- a/src/web-ui/src/infrastructure/config/types/index.ts
+++ b/src/web-ui/src/infrastructure/config/types/index.ts
@@ -130,6 +130,9 @@ export interface AIModelConfig {
support_preserved_thinking?: boolean;
+ /** Parse `...` text chunks into streaming reasoning content. */
+ inline_think_in_text?: boolean;
+
/** Reasoning effort for OpenAI Responses API ("low" | "medium" | "high" | "xhigh") */
reasoning_effort?: string;
}
diff --git a/src/web-ui/src/locales/en-US/settings/ai-model.json b/src/web-ui/src/locales/en-US/settings/ai-model.json
index 441318db..cd7d5190 100644
--- a/src/web-ui/src/locales/en-US/settings/ai-model.json
+++ b/src/web-ui/src/locales/en-US/settings/ai-model.json
@@ -255,6 +255,10 @@
},
"advancedSettings": {
"title": "Advanced Settings",
+ "inlineThinkInText": {
+ "label": "Parse Tags In Text",
+ "hint": "Split OpenAI-compatible text streams containing ... into live reasoning content. Disabled by default."
+ },
"skipSslVerify": {
"label": "Skip SSL Certificate Verification",
"warning": "Skipping SSL verification is a security risk, use only in test or internal network environments!"
diff --git a/src/web-ui/src/locales/zh-CN/settings/ai-model.json b/src/web-ui/src/locales/zh-CN/settings/ai-model.json
index bd1fb3d3..c98567e6 100644
--- a/src/web-ui/src/locales/zh-CN/settings/ai-model.json
+++ b/src/web-ui/src/locales/zh-CN/settings/ai-model.json
@@ -255,6 +255,10 @@
},
"advancedSettings": {
"title": "高级设置",
+ "inlineThinkInText": {
+ "label": "解析文本中的 标签",
+ "hint": "将 OpenAI 兼容文本流中的 ... 实时拆分为思考内容,默认关闭"
+ },
"skipSslVerify": {
"label": "跳过SSL证书验证",
"warning": "跳过SSL证书验证存在安全风险,请仅在测试环境或内网环境使用!"