local inference: stricter GGUF requirements, auto detection of tool calling support, fixed thinking output parsing (#9442)

Signed-off-by: jh-block <jhugo@block.xyz>
This commit is contained in:
jh-block
2026-05-27 20:00:30 +02:00
committed by GitHub
parent d90b349a69
commit 27b41d93f5
23 changed files with 1844 additions and 492 deletions
+94 -6
View File
@@ -1993,6 +1993,29 @@
}
}
},
"/local-inference/chat-templates/builtin": {
"get": {
"tags": [
"super::routes::local_inference"
],
"operationId": "list_builtin_chat_templates",
"responses": {
"200": {
"description": "llama.cpp built-in chat template names",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
},
"/local-inference/download": {
"post": {
"tags": [
@@ -4260,6 +4283,63 @@
}
}
},
"ChatTemplate": {
"oneOf": [
{
"type": "object",
"required": [
"type"
],
"properties": {
"type": {
"type": "string",
"enum": [
"embedded"
]
}
}
},
{
"type": "object",
"required": [
"name",
"type"
],
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"builtin"
]
}
}
},
{
"type": "object",
"required": [
"template",
"type"
],
"properties": {
"template": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"custom_inline"
]
}
}
}
],
"discriminator": {
"propertyName": "type"
}
},
"CheckProviderRequest": {
"type": "object",
"required": [
@@ -6716,6 +6796,9 @@
"ModelSettings": {
"type": "object",
"properties": {
"chat_template": {
"$ref": "#/components/schemas/ChatTemplate"
},
"context_size": {
"type": "integer",
"format": "int32",
@@ -6766,9 +6849,6 @@
"format": "int32",
"nullable": true
},
"native_tool_calling": {
"type": "boolean"
},
"presence_penalty": {
"type": "number",
"format": "float"
@@ -6784,15 +6864,15 @@
"sampling": {
"$ref": "#/components/schemas/SamplingConfig"
},
"use_jinja": {
"type": "boolean"
"tool_calling": {
"$ref": "#/components/schemas/ToolCallingMode"
},
"use_mlock": {
"type": "boolean"
},
"vision_capable": {
"type": "boolean",
"description": "Whether this model architecture supports vision input.\nDerived from the featured model table, not user-configurable."
"description": "Whether this model architecture supports vision input.\nDerived from associated mmproj metadata, not user-configurable."
}
}
},
@@ -8797,6 +8877,14 @@
}
}
},
"ToolCallingMode": {
"type": "string",
"enum": [
"auto",
"force_native",
"force_emulated"
]
},
"ToolConfirmationRequest": {
"type": "object",
"required": [
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+31 -3
View File
@@ -76,6 +76,16 @@ export type ChatRequest = {
user_message: Message;
};
export type ChatTemplate = {
type: 'embedded';
} | {
name: string;
type: 'builtin';
} | {
template: string;
type: 'custom_inline';
};
export type CheckProviderRequest = {
provider: string;
};
@@ -863,6 +873,7 @@ export type ModelInfoResponse = {
};
export type ModelSettings = {
chat_template?: ChatTemplate;
context_size?: number | null;
enable_thinking?: boolean;
flash_attention?: boolean | null;
@@ -880,16 +891,15 @@ export type ModelSettings = {
n_batch?: number | null;
n_gpu_layers?: number | null;
n_threads?: number | null;
native_tool_calling?: boolean;
presence_penalty?: number;
repeat_last_n?: number;
repeat_penalty?: number;
sampling?: SamplingConfig;
use_jinja?: boolean;
tool_calling?: ToolCallingMode;
use_mlock?: boolean;
/**
* Whether this model architecture supports vision input.
* Derived from the featured model table, not user-configurable.
* Derived from associated mmproj metadata, not user-configurable.
*/
vision_capable?: boolean;
};
@@ -1544,6 +1554,8 @@ export type ToolAnnotations = {
title?: string;
};
export type ToolCallingMode = 'auto' | 'force_native' | 'force_emulated';
export type ToolConfirmationRequest = {
arguments: JsonObject;
id: string;
@@ -3241,6 +3253,22 @@ export type StartTetrateSetupResponses = {
export type StartTetrateSetupResponse = StartTetrateSetupResponses[keyof StartTetrateSetupResponses];
export type ListBuiltinChatTemplatesData = {
body?: never;
path?: never;
query?: never;
url: '/local-inference/chat-templates/builtin';
};
export type ListBuiltinChatTemplatesResponses = {
/**
* llama.cpp built-in chat template names
*/
200: Array<string>;
};
export type ListBuiltinChatTemplatesResponse = ListBuiltinChatTemplatesResponses[keyof ListBuiltinChatTemplatesResponses];
export type DownloadHfModelData = {
body: DownloadModelRequest;
path?: never;
@@ -4,9 +4,12 @@ import { Button } from '../../ui/button';
import { Switch } from '../../ui/switch';
import {
getModelSettings,
listBuiltinChatTemplates,
updateModelSettings,
type ChatTemplate,
type ModelSettings,
type SamplingConfig,
type ToolCallingMode,
} from '../../../api';
import { defineMessages, useIntl } from '../../../i18n';
@@ -161,16 +164,59 @@ const i18n = defineMessages({
},
toolCalling: {
id: 'modelSettingsPanel.toolCalling',
defaultMessage: 'Tool Calling',
defaultMessage: 'Tool calling',
},
nativeToolCalling: {
id: 'modelSettingsPanel.nativeToolCalling',
defaultMessage: 'Native tool calling',
toolCallingDescription: {
id: 'modelSettingsPanel.toolCallingDescription',
defaultMessage: 'Choose how local models select native or emulated tool calling',
},
nativeToolCallingDescription: {
id: 'modelSettingsPanel.nativeToolCallingDescription',
defaultMessage:
"Use the model's built-in tool-call format instead of the shell-command emulator. Enable for large models that reliably support tool calling.",
toolCallingAuto: {
id: 'modelSettingsPanel.toolCallingAuto',
defaultMessage: 'Auto',
},
toolCallingForceNative: {
id: 'modelSettingsPanel.toolCallingForceNative',
defaultMessage: 'Force native',
},
toolCallingForceEmulated: {
id: 'modelSettingsPanel.toolCallingForceEmulated',
defaultMessage: 'Force emulated',
},
chatTemplate: {
id: 'modelSettingsPanel.chatTemplate',
defaultMessage: 'Chat template',
},
chatTemplateDescription: {
id: 'modelSettingsPanel.chatTemplateDescription',
defaultMessage: 'Use embedded GGUF metadata, a llama.cpp built-in template, or inline Jinja',
},
chatTemplateEmbedded: {
id: 'modelSettingsPanel.chatTemplateEmbedded',
defaultMessage: 'Embedded',
},
chatTemplateBuiltin: {
id: 'modelSettingsPanel.chatTemplateBuiltin',
defaultMessage: 'Built-in',
},
chatTemplateCustomInline: {
id: 'modelSettingsPanel.chatTemplateCustomInline',
defaultMessage: 'Custom inline',
},
builtinChatTemplate: {
id: 'modelSettingsPanel.builtinChatTemplate',
defaultMessage: 'Built-in template',
},
builtinChatTemplateDescription: {
id: 'modelSettingsPanel.builtinChatTemplateDescription',
defaultMessage: 'Select a llama.cpp built-in template name',
},
customChatTemplate: {
id: 'modelSettingsPanel.customChatTemplate',
defaultMessage: 'Custom chat template',
},
customChatTemplateDescription: {
id: 'modelSettingsPanel.customChatTemplateDescription',
defaultMessage: 'Paste the full Jinja chat template source',
},
});
@@ -194,10 +240,12 @@ const DEFAULT_SETTINGS: ModelSettings = {
use_mlock: false,
flash_attention: null,
n_threads: null,
native_tool_calling: false,
tool_calling: 'auto',
chat_template: { type: 'embedded' },
};
type SamplingType = SamplingConfig['type'];
type ChatTemplateMode = 'embedded' | 'builtin' | 'custom_inline';
function NumberField({
label,
@@ -302,16 +350,59 @@ function SelectField<T extends string>({
);
}
function TextAreaField({
label,
description,
value,
onChange,
onBlur,
}: {
label: string;
description?: string;
value: string;
onChange: (v: string) => void;
onBlur: () => void;
}) {
return (
<div className="flex flex-col gap-1">
<label className="text-xs font-medium text-text-default">{label}</label>
{description && <span className="text-xs text-text-muted">{description}</span>}
<textarea
value={value}
onChange={(e) => onChange(e.target.value)}
onBlur={onBlur}
spellCheck={false}
className="min-h-32 rounded border border-border-subtle bg-background-default px-2 py-1 font-mono text-xs text-text-default"
/>
</div>
);
}
export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
const intl = useIntl();
const [settings, setSettings] = useState<ModelSettings>(DEFAULT_SETTINGS);
const [chatTemplateDraft, setChatTemplateDraft] = useState('');
const [builtinTemplateDraft, setBuiltinTemplateDraft] = useState('chatml');
const [builtinTemplateOptions, setBuiltinTemplateOptions] = useState<string[]>(['chatml']);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const load = useCallback(async () => {
try {
const res = await getModelSettings({ path: { model_id: modelId } });
if (res.data) setSettings(res.data);
const [settingsResult, builtinsResult] = await Promise.allSettled([
getModelSettings({ path: { model_id: modelId } }),
listBuiltinChatTemplates(),
]);
if (builtinsResult.status === 'fulfilled' && builtinsResult.value.data?.length) {
setBuiltinTemplateOptions(builtinsResult.value.data);
}
if (settingsResult.status === 'fulfilled' && settingsResult.value.data) {
setSettings({
...settingsResult.value.data,
tool_calling: settingsResult.value.data.tool_calling ?? 'auto',
chat_template: settingsResult.value.data.chat_template ?? { type: 'embedded' },
});
}
} catch {
// use defaults
} finally {
@@ -323,6 +414,18 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
load();
}, [load]);
useEffect(() => {
const chatTemplate = settings.chat_template;
if (chatTemplate?.type === 'custom_inline') {
setChatTemplateDraft(chatTemplate.template ?? '');
} else {
setChatTemplateDraft('');
}
if (chatTemplate?.type === 'builtin') {
setBuiltinTemplateDraft(chatTemplate.name ?? 'chatml');
}
}, [settings.chat_template]);
const save = async (updated: ModelSettings) => {
setSettings(updated);
setSaving(true);
@@ -342,6 +445,38 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
};
const samplingType: SamplingType = settings.sampling?.type ?? 'Temperature';
const chatTemplate = settings.chat_template ?? { type: 'embedded' };
const chatTemplateMode: ChatTemplateMode =
chatTemplate.type === 'custom_inline'
? 'custom_inline'
: chatTemplate.type === 'builtin'
? 'builtin'
: 'embedded';
const setChatTemplateMode = (mode: ChatTemplateMode) => {
let next: ChatTemplate;
if (mode === 'custom_inline') {
next = { type: 'custom_inline', template: chatTemplateDraft };
} else if (mode === 'builtin') {
next = { type: 'builtin', name: builtinTemplateDraft.trim() || builtinTemplateOptions[0] || 'chatml' };
} else {
next = { type: 'embedded' };
}
updateField('chat_template', next);
};
const setBuiltinTemplateName = (name: string) => {
setBuiltinTemplateDraft(name);
if (chatTemplateMode === 'builtin') {
updateField('chat_template', { type: 'builtin', name });
}
};
const saveChatTemplateDraft = () => {
if (chatTemplateMode === 'custom_inline') {
updateField('chat_template', { type: 'custom_inline', template: chatTemplateDraft });
}
};
const setSamplingType = (type: SamplingType) => {
let sampling: SamplingConfig;
@@ -366,6 +501,10 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
save({ ...settings, sampling: { ...settings.sampling!, ...partial } as SamplingConfig });
};
const visibleBuiltinTemplateOptions = builtinTemplateOptions.includes(builtinTemplateDraft)
? builtinTemplateOptions
: [builtinTemplateDraft, ...builtinTemplateOptions].filter(Boolean);
if (loading) {
return <div className="py-2 text-xs text-text-muted">{intl.formatMessage(i18n.loadingSettings)}</div>;
}
@@ -585,16 +724,52 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
]}
onChange={(v) => updateField('flash_attention', v === 'auto' ? null : v === 'on')}
/>
</div>
{/* Tool Calling */}
<div className="space-y-2">
<h5 className="text-xs font-medium text-text-default">{intl.formatMessage(i18n.toolCalling)}</h5>
<ToggleField
label={intl.formatMessage(i18n.nativeToolCalling)}
description={intl.formatMessage(i18n.nativeToolCallingDescription)}
value={settings.native_tool_calling ?? false}
onChange={(v) => updateField('native_tool_calling', v)}
<SelectField<ToolCallingMode>
label={intl.formatMessage(i18n.toolCalling)}
description={intl.formatMessage(i18n.toolCallingDescription)}
value={settings.tool_calling ?? 'auto'}
options={[
{ value: 'auto', label: intl.formatMessage(i18n.toolCallingAuto) },
{ value: 'force_native', label: intl.formatMessage(i18n.toolCallingForceNative) },
{ value: 'force_emulated', label: intl.formatMessage(i18n.toolCallingForceEmulated) },
]}
onChange={(v) => updateField('tool_calling', v)}
/>
<SelectField<ChatTemplateMode>
label={intl.formatMessage(i18n.chatTemplate)}
description={intl.formatMessage(i18n.chatTemplateDescription)}
value={chatTemplateMode}
options={[
{ value: 'embedded', label: intl.formatMessage(i18n.chatTemplateEmbedded) },
{ value: 'builtin', label: intl.formatMessage(i18n.chatTemplateBuiltin) },
{
value: 'custom_inline',
label: intl.formatMessage(i18n.chatTemplateCustomInline),
},
]}
onChange={setChatTemplateMode}
/>
{chatTemplateMode === 'builtin' && (
<SelectField<string>
label={intl.formatMessage(i18n.builtinChatTemplate)}
description={intl.formatMessage(i18n.builtinChatTemplateDescription)}
value={builtinTemplateDraft}
options={visibleBuiltinTemplateOptions.map((template) => ({
value: template,
label: template,
}))}
onChange={setBuiltinTemplateName}
/>
)}
{chatTemplateMode === 'custom_inline' && (
<TextAreaField
label={intl.formatMessage(i18n.customChatTemplate)}
description={intl.formatMessage(i18n.customChatTemplateDescription)}
value={chatTemplateDraft}
onChange={setChatTemplateDraft}
onBlur={saveChatTemplateDraft}
/>
)}
</div>
</div>
);
+40 -7
View File
@@ -2183,6 +2183,27 @@
"modelSettingsPanel.batchSizeDescription": {
"defaultMessage": "Prompt processing batch"
},
"modelSettingsPanel.builtinChatTemplate": {
"defaultMessage": "Built-in template"
},
"modelSettingsPanel.builtinChatTemplateDescription": {
"defaultMessage": "Select a llama.cpp built-in template name"
},
"modelSettingsPanel.chatTemplate": {
"defaultMessage": "Chat template"
},
"modelSettingsPanel.chatTemplateBuiltin": {
"defaultMessage": "Built-in"
},
"modelSettingsPanel.chatTemplateCustomInline": {
"defaultMessage": "Custom inline"
},
"modelSettingsPanel.chatTemplateDescription": {
"defaultMessage": "Use embedded GGUF metadata, a llama.cpp built-in template, or inline Jinja"
},
"modelSettingsPanel.chatTemplateEmbedded": {
"defaultMessage": "Embedded"
},
"modelSettingsPanel.contextAndGeneration": {
"defaultMessage": "Context & Generation"
},
@@ -2192,6 +2213,12 @@
"modelSettingsPanel.contextSizeDescription": {
"defaultMessage": "Max context window (0 = model default)"
},
"modelSettingsPanel.customChatTemplate": {
"defaultMessage": "Custom chat template"
},
"modelSettingsPanel.customChatTemplateDescription": {
"defaultMessage": "Paste the full Jinja chat template source"
},
"modelSettingsPanel.etaLearningRate": {
"defaultMessage": "Eta (learning rate)"
},
@@ -2231,12 +2258,6 @@
"modelSettingsPanel.minP": {
"defaultMessage": "Min P"
},
"modelSettingsPanel.nativeToolCalling": {
"defaultMessage": "Native tool calling"
},
"modelSettingsPanel.nativeToolCallingDescription": {
"defaultMessage": "Use the model's built-in tool-call format instead of the shell-command emulator. Enable for large models that reliably support tool calling."
},
"modelSettingsPanel.performance": {
"defaultMessage": "Performance"
},
@@ -2289,7 +2310,19 @@
"defaultMessage": "CPU threads for generation"
},
"modelSettingsPanel.toolCalling": {
"defaultMessage": "Tool Calling"
"defaultMessage": "Tool calling"
},
"modelSettingsPanel.toolCallingAuto": {
"defaultMessage": "Auto"
},
"modelSettingsPanel.toolCallingDescription": {
"defaultMessage": "Choose how local models select native or emulated tool calling"
},
"modelSettingsPanel.toolCallingForceEmulated": {
"defaultMessage": "Force emulated"
},
"modelSettingsPanel.toolCallingForceNative": {
"defaultMessage": "Force native"
},
"modelSettingsPanel.topK": {
"defaultMessage": "Top K"
+42 -9
View File
@@ -2201,6 +2201,48 @@
"modelSettingsPanel.flashAttentionDescription": {
"defaultMessage": "Включить оптимизацию flash attention"
},
"modelSettingsPanel.builtinChatTemplate": {
"defaultMessage": "Встроенный шаблон"
},
"modelSettingsPanel.builtinChatTemplateDescription": {
"defaultMessage": "Выберите имя встроенного шаблона llama.cpp"
},
"modelSettingsPanel.chatTemplate": {
"defaultMessage": "Шаблон чата"
},
"modelSettingsPanel.chatTemplateBuiltin": {
"defaultMessage": "Встроенный"
},
"modelSettingsPanel.chatTemplateCustomInline": {
"defaultMessage": "Пользовательский inline"
},
"modelSettingsPanel.chatTemplateDescription": {
"defaultMessage": "Использовать встроенные метаданные GGUF, встроенный шаблон llama.cpp или inline-шаблон Jinja"
},
"modelSettingsPanel.chatTemplateEmbedded": {
"defaultMessage": "Встроенный"
},
"modelSettingsPanel.customChatTemplate": {
"defaultMessage": "Пользовательский шаблон чата"
},
"modelSettingsPanel.customChatTemplateDescription": {
"defaultMessage": "Вставьте полный исходный код шаблона Jinja"
},
"modelSettingsPanel.toolCalling": {
"defaultMessage": "Вызов инструментов"
},
"modelSettingsPanel.toolCallingAuto": {
"defaultMessage": "Авто"
},
"modelSettingsPanel.toolCallingDescription": {
"defaultMessage": "Выберите, как локальные модели используют нативный или эмулируемый вызов инструментов"
},
"modelSettingsPanel.toolCallingForceEmulated": {
"defaultMessage": "Принудительно эмулируемый"
},
"modelSettingsPanel.toolCallingForceNative": {
"defaultMessage": "Принудительно нативный"
},
"modelSettingsPanel.frequencyPenalty": {
"defaultMessage": "Штраф частоты"
},
@@ -2231,12 +2273,6 @@
"modelSettingsPanel.minP": {
"defaultMessage": "Min P"
},
"modelSettingsPanel.nativeToolCalling": {
"defaultMessage": "Нативный вызов инструментов"
},
"modelSettingsPanel.nativeToolCallingDescription": {
"defaultMessage": "Использовать встроенный формат вызова инструментов модели вместо эмулятора shell-команд. Включайте для крупных моделей, надежно поддерживающих вызов tool calling."
},
"modelSettingsPanel.performance": {
"defaultMessage": "Производительность"
},
@@ -2288,9 +2324,6 @@
"modelSettingsPanel.threadsDescription": {
"defaultMessage": "Потоки CPU для генерации"
},
"modelSettingsPanel.toolCalling": {
"defaultMessage": "Вызов инструментов"
},
"modelSettingsPanel.topK": {
"defaultMessage": "Top K"
},
+42 -9
View File
@@ -2279,6 +2279,48 @@
"modelSettingsPanel.flashAttentionDescription": {
"defaultMessage": "Flaş dikkati optimizasyonunu etkinleştir"
},
"modelSettingsPanel.builtinChatTemplate": {
"defaultMessage": "Yerleşik şablon"
},
"modelSettingsPanel.builtinChatTemplateDescription": {
"defaultMessage": "Bir llama.cpp yerleşik şablon adı seçin"
},
"modelSettingsPanel.chatTemplate": {
"defaultMessage": "Sohbet şablonu"
},
"modelSettingsPanel.chatTemplateBuiltin": {
"defaultMessage": "Yerleşik"
},
"modelSettingsPanel.chatTemplateCustomInline": {
"defaultMessage": "Özel satır içi"
},
"modelSettingsPanel.chatTemplateDescription": {
"defaultMessage": "Gömülü GGUF meta verilerini, llama.cpp yerleşik şablonunu veya satır içi Jinja şablonunu kullan"
},
"modelSettingsPanel.chatTemplateEmbedded": {
"defaultMessage": "Gömülü"
},
"modelSettingsPanel.customChatTemplate": {
"defaultMessage": "Özel sohbet şablonu"
},
"modelSettingsPanel.customChatTemplateDescription": {
"defaultMessage": "Tam Jinja sohbet şablonu kaynağını yapıştır"
},
"modelSettingsPanel.toolCalling": {
"defaultMessage": "Araç çağırma"
},
"modelSettingsPanel.toolCallingAuto": {
"defaultMessage": "Otomatik"
},
"modelSettingsPanel.toolCallingDescription": {
"defaultMessage": "Yerel modellerin yerel veya emüle araç çağırmayı nasıl seçeceğini belirleyin"
},
"modelSettingsPanel.toolCallingForceEmulated": {
"defaultMessage": "Emüle etmeye zorla"
},
"modelSettingsPanel.toolCallingForceNative": {
"defaultMessage": "Yereli zorla"
},
"modelSettingsPanel.frequencyPenalty": {
"defaultMessage": "Frekans cezası"
},
@@ -2309,12 +2351,6 @@
"modelSettingsPanel.minP": {
"defaultMessage": "Min P"
},
"modelSettingsPanel.nativeToolCalling": {
"defaultMessage": "Yerel araç çağrısı"
},
"modelSettingsPanel.nativeToolCallingDescription": {
"defaultMessage": "Kabuk komutu emülatörü yerine modelin yerleşik araç çağrısı formatını kullanın. Araç çağırmayı güvenilir şekilde destekleyen büyük modeller için etkinleştirin."
},
"modelSettingsPanel.performance": {
"defaultMessage": "Performans"
},
@@ -2366,9 +2402,6 @@
"modelSettingsPanel.threadsDescription": {
"defaultMessage": "Nesil için CPU iş parçacıkları"
},
"modelSettingsPanel.toolCalling": {
"defaultMessage": "Takım Çağırma"
},
"modelSettingsPanel.topK": {
"defaultMessage": "En İyi K"
},
+42 -9
View File
@@ -2150,6 +2150,48 @@
"modelSettingsPanel.flashAttentionDescription": {
"defaultMessage": "启用 Flash Attention 优化"
},
"modelSettingsPanel.builtinChatTemplate": {
"defaultMessage": "内置模板"
},
"modelSettingsPanel.builtinChatTemplateDescription": {
"defaultMessage": "选择 llama.cpp 内置模板名称"
},
"modelSettingsPanel.chatTemplate": {
"defaultMessage": "聊天模板"
},
"modelSettingsPanel.chatTemplateBuiltin": {
"defaultMessage": "内置"
},
"modelSettingsPanel.chatTemplateCustomInline": {
"defaultMessage": "自定义内联"
},
"modelSettingsPanel.chatTemplateDescription": {
"defaultMessage": "使用嵌入式 GGUF 元数据、llama.cpp 内置模板或内联 Jinja 模板"
},
"modelSettingsPanel.chatTemplateEmbedded": {
"defaultMessage": "嵌入式"
},
"modelSettingsPanel.customChatTemplate": {
"defaultMessage": "自定义聊天模板"
},
"modelSettingsPanel.customChatTemplateDescription": {
"defaultMessage": "粘贴完整的 Jinja 聊天模板源码"
},
"modelSettingsPanel.toolCalling": {
"defaultMessage": "工具调用"
},
"modelSettingsPanel.toolCallingAuto": {
"defaultMessage": "自动"
},
"modelSettingsPanel.toolCallingDescription": {
"defaultMessage": "选择本地模型如何使用原生或模拟工具调用"
},
"modelSettingsPanel.toolCallingForceEmulated": {
"defaultMessage": "强制模拟"
},
"modelSettingsPanel.toolCallingForceNative": {
"defaultMessage": "强制原生"
},
"modelSettingsPanel.frequencyPenalty": {
"defaultMessage": "频率惩罚"
},
@@ -2180,12 +2222,6 @@
"modelSettingsPanel.minP": {
"defaultMessage": "Min P"
},
"modelSettingsPanel.nativeToolCalling": {
"defaultMessage": "原生工具调用"
},
"modelSettingsPanel.nativeToolCallingDescription": {
"defaultMessage": "使用模型内置的工具调用格式,而不是 shell 命令模拟器。对可靠支持工具调用的大模型建议启用。"
},
"modelSettingsPanel.performance": {
"defaultMessage": "性能"
},
@@ -2237,9 +2273,6 @@
"modelSettingsPanel.threadsDescription": {
"defaultMessage": "用于生成的 CPU 线程数"
},
"modelSettingsPanel.toolCalling": {
"defaultMessage": "工具调用"
},
"modelSettingsPanel.topK": {
"defaultMessage": "Top K"
},
-15
View File
@@ -97,7 +97,6 @@ export function getTextAndImageContent(message: Message): {
// Strip assistant-only markup that shouldn't appear in rendered text
if (message.role === 'assistant') {
textContent = stripToolCallMarkers(textContent);
textContent = textContent.replace(/<think>[\s\S]*?<\/think>/gi, '');
}
return { textContent, imagePaths };
@@ -122,20 +121,6 @@ export function getThinkingContent(message: Message): string | null {
}
}
// Inline <think> tags in assistant text content
if (message.role === 'assistant') {
for (const content of message.content) {
if (content.type === 'text') {
const regex = /<think>([\s\S]*?)<\/think>/gi;
let match;
while ((match = regex.exec(content.text)) !== null) {
const text = match[1].trim();
if (text) parts.push(text);
}
}
}
}
return parts.length > 0 ? parts.join('') : null;
}