mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-05 22:32:12 +00:00
148 lines
4.1 KiB
TypeScript
148 lines
4.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { buildProviderReplayFamilyHooks } from "./provider-model-shared.js";
|
|
|
|
describe("buildProviderReplayFamilyHooks", () => {
|
|
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,
|
|
},
|
|
];
|
|
|
|
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("keeps google-gemini replay sanitation on the bootstrap path", async () => {
|
|
const hooks = buildProviderReplayFamilyHooks({
|
|
family: "google-gemini",
|
|
});
|
|
|
|
const sanitized = await hooks.sanitizeReplayHistory?.({
|
|
provider: "google",
|
|
modelApi: "google-generative-ai",
|
|
modelId: "gemini-3.1-pro-preview",
|
|
sessionId: "session-1",
|
|
messages: [
|
|
{
|
|
role: "assistant",
|
|
content: [{ type: "text", text: "hello" }],
|
|
},
|
|
],
|
|
sessionState: {
|
|
getCustomEntries: () => [],
|
|
appendCustomEntry: () => {},
|
|
},
|
|
} as never);
|
|
|
|
expect(sanitized?.[0]).toMatchObject({
|
|
role: "user",
|
|
content: "(session bootstrap)",
|
|
});
|
|
});
|
|
|
|
it("keeps anthropic-by-model replay family scoped to claude ids", () => {
|
|
const hooks = buildProviderReplayFamilyHooks({
|
|
family: "anthropic-by-model",
|
|
});
|
|
|
|
expect(
|
|
hooks.buildReplayPolicy?.({
|
|
provider: "amazon-bedrock",
|
|
modelApi: "anthropic-messages",
|
|
modelId: "amazon.nova-pro-v1",
|
|
} as never),
|
|
).not.toHaveProperty("dropThinkingBlocks");
|
|
});
|
|
});
|