imspsycho's picture
Initial upload from Google Colab
98c9143 verified
Raw
History Blame Contribute Delete
3.21 kB
import type { Context } from "hono"
import { streamSSE, type SSEMessage } from "hono/streaming"
import { awaitApproval } from "~/lib/approval"
import { createHandlerLogger, debugJson, debugJsonTail } from "~/lib/logger"
import { checkRateLimit } from "~/lib/rate-limit"
import { state } from "~/lib/state"
import {
createCopilotTokenUsageRecorder,
normalizeOpenAIUsage,
type UsageTokens,
} from "~/lib/token-usage"
import { generateRequestIdFromPayload, getUUID, isNullish } from "~/lib/utils"
import {
createChatCompletions,
type ChatCompletionChunk,
type ChatCompletionResponse,
type ChatCompletionsPayload,
} from "~/services/copilot/create-chat-completions"
const logger = createHandlerLogger("chat-completions-handler")
export async function handleCompletion(c: Context) {
await checkRateLimit(state)
let payload = await c.req.json<ChatCompletionsPayload>()
debugJsonTail(logger, "Request payload:", { value: payload, tailLength: 400 })
// Find the selected model
const selectedModel = state.models?.data.find(
(model) => model.id === payload.model,
)
if (selectedModel?.id === "gpt-5.4") {
return c.json(
{
error: {
message: "Please use `/v1/responses` or `/v1/messages` API",
type: "invalid_request_error",
},
},
400,
)
}
if (state.manualApprove) await awaitApproval()
if (isNullish(payload.max_tokens)) {
payload = {
...payload,
max_tokens: selectedModel?.capabilities.limits.max_output_tokens,
}
debugJson(logger, "Set max_tokens to:", payload.max_tokens)
}
// not support subagent marker for now , set sessionId = getUUID(requestId)
const requestId = generateRequestIdFromPayload(payload)
logger.debug("Generated request ID:", requestId)
const sessionId = getUUID(requestId)
logger.debug("Extracted session ID:", sessionId)
const recordUsage = createCopilotTokenUsageRecorder({
endpoint: "chat_completions",
fallbackSessionId: sessionId,
model: payload.model,
})
const response = await createChatCompletions(payload, {
requestId,
sessionId,
})
if (isNonStreaming(response)) {
debugJson(logger, "Non-streaming response:", response)
recordUsage(normalizeOpenAIUsage(response.usage))
return c.json(response)
}
logger.debug("Streaming response")
return streamSSE(c, async (stream) => {
let usage: UsageTokens = {}
for await (const chunk of response) {
debugJson(logger, "Streaming chunk:", chunk)
const parsedChunk = parseChatCompletionChunk(chunk)
if (parsedChunk?.usage) {
usage = normalizeOpenAIUsage(parsedChunk.usage)
}
await stream.writeSSE(chunk as SSEMessage)
}
recordUsage(usage)
})
}
const isNonStreaming = (
response: Awaited<ReturnType<typeof createChatCompletions>>,
): response is ChatCompletionResponse => Object.hasOwn(response, "choices")
const parseChatCompletionChunk = (
chunk: unknown,
): ChatCompletionChunk | null => {
const data = (chunk as { data?: string }).data
if (!data || data === "[DONE]") {
return null
}
try {
return JSON.parse(data) as ChatCompletionChunk
} catch {
return null
}
}