{"version":3,"file":"openai-adapter.cjs","names":["openai","getSdkClientOptions","Openai","convertActionInputToOpenAITool","convertMessageToOpenAIMessage","limitMessagesToTokenCount","convertServiceAdapterError"],"sources":["../../../src/service-adapters/openai/openai-adapter.ts"],"sourcesContent":["/**\n * Copilot Runtime adapter for OpenAI.\n *\n * ## Example\n *\n * ```ts\n * import { CopilotRuntime, OpenAIAdapter } from \"@copilotkit/runtime\";\n * import OpenAI from \"openai\";\n *\n * const copilotKit = new CopilotRuntime();\n *\n * const openai = new OpenAI({\n *   organization: \"<your-organization-id>\", // optional\n *   apiKey: \"<your-api-key>\",\n * });\n *\n * return new OpenAIAdapter({ openai });\n * ```\n *\n * ## Example with Azure OpenAI\n *\n * ```ts\n * import { CopilotRuntime, OpenAIAdapter } from \"@copilotkit/runtime\";\n * import OpenAI from \"openai\";\n *\n * // The name of your Azure OpenAI Instance.\n * // https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource\n * const instance = \"<your instance name>\";\n *\n * // Corresponds to your Model deployment within your OpenAI resource, e.g. my-gpt35-16k-deployment\n * // Navigate to the Azure OpenAI Studio to deploy a model.\n * const model = \"<your model>\";\n *\n * const apiKey = process.env[\"AZURE_OPENAI_API_KEY\"];\n * if (!apiKey) {\n *   throw new Error(\"The AZURE_OPENAI_API_KEY environment variable is missing or empty.\");\n * }\n *\n * const copilotKit = new CopilotRuntime();\n *\n * const openai = new OpenAI({\n *   apiKey,\n *   baseURL: `https://${instance}.openai.azure.com/openai/deployments/${model}`,\n *   defaultQuery: { \"api-version\": \"2024-04-01-preview\" },\n *   defaultHeaders: { \"api-key\": apiKey },\n * });\n *\n * return new OpenAIAdapter({ openai });\n * ```\n */\nimport type { LanguageModel } from \"ai\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\nimport type OpenAI from \"openai\";\nimport Openai from \"openai\";\nimport {\n  CopilotServiceAdapter,\n  CopilotRuntimeChatCompletionRequest,\n  CopilotRuntimeChatCompletionResponse,\n} from \"../service-adapter\";\nimport {\n  convertActionInputToOpenAITool,\n  convertMessageToOpenAIMessage,\n  limitMessagesToTokenCount,\n} from \"./utils\";\nimport { randomUUID } from \"@copilotkit/shared\";\nimport { convertServiceAdapterError, getSdkClientOptions } from \"../shared\";\n\nconst DEFAULT_MODEL = \"gpt-4o\";\n\nexport interface OpenAIAdapterParams {\n  /**\n   * An optional OpenAI instance to use.  If not provided, a new instance will be\n   * created.\n   */\n  openai?: OpenAI;\n\n  /**\n   * The model to use.\n   */\n  model?: string;\n\n  /**\n   * Whether to disable parallel tool calls.\n   * You can disable parallel tool calls to force the model to execute tool calls sequentially.\n   * This is useful if you want to execute tool calls in a specific order so that the state changes\n   * introduced by one tool call are visible to the next tool call. (i.e. new actions or readables)\n   *\n   * @default false\n   */\n  disableParallelToolCalls?: boolean;\n\n  /**\n   * Whether to keep the role in system messages as \"System\".\n   * By default, it is converted to \"developer\", which is used by newer OpenAI models\n   *\n   * @default false\n   */\n  keepSystemRole?: boolean;\n}\n\nexport class OpenAIAdapter implements CopilotServiceAdapter {\n  public model: string = DEFAULT_MODEL;\n  public provider = \"openai\";\n\n  private disableParallelToolCalls: boolean = false;\n  private _openai: OpenAI;\n  private keepSystemRole: boolean = false;\n\n  public get openai(): OpenAI {\n    return this._openai;\n  }\n  public get name() {\n    return \"OpenAIAdapter\";\n  }\n\n  constructor(params?: OpenAIAdapterParams) {\n    if (params?.openai) {\n      this._openai = params.openai;\n    }\n    // If no instance provided, we'll lazy-load in ensureOpenAI()\n\n    if (params?.model) {\n      this.model = params.model;\n    }\n    this.disableParallelToolCalls = params?.disableParallelToolCalls || false;\n    this.keepSystemRole = params?.keepSystemRole ?? false;\n  }\n\n  getLanguageModel(): LanguageModel {\n    const openai = this.ensureOpenAI();\n    const options = getSdkClientOptions(openai);\n    const provider = createOpenAI({\n      baseURL: openai.baseURL,\n      apiKey: openai.apiKey,\n      organization: openai.organization ?? undefined,\n      project: openai.project ?? undefined,\n      headers: options.defaultHeaders,\n      fetch: options.fetch,\n    });\n    return provider(this.model);\n  }\n\n  private ensureOpenAI(): OpenAI {\n    if (!this._openai) {\n      this._openai = new Openai();\n    }\n    return this._openai;\n  }\n\n  async process(\n    request: CopilotRuntimeChatCompletionRequest,\n  ): Promise<CopilotRuntimeChatCompletionResponse> {\n    const {\n      threadId: threadIdFromRequest,\n      model = this.model,\n      messages,\n      actions,\n      eventSource,\n      forwardedParameters,\n    } = request;\n    const tools = actions.map(convertActionInputToOpenAITool);\n    const threadId = threadIdFromRequest ?? randomUUID();\n\n    // ALLOWLIST APPROACH: Only include tool_result messages that correspond to valid tool_calls\n    // Step 1: Extract valid tool_call IDs\n    const validToolUseIds = new Set<string>();\n\n    for (const message of messages) {\n      if (message.isActionExecutionMessage()) {\n        validToolUseIds.add(message.id);\n      }\n    }\n\n    // Step 2: Filter messages, keeping only those with valid tool_call IDs\n    const filteredMessages = messages.filter((message) => {\n      if (message.isResultMessage()) {\n        // Skip if there's no corresponding tool_call\n        if (!validToolUseIds.has(message.actionExecutionId)) {\n          return false;\n        }\n\n        // Remove this ID from valid IDs so we don't process duplicates\n        validToolUseIds.delete(message.actionExecutionId);\n        return true;\n      }\n\n      // Keep all non-tool-result messages\n      return true;\n    });\n\n    let openaiMessages = filteredMessages.map((m) =>\n      convertMessageToOpenAIMessage(m, { keepSystemRole: this.keepSystemRole }),\n    );\n    openaiMessages = limitMessagesToTokenCount(openaiMessages, tools, model);\n\n    let toolChoice: any = forwardedParameters?.toolChoice;\n    if (forwardedParameters?.toolChoice === \"function\") {\n      toolChoice = {\n        type: \"function\",\n        function: { name: forwardedParameters.toolChoiceFunctionName },\n      };\n    }\n\n    try {\n      const openai = this.ensureOpenAI();\n      const stream = openai.beta.chat.completions.stream({\n        model: model,\n        stream: true,\n        messages: openaiMessages,\n        ...(tools.length > 0 && { tools }),\n        ...(forwardedParameters?.maxTokens && {\n          max_completion_tokens: forwardedParameters.maxTokens,\n        }),\n        ...(forwardedParameters?.stop && { stop: forwardedParameters.stop }),\n        ...(toolChoice && { tool_choice: toolChoice }),\n        ...(this.disableParallelToolCalls && { parallel_tool_calls: false }),\n        ...(forwardedParameters?.temperature && {\n          temperature: forwardedParameters.temperature,\n        }),\n      });\n\n      eventSource.stream(async (eventStream$) => {\n        let mode: \"function\" | \"message\" | null = null;\n        let currentMessageId: string;\n        let currentToolCallId: string;\n\n        try {\n          for await (const chunk of stream) {\n            if (chunk.choices.length === 0) {\n              continue;\n            }\n\n            const toolCall = chunk.choices[0].delta.tool_calls?.[0];\n            const content = chunk.choices[0].delta.content;\n\n            // When switching from message to function or vice versa,\n            // send the respective end event.\n            // If toolCall?.id is defined, it means a new tool call starts.\n            if (mode === \"message\" && toolCall?.id) {\n              mode = null;\n              eventStream$.sendTextMessageEnd({ messageId: currentMessageId });\n            } else if (\n              mode === \"function\" &&\n              (toolCall === undefined || toolCall?.id)\n            ) {\n              mode = null;\n              eventStream$.sendActionExecutionEnd({\n                actionExecutionId: currentToolCallId,\n              });\n            }\n\n            // If we send a new message type, send the appropriate start event.\n            if (mode === null) {\n              if (toolCall?.id) {\n                mode = \"function\";\n                currentToolCallId = toolCall!.id;\n                eventStream$.sendActionExecutionStart({\n                  actionExecutionId: currentToolCallId,\n                  parentMessageId: chunk.id,\n                  actionName: toolCall!.function!.name,\n                });\n              } else if (content) {\n                mode = \"message\";\n                currentMessageId = chunk.id;\n                eventStream$.sendTextMessageStart({\n                  messageId: currentMessageId,\n                });\n              }\n            }\n\n            // send the content events\n            if (mode === \"message\" && content) {\n              eventStream$.sendTextMessageContent({\n                messageId: currentMessageId,\n                content: content,\n              });\n            } else if (mode === \"function\" && toolCall?.function?.arguments) {\n              eventStream$.sendActionExecutionArgs({\n                actionExecutionId: currentToolCallId,\n                args: toolCall.function.arguments,\n              });\n            }\n          }\n\n          // send the end events\n          if (mode === \"message\") {\n            eventStream$.sendTextMessageEnd({ messageId: currentMessageId });\n          } else if (mode === \"function\") {\n            eventStream$.sendActionExecutionEnd({\n              actionExecutionId: currentToolCallId,\n            });\n          }\n        } catch (error) {\n          console.error(\"[OpenAI] Error during API call:\", error);\n          throw convertServiceAdapterError(error, \"OpenAI\");\n        }\n\n        eventStream$.complete();\n      });\n    } catch (error) {\n      console.error(\"[OpenAI] Error during API call:\", error);\n      throw convertServiceAdapterError(error, \"OpenAI\");\n    }\n\n    return {\n      threadId,\n    };\n  }\n}\n"],"mappings":"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"}