// Deepseek tests cover provider policy api plugin behavior. import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-types"; import { describe, expect, it } from "vitest"; import { normalizeConfig, resolveThinkingProfile } from "./provider-policy-api.js"; describe("deepseek provider-policy-api", () => { it("advertises max thinking levels for DeepSeek V4 models", () => { const expectedV4Levels = ["off", "minimal", "low", "medium", "high", "xhigh", "max"]; expect( resolveThinkingProfile({ provider: "deepseek", modelId: "deepseek-v4-pro", })?.levels.map((level) => level.id), ).toEqual(expectedV4Levels); expect( resolveThinkingProfile({ provider: "deepseek", modelId: "deepseek-v4-flash", })?.defaultLevel, ).toBe("high"); expect( resolveThinkingProfile({ provider: "deepseek", modelId: "deepseek-chat", }), ).toBe(undefined); expect( resolveThinkingProfile({ provider: "openrouter", modelId: "deepseek-v4-pro", }), ).toBe(null); }); it("hydrates contextWindow and cost from catalog for known models", () => { const providerConfig: ModelProviderConfig = { baseUrl: "https://api.deepseek.com", api: "openai-completions", models: [ { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", reasoning: true, input: ["text"], } as never, ], }; const result = normalizeConfig({ provider: "deepseek", providerConfig }); expect(result).not.toBe(providerConfig); const model = result.models[0]; expect(model.contextWindow).toBe(1_000_000); expect(model.maxTokens).toBe(384_000); expect(model.cost).toEqual({ input: 0.14, output: 0.28, cacheRead: 0.028, cacheWrite: 0, }); }); it("hydrates deepseek-v4-pro with correct metadata", () => { const providerConfig: ModelProviderConfig = { baseUrl: "https://api.deepseek.com", api: "openai-completions", models: [ { id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true, input: ["text"], } as never, ], }; const result = normalizeConfig({ provider: "deepseek", providerConfig }); const model = result.models[0]; expect(model.contextWindow).toBe(1_000_000); expect(model.maxTokens).toBe(384_000); expect(model.cost).toEqual({ input: 1.74, output: 3.48, cacheRead: 0.145, cacheWrite: 0, }); }); it("hydrates deepseek-chat with 131k context", () => { const providerConfig: ModelProviderConfig = { baseUrl: "https://api.deepseek.com", api: "openai-completions", models: [ { id: "deepseek-chat", name: "DeepSeek Chat", reasoning: false, input: ["text"], } as never, ], }; const result = normalizeConfig({ provider: "deepseek", providerConfig }); const model = result.models[0]; expect(model.contextWindow).toBe(131_072); }); it("preserves explicit user contextWindow override", () => { const providerConfig: ModelProviderConfig = { baseUrl: "https://api.deepseek.com", api: "openai-completions", models: [ { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", reasoning: true, input: ["text"], contextWindow: 500_000, } as never, ], }; const result = normalizeConfig({ provider: "deepseek", providerConfig }); const model = result.models[0]; expect(model.contextWindow).toBe(500_000); // cost should still be hydrated since it was missing expect(model.cost).toEqual({ input: 0.14, output: 0.28, cacheRead: 0.028, cacheWrite: 0, }); }); it("preserves explicit user cost override", () => { const userCost = { input: 99, output: 99, cacheRead: 99, cacheWrite: 99 }; const providerConfig: ModelProviderConfig = { baseUrl: "https://api.deepseek.com", api: "openai-completions", models: [ { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", reasoning: true, input: ["text"], cost: userCost, } as never, ], }; const result = normalizeConfig({ provider: "deepseek", providerConfig }); const model = result.models[0]; expect(model.cost).toEqual(userCost); // contextWindow should still be hydrated since it was missing expect(model.contextWindow).toBe(1_000_000); }); it("preserves explicit user maxTokens override", () => { const providerConfig: ModelProviderConfig = { baseUrl: "https://api.deepseek.com", api: "openai-completions", models: [ { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", reasoning: true, input: ["text"], maxTokens: 100_000, } as never, ], }; const result = normalizeConfig({ provider: "deepseek", providerConfig }); const model = result.models[0]; expect(model.maxTokens).toBe(100_000); }); it("returns providerConfig unchanged when all models already have metadata", () => { const providerConfig: ModelProviderConfig = { baseUrl: "https://api.deepseek.com", api: "openai-completions", models: [ { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", reasoning: true, input: ["text"], contextWindow: 1_000_000, maxTokens: 384_000, cost: { input: 0.14, output: 0.28, cacheRead: 0.028, cacheWrite: 0 }, } as never, ], }; const result = normalizeConfig({ provider: "deepseek", providerConfig }); expect(result).toBe(providerConfig); }); it("passes through unknown model ids unchanged", () => { const providerConfig: ModelProviderConfig = { baseUrl: "https://api.deepseek.com", api: "openai-completions", models: [ { id: "deepseek-custom-finetune", name: "Custom Fine-tune", reasoning: false, input: ["text"], } as never, ], }; const result = normalizeConfig({ provider: "deepseek", providerConfig }); expect(result).toBe(providerConfig); }); it("returns providerConfig unchanged when models array is empty", () => { const providerConfig: ModelProviderConfig = { baseUrl: "https://api.deepseek.com", api: "openai-completions", models: [], }; const result = normalizeConfig({ provider: "deepseek", providerConfig }); expect(result).toBe(providerConfig); }); it("hydrates only the models that need it in a mixed list", () => { const providerConfig: ModelProviderConfig = { baseUrl: "https://api.deepseek.com", api: "openai-completions", models: [ { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", reasoning: true, input: ["text"], contextWindow: 1_000_000, maxTokens: 384_000, cost: { input: 0.14, output: 0.28, cacheRead: 0.028, cacheWrite: 0 }, } as never, { id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true, input: ["text"], } as never, ], }; const result = normalizeConfig({ provider: "deepseek", providerConfig }); expect(result).not.toBe(providerConfig); // First model should be unchanged (same reference) expect(result.models[0]).toBe(providerConfig.models[0]); // Second model should be hydrated expect(result.models[1].contextWindow).toBe(1_000_000); expect(result.models[1].cost).toEqual({ input: 1.74, output: 3.48, cacheRead: 0.145, cacheWrite: 0, }); }); });