From c29d4bbb868d7c881daef8db306e8c659b7b4e37 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 4 Apr 2026 20:11:20 +0900 Subject: [PATCH] test(providers): add family capability matrix coverage --- src/plugin-sdk/provider-model-shared.test.ts | 139 ++++++++++++++----- src/plugin-sdk/provider-tools.test.ts | 18 ++- 2 files changed, 116 insertions(+), 41 deletions(-) diff --git a/src/plugin-sdk/provider-model-shared.test.ts b/src/plugin-sdk/provider-model-shared.test.ts index 1bc811f2d31..9f7641cbf89 100644 --- a/src/plugin-sdk/provider-model-shared.test.ts +++ b/src/plugin-sdk/provider-model-shared.test.ts @@ -2,41 +2,110 @@ import { describe, expect, it } from "vitest"; import { buildProviderReplayFamilyHooks } from "./provider-model-shared.js"; describe("buildProviderReplayFamilyHooks", () => { - it("maps openai-compatible replay families", () => { - const hooks = buildProviderReplayFamilyHooks({ - family: "openai-compatible", - }); + it("covers the replay family matrix", async () => { + const cases = [ + { + family: "openai-compatible" as const, + ctx: { + provider: "xai", + modelApi: "openai-completions", + modelId: "grok-4", + }, + match: { + sanitizeToolCallIds: true, + applyAssistantFirstOrderingFix: true, + validateGeminiTurns: true, + }, + hasSanitizeReplayHistory: false, + reasoningMode: undefined, + }, + { + family: "anthropic-by-model" as const, + ctx: { + provider: "anthropic-vertex", + modelApi: "anthropic-messages", + modelId: "claude-sonnet-4-6", + }, + match: { + validateAnthropicTurns: true, + dropThinkingBlocks: true, + }, + hasSanitizeReplayHistory: false, + reasoningMode: undefined, + }, + { + family: "google-gemini" as const, + ctx: { + provider: "google", + modelApi: "google-generative-ai", + modelId: "gemini-3.1-pro-preview", + }, + match: { + validateGeminiTurns: true, + allowSyntheticToolResults: true, + }, + hasSanitizeReplayHistory: true, + reasoningMode: "tagged", + }, + { + family: "passthrough-gemini" as const, + ctx: { + provider: "openrouter", + modelApi: "openai-completions", + modelId: "gemini-2.5-pro", + }, + match: { + applyAssistantFirstOrderingFix: false, + validateGeminiTurns: false, + validateAnthropicTurns: false, + sanitizeThoughtSignatures: { + allowBase64Only: true, + includeCamelCase: true, + }, + }, + hasSanitizeReplayHistory: false, + reasoningMode: undefined, + }, + { + family: "hybrid-anthropic-openai" as const, + options: { + anthropicModelDropThinkingBlocks: true, + }, + ctx: { + provider: "minimax", + modelApi: "anthropic-messages", + modelId: "claude-sonnet-4-6", + }, + match: { + validateAnthropicTurns: true, + dropThinkingBlocks: true, + }, + hasSanitizeReplayHistory: false, + reasoningMode: undefined, + }, + ]; - expect( - hooks.buildReplayPolicy?.({ - provider: "xai", - modelApi: "openai-completions", - modelId: "grok-4", - } as never), - ).toMatchObject({ - sanitizeToolCallIds: true, - applyAssistantFirstOrderingFix: true, - validateGeminiTurns: true, - }); + for (const testCase of cases) { + const hooks = buildProviderReplayFamilyHooks( + testCase.options + ? { + family: testCase.family, + ...testCase.options, + } + : { family: testCase.family }, + ); + + expect(hooks.buildReplayPolicy?.(testCase.ctx as never)).toMatchObject(testCase.match); + expect(Boolean(hooks.sanitizeReplayHistory)).toBe(testCase.hasSanitizeReplayHistory); + expect(hooks.resolveReasoningOutputMode?.(testCase.ctx as never)).toBe(testCase.reasoningMode); + } }); - it("maps google-gemini replay families", async () => { + it("keeps google-gemini replay sanitation on the bootstrap path", async () => { const hooks = buildProviderReplayFamilyHooks({ family: "google-gemini", }); - expect(hooks.resolveReasoningOutputMode?.({} as never)).toBe("tagged"); - expect( - hooks.buildReplayPolicy?.({ - provider: "google", - modelApi: "google-generative-ai", - modelId: "gemini-3.1-pro-preview", - } as never), - ).toMatchObject({ - validateGeminiTurns: true, - allowSyntheticToolResults: true, - }); - const sanitized = await hooks.sanitizeReplayHistory?.({ provider: "google", modelApi: "google-generative-ai", @@ -60,21 +129,17 @@ describe("buildProviderReplayFamilyHooks", () => { }); }); - it("maps hybrid anthropic/openai replay families", () => { + it("keeps anthropic-by-model replay family scoped to claude ids", () => { const hooks = buildProviderReplayFamilyHooks({ - family: "hybrid-anthropic-openai", - anthropicModelDropThinkingBlocks: true, + family: "anthropic-by-model", }); expect( hooks.buildReplayPolicy?.({ - provider: "minimax", + provider: "amazon-bedrock", modelApi: "anthropic-messages", - modelId: "claude-sonnet-4-6", + modelId: "amazon.nova-pro-v1", } as never), - ).toMatchObject({ - validateAnthropicTurns: true, - dropThinkingBlocks: true, - }); + ).not.toHaveProperty("dropThinkingBlocks"); }); }); diff --git a/src/plugin-sdk/provider-tools.test.ts b/src/plugin-sdk/provider-tools.test.ts index b877f02933c..3e6762a88e1 100644 --- a/src/plugin-sdk/provider-tools.test.ts +++ b/src/plugin-sdk/provider-tools.test.ts @@ -6,10 +6,20 @@ import { } from "./provider-tools.js"; describe("buildProviderToolCompatFamilyHooks", () => { - it("maps the gemini family to the shared schema helpers", () => { - const hooks = buildProviderToolCompatFamilyHooks("gemini"); + it("covers the tool compat family matrix", () => { + const cases = [ + { + family: "gemini" as const, + normalizeToolSchemas: normalizeGeminiToolSchemas, + inspectToolSchemas: inspectGeminiToolSchemas, + }, + ]; - expect(hooks.normalizeToolSchemas).toBe(normalizeGeminiToolSchemas); - expect(hooks.inspectToolSchemas).toBe(inspectGeminiToolSchemas); + for (const testCase of cases) { + const hooks = buildProviderToolCompatFamilyHooks(testCase.family); + + expect(hooks.normalizeToolSchemas).toBe(testCase.normalizeToolSchemas); + expect(hooks.inspectToolSchemas).toBe(testCase.inspectToolSchemas); + } }); });