diff --git a/scripts/ab-corpus/clients/deepseek.ts b/scripts/ab-corpus/clients/deepseek.ts new file mode 100644 index 000000000..d42380fd4 --- /dev/null +++ b/scripts/ab-corpus/clients/deepseek.ts @@ -0,0 +1,42 @@ +/** + * DeepSeek wrapper. OpenAI-compatible endpoint at api.deepseek.com. + * Key in `DEEPSEEK_API_KEY`. Latest flagship at time of wiring is + * `deepseek-v4-pro`; `deepseek-v4-flash` is the lighter variant; the + * older `deepseek-chat` / `deepseek-reasoner` IDs are deprecated + * (sunset 2026-07-24) but the harness routes them anyway so corpus + * comparisons against pre-deprecation runs still work. + * + * Docs: https://api-docs.deepseek.com/zh-cn/ + */ + +import { callOpenAICompat } from './openai-compat'; + +const DEEPSEEK_BASE_URL = process.env.DEEPSEEK_BASE_URL ?? 'https://api.deepseek.com'; +const DEEPSEEK_API_KEY_ENV = 'DEEPSEEK_API_KEY'; + +export interface CallDeepSeekArgs { + model: string; + system: string; + user: string; + temperature?: number; + maxTokens?: number; +} + +export async function callDeepSeek(args: CallDeepSeekArgs): Promise { + const apiKey = process.env[DEEPSEEK_API_KEY_ENV]; + if (!apiKey) { + throw new Error( + `${DEEPSEEK_API_KEY_ENV} not set — export it before running with --live, or pass --dry-run to use fixtures`, + ); + } + return callOpenAICompat({ + baseURL: DEEPSEEK_BASE_URL, + apiKey, + model: args.model, + system: args.system, + user: args.user, + temperature: args.temperature, + maxTokens: args.maxTokens, + label: 'deepseek', + }); +} diff --git a/scripts/ab-corpus/real-model.ts b/scripts/ab-corpus/real-model.ts index b7af25069..2d63f352e 100644 --- a/scripts/ab-corpus/real-model.ts +++ b/scripts/ab-corpus/real-model.ts @@ -16,6 +16,7 @@ import { callMinimax } from './clients/minimax'; import { callCodex } from './clients/codex-cli'; import { callBailian } from './clients/bailian'; import { callArk } from './clients/ark'; +import { callDeepSeek } from './clients/deepseek'; import { buildSystemPrompt } from './build-prompt'; import type { ModelCall } from './stub-model'; @@ -100,8 +101,20 @@ export async function realModelCall(call: ModelCall): Promise { user, }); } + // DeepSeek direct (api.deepseek.com, OpenAI-compat). Latest flagship + // is `deepseek-v4-pro`; `deepseek-v4-flash` is the lighter variant. + // The pre-2026-07-24 ids `deepseek-chat` / `deepseek-reasoner` are + // deprecated upstream but routed here too so older corpus runs stay + // reproducible until cutoff. + if (/^deepseek/i.test(model)) { + return callDeepSeek({ + model: mapDeepSeekId(model), + system: built.system, + user, + }); + } throw new Error( - `No live adapter for model "${model}". Supported: minimax-* (MINIMAX_API_KEY), gpt-*/o1/o3/o4 (Codex CLI), glm-5.1 / kimi-k2.6 (ARK_CODING_KEY), glm-5 / kimi-k2.5 (DASHSCOPE_BAILIAN_CODING_KEY).`, + `No live adapter for model "${model}". Supported: minimax-* (MINIMAX_API_KEY), gpt-*/o1/o3/o4 (Codex CLI), glm-5.1 / kimi-k2.6 (ARK_CODING_KEY), glm-5 / kimi-k2.5 (DASHSCOPE_BAILIAN_CODING_KEY), deepseek-* (DEEPSEEK_API_KEY).`, ); } @@ -175,3 +188,16 @@ function mapGlmBailianIdInternal(id: string): string { if (lower === 'glm-5' || lower === 'glm-4.7' || lower === 'glm-coding') return 'glm-4.7'; return id; } + +function mapDeepSeekId(id: string): string { + // DeepSeek's current flagship is `deepseek-v4-pro`. Harness aliases + // `deepseek` (bare) and `deepseek-pro` resolve to that. The lighter + // `deepseek-v4-flash` and the deprecated `deepseek-chat` / + // `deepseek-reasoner` ids pass through verbatim — server still + // accepts them until 2026-07-24 sunset per docs. + const lower = id.toLowerCase(); + if (lower === 'deepseek' || lower === 'deepseek-pro' || lower === 'deepseek-v4-pro') { + return 'deepseek-v4-pro'; + } + return id; +}