import { describe, test, expect } from "bun:test" import { z } from "zod" import type { ChatCompletionChunk, ChatCompletionResponse, } from "~/services/copilot/create-chat-completions" import { type AnthropicStreamState } from "~/routes/messages/anthropic-types" import { translateToAnthropic } from "~/routes/messages/non-stream-translation" import { translateChunkToAnthropicEvents } from "~/routes/messages/stream-translation" const anthropicUsageSchema = z.object({ input_tokens: z.number().int(), output_tokens: z.number().int(), }) const anthropicContentBlockTextSchema = z.object({ type: z.literal("text"), text: z.string(), }) const anthropicContentBlockToolUseSchema = z.object({ type: z.literal("tool_use"), id: z.string(), name: z.string(), input: z.record(z.string(), z.any()), }) const anthropicMessageResponseSchema = z.object({ id: z.string(), type: z.literal("message"), role: z.literal("assistant"), content: z.array( z.union([ anthropicContentBlockTextSchema, anthropicContentBlockToolUseSchema, ]), ), model: z.string(), stop_reason: z.enum(["end_turn", "max_tokens", "stop_sequence", "tool_use"]), stop_sequence: z.string().nullable(), usage: anthropicUsageSchema, }) /** * Validates if a response payload conforms to the Anthropic Message shape. * @param payload The response payload to validate. * @returns True if the payload is valid, false otherwise. */ function isValidAnthropicResponse(payload: unknown): boolean { return anthropicMessageResponseSchema.safeParse(payload).success } const anthropicStreamEventSchema = z.looseObject({ type: z.enum([ "message_start", "content_block_start", "content_block_delta", "content_block_stop", "message_delta", "message_stop", ]), }) function isValidAnthropicStreamEvent(payload: unknown): boolean { return anthropicStreamEventSchema.safeParse(payload).success } describe("OpenAI to Anthropic Non-Streaming Response Translation", () => { test("should translate a simple text response correctly", () => { const openAIResponse: ChatCompletionResponse = { id: "chatcmpl-123", object: "chat.completion", created: 1677652288, model: "gpt-4o-2024-05-13", choices: [ { index: 0, message: { role: "assistant", content: "Hello! How can I help you today?", }, finish_reason: "stop", logprobs: null, }, ], usage: { prompt_tokens: 9, completion_tokens: 12, total_tokens: 21, }, } const anthropicResponse = translateToAnthropic(openAIResponse) expect(isValidAnthropicResponse(anthropicResponse)).toBe(true) expect(anthropicResponse.id).toBe("chatcmpl-123") expect(anthropicResponse.stop_reason).toBe("end_turn") expect(anthropicResponse.usage.input_tokens).toBe(9) expect(anthropicResponse.content[0].type).toBe("text") if (anthropicResponse.content[0].type === "text") { expect(anthropicResponse.content[0].text).toBe( "Hello! How can I help you today?", ) } else { throw new Error("Expected text block") } }) test("should translate a response with tool calls", () => { const openAIResponse: ChatCompletionResponse = { id: "chatcmpl-456", object: "chat.completion", created: 1677652288, model: "gpt-4o-2024-05-13", choices: [ { index: 0, message: { role: "assistant", content: null, tool_calls: [ { id: "call_abc", type: "function", function: { name: "get_current_weather", arguments: '{"location": "Boston, MA"}', }, }, ], }, finish_reason: "tool_calls", logprobs: null, }, ], usage: { prompt_tokens: 30, completion_tokens: 20, total_tokens: 50, }, } const anthropicResponse = translateToAnthropic(openAIResponse) expect(isValidAnthropicResponse(anthropicResponse)).toBe(true) expect(anthropicResponse.stop_reason).toBe("tool_use") expect(anthropicResponse.content[0].type).toBe("tool_use") if (anthropicResponse.content[0].type === "tool_use") { expect(anthropicResponse.content[0].id).toBe("call_abc") expect(anthropicResponse.content[0].name).toBe("get_current_weather") expect(anthropicResponse.content[0].input).toEqual({ location: "Boston, MA", }) } else { throw new Error("Expected tool_use block") } }) test("should translate a response stopped due to length", () => { const openAIResponse: ChatCompletionResponse = { id: "chatcmpl-789", object: "chat.completion", created: 1677652288, model: "gpt-4o-2024-05-13", choices: [ { index: 0, message: { role: "assistant", content: "This is a very long response that was cut off...", }, finish_reason: "length", logprobs: null, }, ], usage: { prompt_tokens: 10, completion_tokens: 2048, total_tokens: 2058, }, } const anthropicResponse = translateToAnthropic(openAIResponse) expect(isValidAnthropicResponse(anthropicResponse)).toBe(true) expect(anthropicResponse.stop_reason).toBe("max_tokens") }) }) describe("OpenAI to Anthropic Streaming Response Translation", () => { test("should translate a simple text stream correctly", () => { const openAIStream: Array = [ { id: "cmpl-1", object: "chat.completion.chunk", created: 1677652288, model: "gpt-4o-2024-05-13", choices: [ { index: 0, delta: { role: "assistant" }, finish_reason: null, logprobs: null, }, ], }, { id: "cmpl-1", object: "chat.completion.chunk", created: 1677652288, model: "gpt-4o-2024-05-13", choices: [ { index: 0, delta: { content: "Hello" }, finish_reason: null, logprobs: null, }, ], }, { id: "cmpl-1", object: "chat.completion.chunk", created: 1677652288, model: "gpt-4o-2024-05-13", choices: [ { index: 0, delta: { content: " there" }, finish_reason: null, logprobs: null, }, ], }, { id: "cmpl-1", object: "chat.completion.chunk", created: 1677652288, model: "gpt-4o-2024-05-13", choices: [ { index: 0, delta: {}, finish_reason: "stop", logprobs: null }, ], }, ] const streamState: AnthropicStreamState = { messageStartSent: false, contentBlockIndex: 0, contentBlockOpen: false, toolCalls: {}, } const translatedStream = openAIStream.flatMap((chunk) => translateChunkToAnthropicEvents(chunk, streamState), ) for (const event of translatedStream) { expect(isValidAnthropicStreamEvent(event)).toBe(true) } }) test("should translate a stream with tool calls", () => { const openAIStream: Array = [ { id: "cmpl-2", object: "chat.completion.chunk", created: 1677652288, model: "gpt-4o-2024-05-13", choices: [ { index: 0, delta: { role: "assistant" }, finish_reason: null, logprobs: null, }, ], }, { id: "cmpl-2", object: "chat.completion.chunk", created: 1677652288, model: "gpt-4o-2024-05-13", choices: [ { index: 0, delta: { tool_calls: [ { index: 0, id: "call_xyz", type: "function", function: { name: "get_weather", arguments: "" }, }, ], }, finish_reason: null, logprobs: null, }, ], }, { id: "cmpl-2", object: "chat.completion.chunk", created: 1677652288, model: "gpt-4o-2024-05-13", choices: [ { index: 0, delta: { tool_calls: [{ index: 0, function: { arguments: '{"loc' } }], }, finish_reason: null, logprobs: null, }, ], }, { id: "cmpl-2", object: "chat.completion.chunk", created: 1677652288, model: "gpt-4o-2024-05-13", choices: [ { index: 0, delta: { tool_calls: [ { index: 0, function: { arguments: 'ation": "Paris"}' } }, ], }, finish_reason: null, logprobs: null, }, ], }, { id: "cmpl-2", object: "chat.completion.chunk", created: 1677652288, model: "gpt-4o-2024-05-13", choices: [ { index: 0, delta: {}, finish_reason: "tool_calls", logprobs: null }, ], }, ] // Streaming translation requires state const streamState: AnthropicStreamState = { messageStartSent: false, contentBlockIndex: 0, contentBlockOpen: false, toolCalls: {}, } const translatedStream = openAIStream.flatMap((chunk) => translateChunkToAnthropicEvents(chunk, streamState), ) // These tests will fail until the stub is implemented for (const event of translatedStream) { expect(isValidAnthropicStreamEvent(event)).toBe(true) } }) })