feat(agent): keep max completion tokens as default or a custom cap

Unset (0) now resolves at call time (2048 / 4096 / 24576 with sandbox) so the editor can offer Default vs Custom without silently rewriting a saved value.
This commit is contained in:
wizardchen
2026-08-30 10:46:09 +08:00
committed by lyingbug
parent b419058abf
commit 4d78f9dc12
13 changed files with 174 additions and 50 deletions
+6 -6
View File
@@ -27,7 +27,7 @@ builtin_agents:
system_prompt_id: "default_kb"
context_template_id: "default_context"
temperature: 0.7
max_completion_tokens: 2048
max_completion_tokens: 0
web_search_enabled: true
web_search_max_results: 5
multi_turn_enabled: true
@@ -70,7 +70,7 @@ builtin_agents:
agent_type: "rag-qa"
system_prompt: ""
temperature: 0.7
max_completion_tokens: 32768
max_completion_tokens: 0
max_iterations: 50
kb_selection_mode: "all"
retrieve_kb_only_when_mentioned: false
@@ -118,7 +118,7 @@ builtin_agents:
agent_type: "data-analysis"
system_prompt_id: "data_analyst"
temperature: 0.3
max_completion_tokens: 32768
max_completion_tokens: 0
max_iterations: 30
kb_selection_mode: "all"
retrieve_kb_only_when_mentioned: false
@@ -163,7 +163,7 @@ builtin_agents:
agent_type: "wiki-qa"
system_prompt_id: "wiki_researcher"
temperature: 0.7
max_completion_tokens: 32768
max_completion_tokens: 0
max_iterations: 30
kb_selection_mode: "all"
retrieve_kb_only_when_mentioned: false
@@ -207,7 +207,7 @@ builtin_agents:
agent_type: "custom"
system_prompt_id: "wiki_fixer"
temperature: 0.7
max_completion_tokens: 32768
max_completion_tokens: 0
max_iterations: 30
kb_selection_mode: "selected"
retrieve_kb_only_when_mentioned: false
@@ -257,7 +257,7 @@ builtin_agents:
agent_type: "custom"
system_prompt_id: "skill_installer"
temperature: 0.2
max_completion_tokens: 32768
max_completion_tokens: 24576
max_iterations: 30
kb_selection_mode: "none"
retrieve_kb_only_when_mentioned: false
+1 -1
View File
@@ -44,7 +44,7 @@ export interface CustomAgentConfig {
model_id?: string;
rerank_model_id?: string; // ReRank 模型 ID
temperature?: number;
max_completion_tokens?: number; // 最大生成 token 数;智能推理 ReAct 轮次会抬到至少 32768,避免工具 JSON 被网关默认 4096 截断
max_completion_tokens?: number; // 0 = 跟随系统默认(快速问答 2048;智能推理 4096,绑沙箱可写文件时 24576)。大于 0 为自定义上限
thinking?: boolean; // 是否启用思考模式(支持扩展思考的模型)
citation_enabled?: boolean; // 是否在最终回答中输出知识库/网页来源引用(默认开启)
+4 -1
View File
@@ -1008,6 +1008,8 @@ export default {
rewritePromptUser: 'Rewrite User Prompt',
rewritePromptUserPlaceholder: 'Leave empty to use default prompt',
maxCompletionTokens: 'Max Completion Tokens',
maxCompletionTokensDefault: 'Default',
maxCompletionTokensCustom: 'Custom',
fallbackStrategy: 'Fallback Strategy',
fallbackResponse: 'Fixed Response',
fallbackResponsePlaceholder: 'Sorry, I cannot answer this question.',
@@ -5529,7 +5531,8 @@ export default {
contextTemplate: 'Define how retrieved content is formatted before passing to the model',
model: 'Select the LLM used by the agent',
temperature: 'Control output randomness, 0 is most deterministic, 1 is most random',
maxTokens: 'Maximum number of tokens for model-generated responses',
maxTokens: 'Maximum tokens for the model reply. Default is 2048. Custom values are saved as entered.',
maxTokensAgent: 'Maximum tokens generated in each reasoning round, including tool-call JSON. Default is 4096 without a sandbox, or 24576 when a sandbox can write or edit files. A custom value is saved as entered and is not changed later.',
thinking: 'Enable extended thinking capability (requires model support)',
conversationSection: 'Configure multi-turn conversation and query rewriting parameters',
conversationSectionAgent: 'How much earlier conversation each turn carries. Smart reasoning is always multi-turn',
+4 -1
View File
@@ -1100,7 +1100,8 @@ export default {
contextTemplate: '검색된 콘텐츠를 모델에 전달하기 전에 형식을 정의합니다',
model: '에이전트가 사용할 대규모 언어 모델을 선택하세요',
temperature: '출력의 무작위성을 제어합니다. 0이 가장 확정적, 1이 가장 무작위',
maxTokens: '모델이 생성하는 응답의 최대 토큰 수',
maxTokens: '모델 응답의 최대 토큰 수. 「기본값」은 2048입니다. 「사용자 지정」은 입력한 값을 그대로 저장합니다.',
maxTokensAgent: '각 추론 라운드에서 생성할 최대 토큰 수(도구 호출 JSON 포함). 「기본값」은 샌드박스 없으면 4096, 파일 쓰기/편집이 가능하면 24576입니다. 「사용자 지정」은 입력한 값을 그대로 저장합니다.',
thinking: '모델의 확장 사고 기능 활성화 (모델 지원 필요)',
conversationSection: '다중 턴 대화 및 질문 재작성 관련 매개변수 설정',
conversationSectionAgent: '매 턴에 실어 보낼 이전 대화 분량 설정 (스마트 추론은 항상 다중 턴)',
@@ -5737,6 +5738,8 @@ export default {
rewritePromptUser: '사용자 프롬프트 다시 작성',
rewritePromptUserPlaceholder: '시스템 기본 프롬프트를 사용하려면 비워 두세요.',
maxCompletionTokens: '생성된 토큰의 최대 수',
maxCompletionTokensDefault: '기본값',
maxCompletionTokensCustom: '사용자 지정',
fallbackStrategy: '폴백 전략',
fallbackResponse: '고정 응답 내용',
fallbackResponsePlaceholder: '죄송합니다. 이 질문에는 답변해 드릴 수 없습니다.',
+4 -1
View File
@@ -1100,7 +1100,8 @@ export default {
contextTemplate: 'Определите формат передачи найденного контента модели',
model: 'Выберите LLM, используемую агентом',
temperature: 'Контроль случайности выхода: 0 — наиболее детерминированный, 1 — наиболее случайный',
maxTokens: 'Максимальное количество токенов в ответе модели',
maxTokens: 'Максимум токенов в ответе. «По умолчанию» — 2048. «Своё» сохраняет введённое число.',
maxTokensAgent: 'Максимум токенов за один раунд рассуждения, включая JSON вызовов инструментов. «По умолчанию»: 4096 без песочницы, 24576 с записью/правкой файлов. «Своё» сохраняет введённое число и больше не меняется.',
thinking: 'Включить расширенное мышление модели (требуется поддержка модели)',
conversationSection: 'Настройка параметров многооборотного диалога и перефразирования вопросов',
conversationSectionAgent: 'Объём предыдущего диалога в каждом ходе. Умные рассуждения всегда многооборотные',
@@ -5737,6 +5738,8 @@ export default {
rewritePromptUser: 'Rewrite User Prompt',
rewritePromptUserPlaceholder: 'Leave empty to use default prompt',
maxCompletionTokens: 'Max Completion Tokens',
maxCompletionTokensDefault: 'По умолчанию',
maxCompletionTokensCustom: 'Своё',
fallbackStrategy: 'Fallback Strategy',
fallbackResponse: 'Fixed Response',
fallbackResponsePlaceholder: 'Sorry, I cannot answer this question.',
+4 -1
View File
@@ -1102,7 +1102,8 @@ export default {
contextTemplate: '定义如何将检索到的内容格式化后传递给模型',
model: '选择智能体使用的大语言模型',
temperature: '控制输出的随机性,0 最确定,1 最随机',
maxTokens: '模型生成回复的最大Token数量',
maxTokens: '模型生成回复的最大 Token 数。选「默认」时为 2048;选「自定义」后按你填的数保存。',
maxTokensAgent: '每一轮推理的最大生成 Token(含工具调用 JSON)。选「默认」时,未绑沙箱为 4096,绑了沙箱(可写/改文件)为 24576。选「自定义」后按你填的数保存,不再自动改。',
thinking: '启用模型的扩展思考能力(需要模型支持)',
conversationSection: '配置多轮对话开关与问题改写开关(改写提示词见「提示词」)',
conversationSectionAgent: '配置每轮携带多少历史对话。智能推理始终为多轮模式。',
@@ -5739,6 +5740,8 @@ export default {
rewritePromptUser: '改写用户提示词',
rewritePromptUserPlaceholder: '留空使用系统默认提示词',
maxCompletionTokens: '最大生成Token数',
maxCompletionTokensDefault: '默认',
maxCompletionTokensCustom: '自定义',
fallbackStrategy: '兜底策略',
fallbackResponse: '固定回复内容',
fallbackResponsePlaceholder: '抱歉,我无法回答这个问题。',
+65 -8
View File
@@ -610,14 +610,25 @@
</div>
</div>
<!-- 最大生成Token数(仅普通模式) -->
<div v-if="!isAgentMode" class="setting-row">
<!-- 最大生成Token数:0 表示跟随系统默认;自定义后按输入值保存 -->
<div class="setting-row">
<div class="setting-info">
<label>{{ $t('agent.editor.maxCompletionTokens') }}</label>
<p class="desc">{{ $t('agentEditor.desc.maxTokens') }}</p>
<p class="desc">{{ isAgentMode ? $t('agentEditor.desc.maxTokensAgent') :
$t('agentEditor.desc.maxTokens') }}
</p>
</div>
<div class="setting-control">
<t-input-number v-model="formData.config.max_completion_tokens" :min="100" :max="100000"
<div class="setting-control max-tokens-control">
<t-radio-group v-model="maxCompletionTokensMode">
<t-radio-button value="default">{{ $t('agent.editor.maxCompletionTokensDefault')
}}</t-radio-button>
<t-radio-button value="custom">{{ $t('agent.editor.maxCompletionTokensCustom')
}}</t-radio-button>
</t-radio-group>
<span v-if="maxCompletionTokensMode === 'default'" class="max-tokens-value">
{{ effectiveDefaultMaxCompletionTokens }}
</span>
<t-input-number v-else v-model="formData.config.max_completion_tokens" :min="100" :max="100000"
:step="100" theme="column" />
</div>
</div>
@@ -2202,9 +2213,20 @@ const defaultKeywordThreshold = ref(0.3);
const defaultVectorThreshold = ref(0.5);
const defaultRerankTopK = ref(5);
const defaultRerankThreshold = ref(0.5);
const defaultMaxCompletionTokens = ref(2048);
const defaultQuickAnswerMaxCompletionTokens = 2048;
const defaultSmartReasoningMaxCompletionTokens = 4096;
const defaultSandboxWriteMaxCompletionTokens = 24576;
const defaultTemperature = ref(0.7);
const defaultMaxCompletionTokensFor = (mode: string, sandboxConfigId?: string) => {
if (mode === 'smart-reasoning') {
return sandboxConfigId
? defaultSandboxWriteMaxCompletionTokens
: defaultSmartReasoningMaxCompletionTokens;
}
return defaultQuickAnswerMaxCompletionTokens;
};
// 知识库相关工具列表(用于 watch(hasKnowledgeBase) 从""变""时 seed 默认工具)
const knowledgeBaseTools = ['grep_chunks', 'knowledge_search', 'list_knowledge_chunks', 'query_knowledge_graph', 'get_document_info', 'database_query'];
@@ -2575,7 +2597,7 @@ const defaultFormData = {
model_id: '',
rerank_model_id: '',
temperature: 0.7,
max_completion_tokens: 2048,
max_completion_tokens: 0,
thinking: false, // 默认禁用思考模式
citation_enabled: true, // 默认输出知识库/网页来源引用
// Agent模式设置
@@ -2719,6 +2741,23 @@ const agentMode = computed({
const isAgentMode = computed(() => agentMode.value === 'smart-reasoning');
const effectiveDefaultMaxCompletionTokens = computed(() =>
defaultMaxCompletionTokensFor(agentMode.value, formData.value.config.sandbox_config_id),
);
const maxCompletionTokensMode = computed({
get: () => (formData.value.config.max_completion_tokens > 0 ? 'custom' : 'default'),
set: (mode: 'default' | 'custom') => {
if (mode === 'default') {
formData.value.config.max_completion_tokens = 0;
return;
}
if (!formData.value.config.max_completion_tokens) {
formData.value.config.max_completion_tokens = effectiveDefaultMaxCompletionTokens.value;
}
},
});
const currentIntentTemplate = computed(() =>
intentPromptTemplates.value.find((template) => template.id === selectedIntent.value),
);
@@ -3250,6 +3289,7 @@ watch(() => props.visible, async (val) => {
// 附件解析调优字段:旧数据缺省时置 0(表示使用全局默认)
if (agentData.config.attachment_ocr_max_pages == null) agentData.config.attachment_ocr_max_pages = 0;
if (agentData.config.attachment_parse_wait_timeout_sec == null) agentData.config.attachment_parse_wait_timeout_sec = 0;
if (agentData.config.max_completion_tokens == null) agentData.config.max_completion_tokens = 0;
// 长期记忆:后端用 omitempty,跟随空间设置的智能体不带这个字段。
// 不补成 true 的话开关会显示为"",用户随手一存就真的把记忆关了。
if (agentData.config.memory_enabled == null) agentData.config.memory_enabled = true;
@@ -3285,7 +3325,7 @@ watch(() => props.visible, async (val) => {
newFormData.config.vector_threshold = defaultVectorThreshold.value;
newFormData.config.rerank_top_k = defaultRerankTopK.value;
newFormData.config.rerank_threshold = defaultRerankThreshold.value;
newFormData.config.max_completion_tokens = defaultMaxCompletionTokens.value;
newFormData.config.max_completion_tokens = 0;
newFormData.config.temperature = defaultTemperature.value;
// 应用系统默认提示词(根据模式填充)
const isAgent = newFormData.config.agent_mode === 'smart-reasoning';
@@ -5126,6 +5166,16 @@ const handleSave = async () => {
justify-content: flex-start;
}
&.max-tokens-control {
flex-direction: column;
align-items: flex-end;
gap: 8px;
:deep(.t-input-number) {
width: 140px;
}
}
// 让 select 和 input 占满控件区域
:deep(.t-select),
:deep(.t-input),
@@ -5479,6 +5529,13 @@ const handleSave = async () => {
color: var(--td-text-color-primary);
}
.max-tokens-value {
font-family: var(--app-font-family-mono);
font-variant-numeric: tabular-nums;
font-size: 12px;
color: var(--td-text-color-placeholder);
}
// 推荐问题列表
.suggested-prompts-list {
display: flex;
+5 -3
View File
@@ -85,14 +85,16 @@ func (e *AgentEngine) getLLMCallTimeout() time.Duration {
}
// getCompletionTokenBudget is the max_tokens / max_completion_tokens sent on
// each ReAct LLM round. Leaving it unset lets providers default to 4096 and
// truncate write_sandbox_file (and similar) tool-call JSON.
// each ReAct LLM round. Unset without a sandbox is 4096; unset with a
// sandbox (write_sandbox_file / edit_sandbox_file) is 24576.
func (e *AgentEngine) getCompletionTokenBudget() int {
configured := 0
sandboxID := ""
if e.config != nil {
configured = e.config.MaxCompletionTokens
sandboxID = e.config.SandboxConfigID
}
return types.AgentRoundMaxCompletionTokens(configured)
return types.AgentRoundMaxCompletionTokensFor(configured, sandboxID)
}
// generateEventID generates a unique event ID with type suffix for better traceability
+17
View File
@@ -446,6 +446,23 @@ func TestStreamThinkingToEventBus_SetsCompletionTokenBudget(t *testing.T) {
[]chat.Message{{Role: "user", Content: "test"}}, nil, 0, "sess-1")
require.NoError(t, err)
require.Len(t, mock.opts, 1)
assert.Equal(t, types.DefaultSmartReasoningMaxCompletionTokens, mock.opts[0].MaxTokens)
assert.Equal(t, types.DefaultSmartReasoningMaxCompletionTokens, mock.opts[0].MaxCompletionTokens)
})
t.Run("defaults to the write-file budget when a sandbox is bound", func(t *testing.T) {
mock := &mockChat{
responses: []mockResponse{
{chunks: []types.StreamResponse{{Content: "ok", Done: true, FinishReason: "stop"}}},
},
}
engine := newTestEngine(t, mock, func(cfg *types.AgentConfig) {
cfg.SandboxConfigID = "cfg-a"
})
_, err := engine.streamThinkingToEventBus(context.Background(),
[]chat.Message{{Role: "user", Content: "test"}}, nil, 0, "sess-1")
require.NoError(t, err)
require.Len(t, mock.opts, 1)
assert.Equal(t, types.DefaultAgentMaxCompletionTokens, mock.opts[0].MaxTokens)
assert.Equal(t, types.DefaultAgentMaxCompletionTokens, mock.opts[0].MaxCompletionTokens)
})
+46 -13
View File
@@ -11,22 +11,54 @@ import (
// DefaultMaxContextTokens is the default context window budget for agent conversations (200k).
const DefaultMaxContextTokens = 200000
// DefaultAgentMaxCompletionTokens is the per-round completion budget for
// ReAct / tool-calling agents. When MaxTokens is omitted, OpenAI-compatible
// providers (and many Chinese gateways) default to 4096 — large tool-call JSON
// such as write_sandbox_file is then truncated mid-stream (finish_reason=length),
// which surfaces as missing required arguments. 32768 matches wiki ingest (#2604).
const DefaultAgentMaxCompletionTokens = 32768
// DefaultSmartReasoningMaxCompletionTokens is the per-round budget when the
// agent cannot emit write_sandbox_file / edit_sandbox_file. 4096 matches
// typical OpenAI-compatible provider defaults and is enough for ordinary
// tool-call JSON.
const DefaultSmartReasoningMaxCompletionTokens = 4096
// DefaultAgentMaxCompletionTokens is the per-round budget when the agent
// can write or edit sandbox files. Those tool calls carry the file body in
// JSON; a 4096 cap truncates mid-stream (finish_reason=length).
const DefaultAgentMaxCompletionTokens = 24576
// DefaultQuickAnswerMaxCompletionTokens is the RAG answer budget.
const DefaultQuickAnswerMaxCompletionTokens = 2048
// NeedsSandboxWriteCompletionBudget reports whether this agent may register
// write_sandbox_file / edit_sandbox_file. Those tools follow the bound
// sandbox, not the allowed_tools checklist.
func NeedsSandboxWriteCompletionBudget(sandboxConfigID string) bool {
return strings.TrimSpace(sandboxConfigID) != ""
}
// DefaultMaxCompletionTokens is the unset-config default for one agent:
// quick-answer 2048, smart-reasoning 4096, smart-reasoning with a sandbox 24576.
func DefaultMaxCompletionTokens(agentMode, sandboxConfigID string) int {
if agentMode == AgentModeSmartReasoning {
if NeedsSandboxWriteCompletionBudget(sandboxConfigID) {
return DefaultAgentMaxCompletionTokens
}
return DefaultSmartReasoningMaxCompletionTokens
}
return DefaultQuickAnswerMaxCompletionTokens
}
// AgentRoundMaxCompletionTokens returns the completion-token budget for one
// ReAct LLM round. Zero (unset) uses DefaultAgentMaxCompletionTokens. An
// explicit configured value is honored as-is so a user who capped a small
// local model at 4096 is not silently sent 32768.
// ReAct LLM round when no sandbox is bound. Zero (unset) uses
// DefaultSmartReasoningMaxCompletionTokens. An explicit configured value is
// honored as-is.
func AgentRoundMaxCompletionTokens(configured int) int {
if configured <= 0 {
return DefaultAgentMaxCompletionTokens
return AgentRoundMaxCompletionTokensFor(configured, "")
}
// AgentRoundMaxCompletionTokensFor is AgentRoundMaxCompletionTokens with
// the agent's sandbox: unset + sandbox uses the large write-file budget.
func AgentRoundMaxCompletionTokensFor(configured int, sandboxConfigID string) int {
if configured > 0 {
return configured
}
return configured
return DefaultMaxCompletionTokens(AgentModeSmartReasoning, sandboxConfigID)
}
// AgentConfig represents the full agent configuration (used at tenant level and runtime)
@@ -90,7 +122,8 @@ type AgentConfig struct {
LLMCallTimeout int `json:"llm_call_timeout,omitempty"`
// Maximum completion tokens for each ReAct LLM round. Zero means
// DefaultAgentMaxCompletionTokens. Explicit values are sent as-is.
// DefaultMaxCompletionTokens for this agent's mode and sandbox.
// Explicit values are sent as-is.
MaxCompletionTokens int `json:"max_completion_tokens,omitempty"`
// Maximum character length for tool output (default: 16000).
+8 -2
View File
@@ -77,8 +77,11 @@ func TestUseCustomSystemPromptFlag(t *testing.T) {
}
func TestAgentRoundMaxCompletionTokens(t *testing.T) {
if got := AgentRoundMaxCompletionTokens(0); got != DefaultAgentMaxCompletionTokens {
t.Fatalf("zero configured = %d, want %d", got, DefaultAgentMaxCompletionTokens)
if got := AgentRoundMaxCompletionTokens(0); got != DefaultSmartReasoningMaxCompletionTokens {
t.Fatalf("zero configured = %d, want %d", got, DefaultSmartReasoningMaxCompletionTokens)
}
if got := AgentRoundMaxCompletionTokensFor(0, "cfg-a"); got != DefaultAgentMaxCompletionTokens {
t.Fatalf("unset with sandbox = %d, want %d", got, DefaultAgentMaxCompletionTokens)
}
if got := AgentRoundMaxCompletionTokens(2048); got != 2048 {
t.Fatalf("explicit 2048 = %d, want 2048", got)
@@ -86,6 +89,9 @@ func TestAgentRoundMaxCompletionTokens(t *testing.T) {
if got := AgentRoundMaxCompletionTokens(4096); got != 4096 {
t.Fatalf("explicit 4096 = %d, want 4096", got)
}
if got := AgentRoundMaxCompletionTokensFor(4096, "cfg-a"); got != 4096 {
t.Fatalf("explicit 4096 with sandbox must stay 4096, got %d", got)
}
if got := AgentRoundMaxCompletionTokens(64000); got != 64000 {
t.Fatalf("explicit high value = %d, want 64000", got)
}
+4 -8
View File
@@ -125,7 +125,7 @@ type CustomAgentConfig struct {
Temperature float64 `yaml:"temperature" json:"temperature"`
// Maximum completion tokens. Quick-answer uses this for the RAG answer.
// Smart-reasoning ReAct rounds send this value as-is (zero becomes
// DefaultAgentMaxCompletionTokens at call time).
// DefaultMaxCompletionTokens at call time: 4096, or 24576 with a sandbox).
MaxCompletionTokens int `yaml:"max_completion_tokens" json:"max_completion_tokens"`
// Whether to enable thinking mode (for models that support extended thinking)
Thinking *bool `yaml:"thinking" json:"thinking"`
@@ -525,13 +525,9 @@ func (a *CustomAgent) EnsureDefaults() {
if a.Config.FallbackStrategy == "" {
a.Config.FallbackStrategy = "model"
}
if a.Config.MaxCompletionTokens == 0 {
if a.Config.AgentMode == AgentModeSmartReasoning {
a.Config.MaxCompletionTokens = DefaultAgentMaxCompletionTokens
} else {
a.Config.MaxCompletionTokens = 2048
}
}
// MaxCompletionTokens 0 means "use DefaultMaxCompletionTokens at call
// time". Do not materialize a number here — that would make the editor
// treat a chosen default as a custom cap.
// Agent mode should always enable multi-turn conversation
if a.Config.AgentMode == AgentModeSmartReasoning {
a.Config.MultiTurnEnabled = true
+6 -5
View File
@@ -43,15 +43,16 @@ func TestEnsureDefaults_ThinkingPreservesTrue(t *testing.T) {
func TestEnsureDefaults_MaxCompletionTokensByMode(t *testing.T) {
qa := &CustomAgent{Config: CustomAgentConfig{AgentMode: AgentModeQuickAnswer}}
qa.EnsureDefaults()
if qa.Config.MaxCompletionTokens != 2048 {
t.Fatalf("quick-answer default MaxCompletionTokens = %d, want 2048", qa.Config.MaxCompletionTokens)
if qa.Config.MaxCompletionTokens != 0 {
t.Fatalf("unset max_completion_tokens must stay 0 (follow default at call time), got %d",
qa.Config.MaxCompletionTokens)
}
sr := &CustomAgent{Config: CustomAgentConfig{AgentMode: AgentModeSmartReasoning}}
sr.EnsureDefaults()
if sr.Config.MaxCompletionTokens != DefaultAgentMaxCompletionTokens {
t.Fatalf("smart-reasoning default MaxCompletionTokens = %d, want %d",
sr.Config.MaxCompletionTokens, DefaultAgentMaxCompletionTokens)
if sr.Config.MaxCompletionTokens != 0 {
t.Fatalf("unset smart-reasoning max_completion_tokens must stay 0, got %d",
sr.Config.MaxCompletionTokens)
}
explicit := &CustomAgent{Config: CustomAgentConfig{