Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: add Anthropic Claude and Google Gemini provider support
Add handler modules for two new LLM provider APIs, both following the
established pattern from responses.ts: convert inbound request to
ChatCompletionRequest, match fixtures, convert response back to
provider-specific format.

Claude Messages API (/v1/messages):
- Streaming via event: type / data: json SSE format
- Non-streaming JSON responses
- Full message lifecycle: message_start through message_stop
- Tool use with input_json_delta streaming
- msg_ and toolu_ ID prefixes

Google Gemini GenerateContent API:
- /v1beta/models/{model}:generateContent (non-streaming)
- /v1beta/models/{model}:streamGenerateContent (streaming)
- data-only SSE format (no event prefix, no [DONE])
- functionCall/functionResponse round-trips with synthetic IDs
- FUNCTION_CALL finishReason for tool call responses

Also adds generateMessageId() and generateToolUseId() helpers,
server routes for both providers, and comprehensive tests.
  • Loading branch information
jpr5 committed Mar 3, 2026
commit 5f0c18bf35467a334802276bbec359cd641d923e
665 changes: 665 additions & 0 deletions src/__tests__/gemini.test.ts

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions src/__tests__/helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { describe, it, expect } from "vitest";
import {
generateId,
generateToolCallId,
generateMessageId,
generateToolUseId,
isTextResponse,
isToolCallResponse,
isErrorResponse,
Expand Down Expand Up @@ -36,6 +38,32 @@ describe("generateToolCallId", () => {
});
});

describe("generateMessageId", () => {
it("generates message IDs with msg_ prefix", () => {
const id = generateMessageId();
expect(id).toMatch(/^msg_/);
expect(id.length).toBeGreaterThan(5);
});

it("generates unique IDs", () => {
const ids = new Set(Array.from({ length: 100 }, () => generateMessageId()));
expect(ids.size).toBe(100);
});
});

describe("generateToolUseId", () => {
it("generates tool use IDs with toolu_ prefix", () => {
const id = generateToolUseId();
expect(id).toMatch(/^toolu_/);
expect(id.length).toBeGreaterThan(7);
});

it("generates unique IDs", () => {
const ids = new Set(Array.from({ length: 100 }, () => generateToolUseId()));
expect(ids.size).toBe(100);
});
});

describe("type guards", () => {
it("isTextResponse identifies text responses", () => {
expect(isTextResponse({ content: "hello" })).toBe(true);
Expand Down
Loading