/** * v0.8.0 P0-3(修订版): 思考参数**用户意图优先**契约。 * * 修订原因:初版按 MODEL_INFO.supportsThinking 元信息硬门控,但事故复盘证明 * 元信息不可靠 —— deepseek-v4-flash-vision-exp 标注"不支持思考"、实际却产生了 * 8189 token 推理内容。既然引擎已有完整兜底链(最大输出上限配置 → 空响应守卫 * → 降级重试 → OUTPUT_LENGTH_EXCEEDED 明确报错),是否开思考应由**用户决定**, * 适配器层只负责:①如实透传用户配置;②元信息不符时告警不拦截;③预算过小告警。 * * 保留的唯一门控是 Ollama 的 /api/show capabilities 探测 —— 那是服务端实时 * 真值且为硬协议约束(向无思考能力的模型发 think 每次请求 400),属协议 * 正确性而非用户意图覆盖。 */ import { describe, it, expect } from 'vitest'; import { DeepSeekAdapter } from '../deepseek.adapter'; import { AgnesAdapter } from '../agnes-ai.adapter'; import { MimoAdapter } from '../mimo.adapter'; import { OllamaAdapter } from '../ollama.adapter'; import type { MetonaRequest } from '../../types'; const SYSTEM_PROMPT = { roleDefinition: 'test', outputConstraints: '', safetyGuidelines: '', }; function makeRequest(overrides?: Partial): MetonaRequest { return { meta: { sessionId: 's', iteration: 1, requestId: 'r', timestamp: Date.now(), agentVersion: '1.0.0', }, systemPrompt: SYSTEM_PROMPT, messages: [{ role: 'user', content: 'hi', timestamp: Date.now() }], params: { maxTokens: 63488, temperature: 0, stream: true, thinkingEnabled: true, thinkingEffort: 'max', ...overrides, }, }; } function asNative( adapter: unknown, ): (req: MetonaRequest, stream: boolean) => Record { return ( adapter as { toNativeRequest: (r: MetonaRequest, s: boolean) => Record } ).toNativeRequest.bind(adapter); } describe('P0-3 修订: 用户思考意图优先于模型元信息', () => { it('DeepSeek: vision-exp(元信息 false)+ 用户开启思考 → 照发 enabled + reasoning_effort,max_tokens 原样透传(v0.8.1 无钳制)', async () => { const adapter = new DeepSeekAdapter({ provider: 'deepseek', baseURL: 'https://api.deepseek.com', apiKey: 'k', defaultModel: 'deepseek-v4-flash-vision-exp', }); const body = asNative(adapter)(makeRequest(), false); expect(body.thinking).toEqual({ type: 'enabled' }); expect(body.reasoning_effort).toBe('max'); expect(body.max_tokens).toBe(63488); }); it('DeepSeek: 用户关闭思考 → 显式 disabled', async () => { const adapter = new DeepSeekAdapter({ provider: 'deepseek', baseURL: 'https://api.deepseek.com', apiKey: 'k', defaultModel: 'deepseek-v4-flash-vision-exp', }); const body = asNative(adapter)( makeRequest({ thinkingEnabled: false, thinkingEffort: undefined }), false, ); expect(body.thinking).toEqual({ type: 'disabled' }); expect(body.reasoning_effort).toBeUndefined(); }); it('DeepSeek: 未配置 → 显式 disabled(确定性契约,不依赖服务端隐式默认)', async () => { const adapter = new DeepSeekAdapter({ provider: 'deepseek', baseURL: 'https://api.deepseek.com', apiKey: 'k', defaultModel: 'deepseek-v4-pro', }); const body = asNative(adapter)(makeRequest({ thinkingEnabled: undefined }), false); expect(body.thinking).toEqual({ type: 'disabled' }); }); it('Agnes: 元信息 false + 用户开启思考 → enable_thinking:true(元信息不拦截)', async () => { const adapter = new AgnesAdapter({ provider: 'agnes', baseURL: 'https://apihub.agnes-ai.com/v1', apiKey: 'k', defaultModel: 'agnes-2.0-flash', }); const table = AgnesAdapter['MODEL_INFO'] as Record; const original = table['agnes-2.0-flash'].supportsThinking; table['agnes-2.0-flash'].supportsThinking = false; try { const body = asNative(adapter)(makeRequest(), false); expect(body.chat_template_kwargs).toEqual({ enable_thinking: true }); } finally { table['agnes-2.0-flash'].supportsThinking = original; } }); it('Agnes: 用户关闭思考 → enable_thinking:false(对称契约保持)', async () => { const adapter = new AgnesAdapter({ provider: 'agnes', baseURL: 'https://apihub.agnes-ai.com/v1', apiKey: 'k', defaultModel: 'agnes-2.0-flash', }); const body = asNative(adapter)(makeRequest({ thinkingEnabled: false }), false); expect(body.chat_template_kwargs).toEqual({ enable_thinking: false }); }); it('MiMo: 元信息 false + 用户开启思考 → thinking enabled(temperature 不传)', async () => { const adapter = new MimoAdapter({ provider: 'mimo', baseURL: 'https://api.xiaomimimo.com/v1', apiKey: 'k', defaultModel: 'mimo-v2.5-pro', }); const table = MimoAdapter['MODEL_INFO'] as Record; const original = table['mimo-v2.5-pro'].supportsThinking; table['mimo-v2.5-pro'].supportsThinking = false; try { const body = asNative(adapter)(makeRequest(), false); expect(body.thinking).toEqual({ type: 'enabled' }); expect(body.temperature).toBeUndefined(); } finally { table['mimo-v2.5-pro'].supportsThinking = original; } }); it('MiMo: 用户关闭思考 → disabled + temperature/top_p 透传', async () => { const adapter = new MimoAdapter({ provider: 'mimo', baseURL: 'https://api.xiaomimimo.com/v1', apiKey: 'k', defaultModel: 'mimo-v2.5-pro', }); const body = asNative(adapter)(makeRequest({ thinkingEnabled: false }), false); expect(body.thinking).toEqual({ type: 'disabled' }); expect(body.temperature).toBe(0); }); it('MiMo: 未配置(undefined)→ 显式 disabled(v0.8.2 P2-6 与 DeepSeek/Agnes 对齐,不隐式吞用户 temperature)', async () => { const adapter = new MimoAdapter({ provider: 'mimo', baseURL: 'https://api.xiaomimimo.com/v1', apiKey: 'k', defaultModel: 'mimo-v2.5-pro', }); const body = asNative(adapter)(makeRequest({ thinkingEnabled: undefined }), false); expect(body.thinking).toEqual({ type: 'disabled' }); expect(body.temperature).toBe(0); }); it('Ollama: 探测不支持思考(服务端硬约束)→ 不发 think 参数(唯一保留的门控)', async () => { const adapter = new OllamaAdapter({ provider: 'ollama', baseURL: 'http://localhost:11434', apiKey: '', defaultModel: 'qwen3:latest', }); (adapter as unknown as { cachedThinkingSupport: boolean | null }).cachedThinkingSupport = false; const body = await ( adapter as unknown as { toNativeRequest: (r: MetonaRequest) => Promise>; } ).toNativeRequest(makeRequest()); expect(body.think).toBeUndefined(); }); it('Ollama: 探测未知(null)fail-open → think 参数照发', async () => { const adapter = new OllamaAdapter({ provider: 'ollama', baseURL: 'http://localhost:11434', apiKey: '', defaultModel: 'qwen3:latest', }); (adapter as unknown as { cachedThinkingSupport: boolean | null }).cachedThinkingSupport = null; const body = await ( adapter as unknown as { toNativeRequest: (r: MetonaRequest) => Promise>; } ).toNativeRequest(makeRequest()); expect(body.think).toBe(true); }); });