test(ai): Add comprehensive E2E tests for all AI providers

- Add multi-turn test to verify thinking and tool calling work together
- Test thinkingSignature handling for proper multi-turn context
- Fix Gemini provider to generate base64 thinkingSignature when needed
- Handle multiple rounds of tool calls in tests (Gemini behavior)
- Make thinking tests more robust for model-dependent behavior
- All 18 tests passing across 4 providers
This commit is contained in:
Mario Zechner 2025-08-25 15:54:26 +02:00
parent 289e60ab88
commit 7a6852081d
7 changed files with 463 additions and 88 deletions

View file

@ -27,6 +27,7 @@ export interface AnthropicLLMOptions extends LLMOptions {
export class AnthropicLLM implements LLM<AnthropicLLMOptions> {
private client: Anthropic;
private model: string;
private isOAuthToken: boolean = false;
constructor(model: string, apiKey?: string, baseUrl?: string) {
if (!apiKey) {
@ -45,8 +46,10 @@ export class AnthropicLLM implements LLM<AnthropicLLMOptions> {
process.env.ANTHROPIC_API_KEY = undefined;
this.client = new Anthropic({ apiKey: null, authToken: apiKey, baseURL: baseUrl, defaultHeaders });
this.isOAuthToken = true;
} else {
this.client = new Anthropic({ apiKey, baseURL: baseUrl });
this.isOAuthToken = false;
}
this.model = model;
}
@ -62,7 +65,8 @@ export class AnthropicLLM implements LLM<AnthropicLLMOptions> {
stream: true,
};
if (context.systemPrompt) {
// For OAuth tokens, we MUST include Claude Code identity
if (this.isOAuthToken) {
params.system = [
{
type: "text",
@ -71,14 +75,18 @@ export class AnthropicLLM implements LLM<AnthropicLLMOptions> {
type: "ephemeral",
},
},
{
];
if (context.systemPrompt) {
params.system.push({
type: "text",
text: context.systemPrompt,
cache_control: {
type: "ephemeral",
},
},
];
});
}
} else if (context.systemPrompt) {
params.system = context.systemPrompt;
}
if (options?.temperature !== undefined) {
@ -128,9 +136,11 @@ export class AnthropicLLM implements LLM<AnthropicLLMOptions> {
if (event.type === "content_block_delta") {
if (event.delta.type === "text_delta") {
options?.onText?.(event.delta.text, false);
blockType = "text"; // Ensure block type is set
}
if (event.delta.type === "thinking_delta") {
options?.onThinking?.(event.delta.thinking, false);
blockType = "thinking"; // Ensure block type is set
}
}
if (event.type === "content_block_stop") {