@copilotkit/runtime
Version:
<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />
1 lines • 11.9 kB
Source Map (JSON)
{"version":3,"file":"utils.mjs","names":[],"sources":["../../../src/service-adapters/openai/utils.ts"],"sourcesContent":["import type OpenAI from \"openai\";\nimport { Message } from \"../../graphql/types/converted\";\nimport { ActionInput } from \"../../graphql/inputs/action.input\";\nimport {\n ChatCompletionAssistantMessageParam,\n ChatCompletionMessageParam,\n ChatCompletionSystemMessageParam,\n ChatCompletionTool,\n ChatCompletionUserMessageParam,\n ChatCompletionDeveloperMessageParam,\n} from \"openai/resources/chat\";\nimport { parseJson } from \"@copilotkit/shared\";\n\n/**\n * OpenAI v4 exposes streaming completions under `beta.chat.completions`.\n * v5 removed `beta.chat` and promoted streaming to `chat.completions`.\n * These interfaces model the v4-specific shape so we can detect and access\n * the beta namespace safely without `as any`.\n */\ninterface OpenAIV4BetaChat {\n chat: {\n completions: OpenAI[\"chat\"][\"completions\"];\n };\n}\n\ninterface OpenAIV4Beta extends OpenAI.Beta {\n chat: OpenAIV4BetaChat[\"chat\"];\n}\n\n/**\n * Type guard: checks whether the OpenAI client has the v4-era `beta.chat`\n * namespace. Returns `false` for v5+ clients where `beta.chat` was removed.\n */\nfunction hasV4BetaChat(beta: OpenAI[\"beta\"] | undefined): beta is OpenAIV4Beta {\n return beta != null && \"chat\" in beta && (beta as OpenAIV4Beta).chat != null;\n}\n\n/**\n * Detects whether the provided OpenAI client is v5+ by checking for the\n * removal of the `beta.chat` namespace (which was promoted to `chat` in v5).\n */\nexport function isOpenAIV5(openai: OpenAI): boolean {\n return !hasV4BetaChat(openai.beta);\n}\n\n/**\n * Returns the chat completions object that supports `.stream()`.\n * In v4 this lives under `openai.beta.chat.completions`;\n * in v5 it was promoted to `openai.chat.completions`.\n */\nexport function getChatCompletionsForStreaming(\n openai: OpenAI,\n): OpenAI[\"chat\"][\"completions\"] {\n if (hasV4BetaChat(openai.beta)) {\n return openai.beta.chat.completions;\n }\n return openai.chat.completions;\n}\n\n/**\n * Retrieves a thread run, handling the v4→v5 API signature change.\n * v4: retrieve(threadId, runId)\n * v5: retrieve(runId, { thread_id: threadId })\n */\nexport async function retrieveThreadRun(\n openai: OpenAI,\n threadId: string,\n runId: string,\n): Promise<OpenAI.Beta.Threads.Runs.Run> {\n if (isOpenAIV5(openai)) {\n // v5 switched to named path params. The type definitions from whichever\n // SDK version is installed won't match both signatures, so we call through\n // a generic function reference. This is the one unavoidable boundary\n // between two incompatible SDK type surfaces.\n const retrieve = openai.beta.threads.runs.retrieve as {\n (...args: unknown[]): Promise<OpenAI.Beta.Threads.Runs.Run>;\n };\n return retrieve(runId, { thread_id: threadId });\n }\n return openai.beta.threads.runs.retrieve(threadId, runId);\n}\n\n/**\n * Submits tool outputs as a stream, handling the v4→v5 API signature change.\n * v4: submitToolOutputsStream(threadId, runId, body)\n * v5: submitToolOutputsStream(runId, { thread_id, ...body })\n */\nexport function submitToolOutputsStream(\n openai: OpenAI,\n threadId: string,\n runId: string,\n body: {\n tool_outputs: Array<{ tool_call_id: string; output: string }>;\n parallel_tool_calls?: false;\n },\n) {\n if (isOpenAIV5(openai)) {\n // Same boundary as retrieveThreadRun — v5 uses named path params.\n const submit = openai.beta.threads.runs.submitToolOutputsStream as {\n (\n ...args: unknown[]\n ): ReturnType<typeof openai.beta.threads.runs.submitToolOutputsStream>;\n };\n return submit(runId, { thread_id: threadId, ...body });\n }\n return openai.beta.threads.runs.submitToolOutputsStream(\n threadId,\n runId,\n body,\n );\n}\n\nexport function limitMessagesToTokenCount(\n messages: any[],\n tools: any[],\n model: string,\n maxTokens?: number,\n): any[] {\n maxTokens ||= maxTokensForOpenAIModel(model);\n\n const result: any[] = [];\n const toolsNumTokens = countToolsTokens(model, tools);\n if (toolsNumTokens > maxTokens) {\n throw new Error(\n `Too many tokens in function definitions: ${toolsNumTokens} > ${maxTokens}`,\n );\n }\n maxTokens -= toolsNumTokens;\n\n for (const message of messages) {\n if ([\"system\", \"developer\"].includes(message.role)) {\n const numTokens = countMessageTokens(model, message);\n maxTokens -= numTokens;\n\n if (maxTokens < 0) {\n throw new Error(\"Not enough tokens for system message.\");\n }\n }\n }\n\n let cutoff: boolean = false;\n\n const reversedMessages = [...messages].toReversed();\n for (const message of reversedMessages) {\n if ([\"system\", \"developer\"].includes(message.role)) {\n result.unshift(message);\n continue;\n } else if (cutoff) {\n continue;\n }\n let numTokens = countMessageTokens(model, message);\n if (maxTokens < numTokens) {\n cutoff = true;\n continue;\n }\n result.unshift(message);\n maxTokens -= numTokens;\n }\n\n return result;\n}\n\nexport function maxTokensForOpenAIModel(model: string): number {\n return maxTokensByModel[model] || DEFAULT_MAX_TOKENS;\n}\n\nconst DEFAULT_MAX_TOKENS = 128000;\n\nconst maxTokensByModel: { [key: string]: number } = {\n // o1\n o1: 200000,\n \"o1-2024-12-17\": 200000,\n \"o1-mini\": 128000,\n \"o1-mini-2024-09-12\": 128000,\n \"o1-preview\": 128000,\n \"o1-preview-2024-09-12\": 128000,\n // o3-mini\n \"o3-mini\": 200000,\n \"o3-mini-2025-01-31\": 200000,\n // GPT-4\n \"gpt-4o\": 128000,\n \"chatgpt-4o-latest\": 128000,\n \"gpt-4o-2024-08-06\": 128000,\n \"gpt-4o-2024-05-13\": 128000,\n \"gpt-4o-mini\": 128000,\n \"gpt-4o-mini-2024-07-18\": 128000,\n \"gpt-4-turbo\": 128000,\n \"gpt-4-turbo-2024-04-09\": 128000,\n \"gpt-4-0125-preview\": 128000,\n \"gpt-4-turbo-preview\": 128000,\n \"gpt-4-1106-preview\": 128000,\n \"gpt-4-vision-preview\": 128000,\n \"gpt-4-1106-vision-preview\": 128000,\n \"gpt-4-32k\": 32768,\n \"gpt-4-32k-0613\": 32768,\n \"gpt-4-32k-0314\": 32768,\n \"gpt-4\": 8192,\n \"gpt-4-0613\": 8192,\n \"gpt-4-0314\": 8192,\n\n // GPT-3.5\n \"gpt-3.5-turbo-0125\": 16385,\n \"gpt-3.5-turbo\": 16385,\n \"gpt-3.5-turbo-1106\": 16385,\n \"gpt-3.5-turbo-instruct\": 4096,\n \"gpt-3.5-turbo-16k\": 16385,\n \"gpt-3.5-turbo-0613\": 4096,\n \"gpt-3.5-turbo-16k-0613\": 16385,\n \"gpt-3.5-turbo-0301\": 4097,\n};\n\nfunction countToolsTokens(model: string, tools: any[]): number {\n if (tools.length === 0) {\n return 0;\n }\n const json = JSON.stringify(tools);\n return countTokens(model, json);\n}\n\nfunction countMessageTokens(model: string, message: any): number {\n return countTokens(model, message.content || \"\");\n}\n\nfunction countTokens(model: string, text: string): number {\n return text.length / 3;\n}\n\nexport function convertActionInputToOpenAITool(\n action: ActionInput,\n): ChatCompletionTool {\n return {\n type: \"function\",\n function: {\n name: action.name,\n description: action.description,\n parameters: parseJson(action.jsonSchema, {}),\n },\n };\n}\n\ntype UsedMessageParams =\n | ChatCompletionUserMessageParam\n | ChatCompletionAssistantMessageParam\n | ChatCompletionDeveloperMessageParam\n | ChatCompletionSystemMessageParam;\nexport function convertMessageToOpenAIMessage(\n message: Message,\n options?: { keepSystemRole: boolean },\n): ChatCompletionMessageParam {\n const { keepSystemRole } = options || { keepSystemRole: false };\n if (message.isTextMessage()) {\n let role = message.role as UsedMessageParams[\"role\"];\n if (message.role === \"system\" && !keepSystemRole) {\n role = \"developer\";\n }\n return {\n role,\n content: message.content,\n } satisfies UsedMessageParams;\n } else if (message.isImageMessage()) {\n return {\n role: \"user\",\n content: [\n {\n type: \"image_url\",\n image_url: {\n url: `data:image/${message.format};base64,${message.bytes}`,\n },\n },\n ],\n } satisfies UsedMessageParams;\n } else if (message.isActionExecutionMessage()) {\n return {\n role: \"assistant\",\n tool_calls: [\n {\n id: message.id,\n type: \"function\",\n function: {\n name: message.name,\n arguments: JSON.stringify(message.arguments),\n },\n },\n ],\n };\n } else if (message.isResultMessage()) {\n return {\n role: \"tool\",\n content: message.result,\n tool_call_id: message.actionExecutionId,\n };\n }\n}\n\nexport function convertSystemMessageToAssistantAPI(\n message: ChatCompletionMessageParam,\n) {\n return {\n ...message,\n ...([\"system\", \"developer\"].includes(message.role) && {\n role: \"assistant\",\n content: \"THE FOLLOWING MESSAGE IS A SYSTEM MESSAGE: \" + message.content,\n }),\n };\n}\n"],"mappings":"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"}