import type { Context } from "hono" import consola from "consola" import { streamSSE } from "hono/streaming" import { awaitApproval } from "~/lib/approval" import { checkRateLimit } from "~/lib/rate-limit" import { state } from "~/lib/state" import { createChatCompletions, type ChatCompletionChunk, type ChatCompletionResponse, } from "~/services/copilot/create-chat-completions" import { type AnthropicMessagesPayload, type AnthropicStreamState, } from "./anthropic-types" import { translateToAnthropic, translateToOpenAI, } from "./non-stream-translation" import { translateChunkToAnthropicEvents } from "./stream-translation" export async function handleCompletion(c: Context) { await checkRateLimit(state) const anthropicPayload = await c.req.json() consola.debug("Anthropic request payload:", JSON.stringify(anthropicPayload)) const openAIPayload = translateToOpenAI(anthropicPayload) consola.debug( "Translated OpenAI request payload:", JSON.stringify(openAIPayload), ) if (state.manualApprove) { await awaitApproval() } const response = await createChatCompletions(openAIPayload) if (isNonStreaming(response)) { consola.debug( "Non-streaming response from Copilot:", JSON.stringify(response).slice(-400), ) const anthropicResponse = translateToAnthropic(response) consola.debug( "Translated Anthropic response:", JSON.stringify(anthropicResponse), ) return c.json(anthropicResponse) } consola.debug("Streaming response from Copilot") return streamSSE(c, async (stream) => { const streamState: AnthropicStreamState = { messageStartSent: false, contentBlockIndex: 0, contentBlockOpen: false, toolCalls: {}, } for await (const rawEvent of response) { consola.debug("Copilot raw stream event:", JSON.stringify(rawEvent)) if (rawEvent.data === "[DONE]") { break } if (!rawEvent.data) { continue } const chunk = JSON.parse(rawEvent.data) as ChatCompletionChunk const events = translateChunkToAnthropicEvents(chunk, streamState) for (const event of events) { consola.debug("Translated Anthropic event:", JSON.stringify(event)) await stream.writeSSE({ event: event.type, data: JSON.stringify(event), }) } } }) } const isNonStreaming = ( response: Awaited>, ): response is ChatCompletionResponse => Object.hasOwn(response, "choices")