/** * v0.8.0 P0-3: 思考参数 × 模型能力 门控矩阵。 * * 根因回顾:MODEL_INFO 标注 supportsThinking:false 的模型(如 * deepseek-v4-flash-vision-exp)此前仍被发送 thinking 参数 —— 思考耗尽输出 * 预算(8192 上限)导致 finish_reason=length 空回复、会话静默停止。 * 本文件钉住四家 Provider 的能力门控行为与输出预算告警前置条件。 */ 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 thinking capability gate', () => { it('DeepSeek: vision-exp (supportsThinking:false) → thinking disabled, no reasoning_effort, max_tokens clamped to 8192', 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: 'disabled' }); expect(body.reasoning_effort).toBeUndefined(); expect(body.max_tokens).toBe(8192); }); it('DeepSeek: pro (supportsThinking:true) → thinking enabled + reasoning_effort mapped (max→max)', async () => { const adapter = new DeepSeekAdapter({ provider: 'deepseek', baseURL: 'https://api.deepseek.com', apiKey: 'k', defaultModel: 'deepseek-v4-pro', }); const body = asNative(adapter)(makeRequest(), false); expect(body.thinking).toEqual({ type: 'enabled' }); expect(body.reasoning_effort).toBe('max'); }); it('Agnes: supportsThinking:false model → 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 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: false }); } finally { table['agnes-2.0-flash'].supportsThinking = original; } }); it('Agnes: supportsThinking:true model → 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 body = asNative(adapter)(makeRequest(), false); expect(body.chat_template_kwargs).toEqual({ enable_thinking: true }); }); it('MiMo: supportsThinking:false model → thinking disabled + temperature passthrough', 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: 'disabled' }); expect(body.temperature).toBe(0); } finally { table['mimo-v2.5-pro'].supportsThinking = original; } }); it('Ollama: probed no-thinking (cachedThinkingSupport=false) → no think parameter', 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: probe unknown (null) fails open → think parameter present', 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); // effort=max → true }); });