{"version":3,"file":"utils.cjs","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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"}