@copilotkit/runtime 1.52.0 → 1.52.1-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/lib/runtime/copilot-runtime.cjs +4 -1
- package/dist/lib/runtime/copilot-runtime.cjs.map +1 -1
- package/dist/lib/runtime/copilot-runtime.d.cts.map +1 -1
- package/dist/lib/runtime/copilot-runtime.d.mts.map +1 -1
- package/dist/lib/runtime/copilot-runtime.mjs +4 -1
- package/dist/lib/runtime/copilot-runtime.mjs.map +1 -1
- package/dist/package.cjs +4 -1
- package/dist/package.mjs +4 -1
- package/dist/service-adapters/anthropic/anthropic-adapter.cjs +12 -0
- package/dist/service-adapters/anthropic/anthropic-adapter.cjs.map +1 -1
- package/dist/service-adapters/anthropic/anthropic-adapter.d.cts +2 -0
- package/dist/service-adapters/anthropic/anthropic-adapter.d.cts.map +1 -1
- package/dist/service-adapters/anthropic/anthropic-adapter.d.mts +2 -0
- package/dist/service-adapters/anthropic/anthropic-adapter.d.mts.map +1 -1
- package/dist/service-adapters/anthropic/anthropic-adapter.mjs +12 -0
- package/dist/service-adapters/anthropic/anthropic-adapter.mjs.map +1 -1
- package/dist/service-adapters/groq/groq-adapter.cjs +13 -0
- package/dist/service-adapters/groq/groq-adapter.cjs.map +1 -1
- package/dist/service-adapters/groq/groq-adapter.d.cts +2 -0
- package/dist/service-adapters/groq/groq-adapter.d.cts.map +1 -1
- package/dist/service-adapters/groq/groq-adapter.d.mts +2 -0
- package/dist/service-adapters/groq/groq-adapter.d.mts.map +1 -1
- package/dist/service-adapters/groq/groq-adapter.mjs +13 -0
- package/dist/service-adapters/groq/groq-adapter.mjs.map +1 -1
- package/dist/service-adapters/index.d.cts +1 -0
- package/dist/service-adapters/index.d.mts +1 -0
- package/dist/service-adapters/openai/openai-adapter.cjs +14 -0
- package/dist/service-adapters/openai/openai-adapter.cjs.map +1 -1
- package/dist/service-adapters/openai/openai-adapter.d.cts +2 -0
- package/dist/service-adapters/openai/openai-adapter.d.cts.map +1 -1
- package/dist/service-adapters/openai/openai-adapter.d.mts +2 -0
- package/dist/service-adapters/openai/openai-adapter.d.mts.map +1 -1
- package/dist/service-adapters/openai/openai-adapter.mjs +14 -0
- package/dist/service-adapters/openai/openai-adapter.mjs.map +1 -1
- package/dist/service-adapters/service-adapter.d.cts +8 -0
- package/dist/service-adapters/service-adapter.d.cts.map +1 -1
- package/dist/service-adapters/service-adapter.d.mts +8 -0
- package/dist/service-adapters/service-adapter.d.mts.map +1 -1
- package/dist/service-adapters/shared/index.d.mts +2 -1
- package/dist/service-adapters/shared/sdk-client-utils.cjs +17 -0
- package/dist/service-adapters/shared/sdk-client-utils.cjs.map +1 -0
- package/dist/service-adapters/shared/sdk-client-utils.d.cts +14 -0
- package/dist/service-adapters/shared/sdk-client-utils.d.cts.map +1 -0
- package/dist/service-adapters/shared/sdk-client-utils.d.mts +14 -0
- package/dist/service-adapters/shared/sdk-client-utils.d.mts.map +1 -0
- package/dist/service-adapters/shared/sdk-client-utils.mjs +16 -0
- package/dist/service-adapters/shared/sdk-client-utils.mjs.map +1 -0
- package/package.json +7 -4
- package/src/lib/runtime/copilot-runtime.ts +4 -3
- package/src/service-adapters/anthropic/anthropic-adapter.ts +15 -1
- package/src/service-adapters/groq/groq-adapter.ts +16 -1
- package/src/service-adapters/openai/openai-adapter.ts +17 -1
- package/src/service-adapters/service-adapter.ts +9 -0
- package/src/service-adapters/shared/index.ts +1 -0
- package/src/service-adapters/shared/sdk-client-utils.ts +19 -0
- package/tests/service-adapters/anthropic/anthropic-adapter-language-model.test.ts +101 -0
- package/tests/service-adapters/groq/groq-adapter-language-model.test.ts +102 -0
- package/tests/service-adapters/openai/openai-adapter-language-model.test.ts +122 -0
- package/tests/service-adapters/shared/sdk-client-utils.test.ts +36 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-adapter.cjs","names":["convertActionInputToAnthropicTool","limitMessagesToTokenCount","convertMessageToAnthropicMessage","convertServiceAdapterError"],"sources":["../../../src/service-adapters/anthropic/anthropic-adapter.ts"],"sourcesContent":["/**\n * Copilot Runtime adapter for Anthropic.\n *\n * ## Example\n *\n * ```ts\n * import { CopilotRuntime, AnthropicAdapter } from \"@copilotkit/runtime\";\n * import Anthropic from \"@anthropic-ai/sdk\";\n *\n * const copilotKit = new CopilotRuntime();\n *\n * const anthropic = new Anthropic({\n * apiKey: \"<your-api-key>\",\n * });\n *\n * return new AnthropicAdapter({\n * anthropic,\n * promptCaching: {\n * enabled: true,\n * debug: true\n * }\n * });\n * ```\n */\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport {\n CopilotServiceAdapter,\n CopilotRuntimeChatCompletionRequest,\n CopilotRuntimeChatCompletionResponse,\n} from \"../service-adapter\";\nimport {\n convertActionInputToAnthropicTool,\n convertMessageToAnthropicMessage,\n limitMessagesToTokenCount,\n} from \"./utils\";\n\nimport { randomId, randomUUID } from \"@copilotkit/shared\";\nimport { convertServiceAdapterError } from \"../shared\";\n\nconst DEFAULT_MODEL = \"claude-3-5-sonnet-latest\";\n\nexport interface AnthropicPromptCachingConfig {\n /**\n * Whether to enable prompt caching.\n */\n enabled: boolean;\n\n /**\n * Whether to enable debug logging for cache operations.\n */\n debug?: boolean;\n}\n\nexport interface AnthropicAdapterParams {\n /**\n * An optional Anthropic instance to use. If not provided, a new instance will be\n * created.\n */\n anthropic?: Anthropic;\n\n /**\n * The model to use.\n */\n model?: string;\n\n /**\n * Configuration for prompt caching.\n * See: https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching\n */\n promptCaching?: AnthropicPromptCachingConfig;\n}\n\nexport class AnthropicAdapter implements CopilotServiceAdapter {\n public model: string = DEFAULT_MODEL;\n public provider = \"anthropic\";\n private promptCaching: AnthropicPromptCachingConfig;\n\n private _anthropic: Anthropic;\n public get anthropic(): Anthropic {\n return this._anthropic;\n }\n public get name() {\n return \"AnthropicAdapter\";\n }\n\n constructor(params?: AnthropicAdapterParams) {\n if (params?.anthropic) {\n this._anthropic = params.anthropic;\n }\n // If no instance provided, we'll lazy-load in ensureAnthropic()\n if (params?.model) {\n this.model = params.model;\n }\n this.promptCaching = params?.promptCaching || { enabled: false };\n }\n\n private ensureAnthropic(): Anthropic {\n if (!this._anthropic) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const Anthropic = require(\"@anthropic-ai/sdk\").default;\n this._anthropic = new Anthropic({});\n }\n return this._anthropic;\n }\n\n /**\n * Adds cache control to system prompt\n */\n private addSystemPromptCaching(\n system: string,\n debug: boolean = false,\n ):\n | string\n | Array<{\n type: \"text\";\n text: string;\n cache_control?: { type: \"ephemeral\" };\n }> {\n if (!this.promptCaching.enabled || !system) {\n return system;\n }\n\n const originalTextLength = system.length;\n\n if (debug) {\n console.log(\n `[ANTHROPIC CACHE DEBUG] Added cache control to system prompt (${originalTextLength} chars).`,\n );\n }\n\n return [\n {\n type: \"text\",\n text: system,\n cache_control: { type: \"ephemeral\" },\n },\n ];\n }\n\n /**\n * Adds cache control to the final message\n */\n private addIncrementalMessageCaching(\n messages: Anthropic.Messages.MessageParam[],\n debug: boolean = false,\n ): any[] {\n if (!this.promptCaching.enabled || messages.length === 0) {\n return messages;\n }\n\n const finalMessage = messages[messages.length - 1];\n const messageNumber = messages.length;\n\n if (\n Array.isArray(finalMessage.content) &&\n finalMessage.content.length > 0\n ) {\n const finalBlock = finalMessage.content[finalMessage.content.length - 1];\n\n const updatedMessages = [\n ...messages.slice(0, -1),\n {\n ...finalMessage,\n content: [\n ...finalMessage.content.slice(0, -1),\n { ...finalBlock, cache_control: { type: \"ephemeral\" } } as any,\n ],\n },\n ];\n\n if (debug) {\n console.log(\n `[ANTHROPIC CACHE DEBUG] Added cache control to final message (message ${messageNumber}).`,\n );\n }\n\n return updatedMessages;\n }\n\n return messages;\n }\n\n private shouldGenerateFallbackResponse(\n messages: Anthropic.Messages.MessageParam[],\n ): boolean {\n if (messages.length === 0) return false;\n\n const lastMessage = messages[messages.length - 1];\n\n // Check if the last message is a tool result\n const endsWithToolResult =\n lastMessage.role === \"user\" &&\n Array.isArray(lastMessage.content) &&\n lastMessage.content.some(\n (content: any) => content.type === \"tool_result\",\n );\n\n // Also check if we have a recent pattern of user message -> assistant tool use -> user tool result\n // This indicates a completed action that might not need a response\n if (messages.length >= 3 && endsWithToolResult) {\n const lastThree = messages.slice(-3);\n const hasRecentToolPattern =\n lastThree[0]?.role === \"user\" && // Initial user message\n lastThree[1]?.role === \"assistant\" && // Assistant tool use\n Array.isArray(lastThree[1].content) &&\n lastThree[1].content.some(\n (content: any) => content.type === \"tool_use\",\n ) &&\n lastThree[2]?.role === \"user\" && // Tool result\n Array.isArray(lastThree[2].content) &&\n lastThree[2].content.some(\n (content: any) => content.type === \"tool_result\",\n );\n\n return hasRecentToolPattern;\n }\n\n return endsWithToolResult;\n }\n\n async process(\n request: CopilotRuntimeChatCompletionRequest,\n ): Promise<CopilotRuntimeChatCompletionResponse> {\n const {\n threadId,\n model = this.model,\n messages: rawMessages,\n actions,\n eventSource,\n forwardedParameters,\n } = request;\n const tools = actions.map(convertActionInputToAnthropicTool);\n\n const messages = [...rawMessages];\n\n // get the instruction message\n const instructionsMessage = messages.shift();\n const instructions = instructionsMessage.isTextMessage()\n ? instructionsMessage.content\n : \"\";\n\n // ALLOWLIST APPROACH:\n // 1. First, identify all valid tool_use calls (from assistant)\n // 2. Then, only keep tool_result blocks that correspond to these valid tool_use IDs\n // 3. Discard any other tool_result blocks\n\n // Step 1: Extract valid tool_use 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: Map each message to an Anthropic message, eliminating invalid tool_results\n const processedToolResultIds = new Set<string>();\n const anthropicMessages = messages\n .map((message) => {\n // For tool results, only include if they match a valid tool_use ID AND haven't been processed\n if (message.isResultMessage()) {\n // Skip if there's no corresponding tool_use\n if (!validToolUseIds.has(message.actionExecutionId)) {\n return null; // Will be filtered out later\n }\n\n // Skip if we've already processed a result for this tool_use ID\n if (processedToolResultIds.has(message.actionExecutionId)) {\n return null; // Will be filtered out later\n }\n\n // Mark this tool result as processed\n processedToolResultIds.add(message.actionExecutionId);\n\n return {\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n content: message.result || \"Action completed successfully\",\n tool_use_id: message.actionExecutionId,\n },\n ],\n };\n }\n\n // For non-tool-result messages, convert normally\n return convertMessageToAnthropicMessage(message);\n })\n .filter(Boolean) // Remove nulls\n .filter((msg) => {\n // Filter out assistant messages with empty text content\n if (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n const hasEmptyTextOnly =\n msg.content.length === 1 &&\n msg.content[0].type === \"text\" &&\n (!(msg.content[0] as any).text ||\n (msg.content[0] as any).text.trim() === \"\");\n\n // Keep messages that have tool_use or non-empty text\n return !hasEmptyTextOnly;\n }\n return true;\n }) as Anthropic.Messages.MessageParam[];\n\n // Apply token limits\n const limitedMessages = limitMessagesToTokenCount(\n anthropicMessages,\n tools,\n model,\n );\n\n // Apply prompt caching if enabled\n const cachedSystemPrompt = this.addSystemPromptCaching(\n instructions,\n this.promptCaching.debug,\n );\n const cachedMessages = this.addIncrementalMessageCaching(\n limitedMessages,\n this.promptCaching.debug,\n );\n\n // We'll check if we need a fallback response after seeing what Anthropic returns\n // We skip grouping by role since we've already ensured uniqueness of tool_results\n\n let toolChoice: any = forwardedParameters?.toolChoice;\n if (forwardedParameters?.toolChoice === \"function\") {\n toolChoice = {\n type: \"tool\",\n name: forwardedParameters.toolChoiceFunctionName,\n };\n }\n\n try {\n const createParams = {\n system: cachedSystemPrompt,\n model: this.model,\n messages: cachedMessages,\n max_tokens: forwardedParameters?.maxTokens || 1024,\n ...(forwardedParameters?.temperature\n ? { temperature: forwardedParameters.temperature }\n : {}),\n ...(tools.length > 0 && { tools }),\n ...(toolChoice && { tool_choice: toolChoice }),\n stream: true,\n };\n\n const anthropic = this.ensureAnthropic();\n const stream = await anthropic.messages.create(createParams);\n\n eventSource.stream(async (eventStream$) => {\n let mode: \"function\" | \"message\" | null = null;\n let didOutputText = false;\n let currentMessageId = randomId();\n let currentToolCallId = randomId();\n let filterThinkingTextBuffer = new FilterThinkingTextBuffer();\n let hasReceivedContent = false;\n\n try {\n for await (const chunk of stream as AsyncIterable<any>) {\n if (chunk.type === \"message_start\") {\n currentMessageId = chunk.message.id;\n } else if (chunk.type === \"content_block_start\") {\n hasReceivedContent = true;\n if (chunk.content_block.type === \"text\") {\n didOutputText = false;\n filterThinkingTextBuffer.reset();\n mode = \"message\";\n } else if (chunk.content_block.type === \"tool_use\") {\n currentToolCallId = chunk.content_block.id;\n eventStream$.sendActionExecutionStart({\n actionExecutionId: currentToolCallId,\n actionName: chunk.content_block.name,\n parentMessageId: currentMessageId,\n });\n mode = \"function\";\n }\n } else if (chunk.type === \"content_block_delta\") {\n if (chunk.delta.type === \"text_delta\") {\n const text = filterThinkingTextBuffer.onTextChunk(\n chunk.delta.text,\n );\n if (text.length > 0) {\n if (!didOutputText) {\n eventStream$.sendTextMessageStart({\n messageId: currentMessageId,\n });\n didOutputText = true;\n }\n eventStream$.sendTextMessageContent({\n messageId: currentMessageId,\n content: text,\n });\n }\n } else if (chunk.delta.type === \"input_json_delta\") {\n eventStream$.sendActionExecutionArgs({\n actionExecutionId: currentToolCallId,\n args: chunk.delta.partial_json,\n });\n }\n } else if (chunk.type === \"content_block_stop\") {\n if (mode === \"message\") {\n if (didOutputText) {\n eventStream$.sendTextMessageEnd({\n messageId: currentMessageId,\n });\n }\n } else if (mode === \"function\") {\n eventStream$.sendActionExecutionEnd({\n actionExecutionId: currentToolCallId,\n });\n }\n }\n }\n } catch (error) {\n throw convertServiceAdapterError(error, \"Anthropic\");\n }\n\n // Generate fallback response only if Anthropic produced no content\n if (\n !hasReceivedContent &&\n this.shouldGenerateFallbackResponse(cachedMessages)\n ) {\n // Extract the tool result content for a more contextual response\n let fallbackContent = \"Task completed successfully.\";\n const lastMessage = cachedMessages[cachedMessages.length - 1];\n if (\n lastMessage?.role === \"user\" &&\n Array.isArray(lastMessage.content)\n ) {\n const toolResult = lastMessage.content.find(\n (c: any) => c.type === \"tool_result\",\n );\n if (\n toolResult?.content &&\n toolResult.content !== \"Action completed successfully\"\n ) {\n fallbackContent = toolResult.content;\n }\n }\n\n currentMessageId = randomId();\n eventStream$.sendTextMessageStart({ messageId: currentMessageId });\n eventStream$.sendTextMessageContent({\n messageId: currentMessageId,\n content: fallbackContent,\n });\n eventStream$.sendTextMessageEnd({ messageId: currentMessageId });\n }\n\n eventStream$.complete();\n });\n } catch (error) {\n throw convertServiceAdapterError(error, \"Anthropic\");\n }\n\n return {\n threadId: threadId || randomUUID(),\n };\n }\n}\n\nconst THINKING_TAG = \"<thinking>\";\nconst THINKING_TAG_END = \"</thinking>\";\n\nclass FilterThinkingTextBuffer {\n private buffer: string;\n private didFilterThinkingTag: boolean = false;\n\n constructor() {\n this.buffer = \"\";\n }\n\n onTextChunk(text: string): string {\n this.buffer += text;\n if (this.didFilterThinkingTag) {\n return text;\n }\n const potentialTag = this.buffer.slice(0, THINKING_TAG.length);\n if (THINKING_TAG.startsWith(potentialTag)) {\n if (this.buffer.includes(THINKING_TAG_END)) {\n const end = this.buffer.indexOf(THINKING_TAG_END);\n const filteredText = this.buffer.slice(end + THINKING_TAG_END.length);\n this.buffer = filteredText;\n this.didFilterThinkingTag = true;\n return filteredText;\n } else {\n return \"\";\n }\n }\n return text;\n }\n\n reset() {\n this.buffer = \"\";\n this.didFilterThinkingTag = false;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"anthropic-adapter.cjs","names":["getSdkClientOptions","convertActionInputToAnthropicTool","limitMessagesToTokenCount","convertMessageToAnthropicMessage","convertServiceAdapterError"],"sources":["../../../src/service-adapters/anthropic/anthropic-adapter.ts"],"sourcesContent":["/**\n * Copilot Runtime adapter for Anthropic.\n *\n * ## Example\n *\n * ```ts\n * import { CopilotRuntime, AnthropicAdapter } from \"@copilotkit/runtime\";\n * import Anthropic from \"@anthropic-ai/sdk\";\n *\n * const copilotKit = new CopilotRuntime();\n *\n * const anthropic = new Anthropic({\n * apiKey: \"<your-api-key>\",\n * });\n *\n * return new AnthropicAdapter({\n * anthropic,\n * promptCaching: {\n * enabled: true,\n * debug: true\n * }\n * });\n * ```\n */\nimport type { LanguageModel } from \"ai\";\nimport { createAnthropic } from \"@ai-sdk/anthropic\";\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport {\n CopilotServiceAdapter,\n CopilotRuntimeChatCompletionRequest,\n CopilotRuntimeChatCompletionResponse,\n} from \"../service-adapter\";\nimport {\n convertActionInputToAnthropicTool,\n convertMessageToAnthropicMessage,\n limitMessagesToTokenCount,\n} from \"./utils\";\n\nimport { randomId, randomUUID } from \"@copilotkit/shared\";\nimport { convertServiceAdapterError, getSdkClientOptions } from \"../shared\";\n\nconst DEFAULT_MODEL = \"claude-3-5-sonnet-latest\";\n\nexport interface AnthropicPromptCachingConfig {\n /**\n * Whether to enable prompt caching.\n */\n enabled: boolean;\n\n /**\n * Whether to enable debug logging for cache operations.\n */\n debug?: boolean;\n}\n\nexport interface AnthropicAdapterParams {\n /**\n * An optional Anthropic instance to use. If not provided, a new instance will be\n * created.\n */\n anthropic?: Anthropic;\n\n /**\n * The model to use.\n */\n model?: string;\n\n /**\n * Configuration for prompt caching.\n * See: https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching\n */\n promptCaching?: AnthropicPromptCachingConfig;\n}\n\nexport class AnthropicAdapter implements CopilotServiceAdapter {\n public model: string = DEFAULT_MODEL;\n public provider = \"anthropic\";\n private promptCaching: AnthropicPromptCachingConfig;\n\n private _anthropic: Anthropic;\n public get anthropic(): Anthropic {\n return this._anthropic;\n }\n public get name() {\n return \"AnthropicAdapter\";\n }\n\n constructor(params?: AnthropicAdapterParams) {\n if (params?.anthropic) {\n this._anthropic = params.anthropic;\n }\n // If no instance provided, we'll lazy-load in ensureAnthropic()\n if (params?.model) {\n this.model = params.model;\n }\n this.promptCaching = params?.promptCaching || { enabled: false };\n }\n\n getLanguageModel(): LanguageModel {\n const anthropic = this.ensureAnthropic();\n const options = getSdkClientOptions(anthropic);\n const provider = createAnthropic({\n baseURL: anthropic.baseURL,\n apiKey: anthropic.apiKey,\n headers: options.defaultHeaders,\n fetch: options.fetch,\n });\n return provider(this.model);\n }\n\n private ensureAnthropic(): Anthropic {\n if (!this._anthropic) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const Anthropic = require(\"@anthropic-ai/sdk\").default;\n this._anthropic = new Anthropic({});\n }\n return this._anthropic;\n }\n\n /**\n * Adds cache control to system prompt\n */\n private addSystemPromptCaching(\n system: string,\n debug: boolean = false,\n ):\n | string\n | Array<{\n type: \"text\";\n text: string;\n cache_control?: { type: \"ephemeral\" };\n }> {\n if (!this.promptCaching.enabled || !system) {\n return system;\n }\n\n const originalTextLength = system.length;\n\n if (debug) {\n console.log(\n `[ANTHROPIC CACHE DEBUG] Added cache control to system prompt (${originalTextLength} chars).`,\n );\n }\n\n return [\n {\n type: \"text\",\n text: system,\n cache_control: { type: \"ephemeral\" },\n },\n ];\n }\n\n /**\n * Adds cache control to the final message\n */\n private addIncrementalMessageCaching(\n messages: Anthropic.Messages.MessageParam[],\n debug: boolean = false,\n ): any[] {\n if (!this.promptCaching.enabled || messages.length === 0) {\n return messages;\n }\n\n const finalMessage = messages[messages.length - 1];\n const messageNumber = messages.length;\n\n if (\n Array.isArray(finalMessage.content) &&\n finalMessage.content.length > 0\n ) {\n const finalBlock = finalMessage.content[finalMessage.content.length - 1];\n\n const updatedMessages = [\n ...messages.slice(0, -1),\n {\n ...finalMessage,\n content: [\n ...finalMessage.content.slice(0, -1),\n { ...finalBlock, cache_control: { type: \"ephemeral\" } } as any,\n ],\n },\n ];\n\n if (debug) {\n console.log(\n `[ANTHROPIC CACHE DEBUG] Added cache control to final message (message ${messageNumber}).`,\n );\n }\n\n return updatedMessages;\n }\n\n return messages;\n }\n\n private shouldGenerateFallbackResponse(\n messages: Anthropic.Messages.MessageParam[],\n ): boolean {\n if (messages.length === 0) return false;\n\n const lastMessage = messages[messages.length - 1];\n\n // Check if the last message is a tool result\n const endsWithToolResult =\n lastMessage.role === \"user\" &&\n Array.isArray(lastMessage.content) &&\n lastMessage.content.some(\n (content: any) => content.type === \"tool_result\",\n );\n\n // Also check if we have a recent pattern of user message -> assistant tool use -> user tool result\n // This indicates a completed action that might not need a response\n if (messages.length >= 3 && endsWithToolResult) {\n const lastThree = messages.slice(-3);\n const hasRecentToolPattern =\n lastThree[0]?.role === \"user\" && // Initial user message\n lastThree[1]?.role === \"assistant\" && // Assistant tool use\n Array.isArray(lastThree[1].content) &&\n lastThree[1].content.some(\n (content: any) => content.type === \"tool_use\",\n ) &&\n lastThree[2]?.role === \"user\" && // Tool result\n Array.isArray(lastThree[2].content) &&\n lastThree[2].content.some(\n (content: any) => content.type === \"tool_result\",\n );\n\n return hasRecentToolPattern;\n }\n\n return endsWithToolResult;\n }\n\n async process(\n request: CopilotRuntimeChatCompletionRequest,\n ): Promise<CopilotRuntimeChatCompletionResponse> {\n const {\n threadId,\n model = this.model,\n messages: rawMessages,\n actions,\n eventSource,\n forwardedParameters,\n } = request;\n const tools = actions.map(convertActionInputToAnthropicTool);\n\n const messages = [...rawMessages];\n\n // get the instruction message\n const instructionsMessage = messages.shift();\n const instructions = instructionsMessage.isTextMessage()\n ? instructionsMessage.content\n : \"\";\n\n // ALLOWLIST APPROACH:\n // 1. First, identify all valid tool_use calls (from assistant)\n // 2. Then, only keep tool_result blocks that correspond to these valid tool_use IDs\n // 3. Discard any other tool_result blocks\n\n // Step 1: Extract valid tool_use 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: Map each message to an Anthropic message, eliminating invalid tool_results\n const processedToolResultIds = new Set<string>();\n const anthropicMessages = messages\n .map((message) => {\n // For tool results, only include if they match a valid tool_use ID AND haven't been processed\n if (message.isResultMessage()) {\n // Skip if there's no corresponding tool_use\n if (!validToolUseIds.has(message.actionExecutionId)) {\n return null; // Will be filtered out later\n }\n\n // Skip if we've already processed a result for this tool_use ID\n if (processedToolResultIds.has(message.actionExecutionId)) {\n return null; // Will be filtered out later\n }\n\n // Mark this tool result as processed\n processedToolResultIds.add(message.actionExecutionId);\n\n return {\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n content: message.result || \"Action completed successfully\",\n tool_use_id: message.actionExecutionId,\n },\n ],\n };\n }\n\n // For non-tool-result messages, convert normally\n return convertMessageToAnthropicMessage(message);\n })\n .filter(Boolean) // Remove nulls\n .filter((msg) => {\n // Filter out assistant messages with empty text content\n if (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n const hasEmptyTextOnly =\n msg.content.length === 1 &&\n msg.content[0].type === \"text\" &&\n (!(msg.content[0] as any).text ||\n (msg.content[0] as any).text.trim() === \"\");\n\n // Keep messages that have tool_use or non-empty text\n return !hasEmptyTextOnly;\n }\n return true;\n }) as Anthropic.Messages.MessageParam[];\n\n // Apply token limits\n const limitedMessages = limitMessagesToTokenCount(\n anthropicMessages,\n tools,\n model,\n );\n\n // Apply prompt caching if enabled\n const cachedSystemPrompt = this.addSystemPromptCaching(\n instructions,\n this.promptCaching.debug,\n );\n const cachedMessages = this.addIncrementalMessageCaching(\n limitedMessages,\n this.promptCaching.debug,\n );\n\n // We'll check if we need a fallback response after seeing what Anthropic returns\n // We skip grouping by role since we've already ensured uniqueness of tool_results\n\n let toolChoice: any = forwardedParameters?.toolChoice;\n if (forwardedParameters?.toolChoice === \"function\") {\n toolChoice = {\n type: \"tool\",\n name: forwardedParameters.toolChoiceFunctionName,\n };\n }\n\n try {\n const createParams = {\n system: cachedSystemPrompt,\n model: this.model,\n messages: cachedMessages,\n max_tokens: forwardedParameters?.maxTokens || 1024,\n ...(forwardedParameters?.temperature\n ? { temperature: forwardedParameters.temperature }\n : {}),\n ...(tools.length > 0 && { tools }),\n ...(toolChoice && { tool_choice: toolChoice }),\n stream: true,\n };\n\n const anthropic = this.ensureAnthropic();\n const stream = await anthropic.messages.create(createParams);\n\n eventSource.stream(async (eventStream$) => {\n let mode: \"function\" | \"message\" | null = null;\n let didOutputText = false;\n let currentMessageId = randomId();\n let currentToolCallId = randomId();\n let filterThinkingTextBuffer = new FilterThinkingTextBuffer();\n let hasReceivedContent = false;\n\n try {\n for await (const chunk of stream as AsyncIterable<any>) {\n if (chunk.type === \"message_start\") {\n currentMessageId = chunk.message.id;\n } else if (chunk.type === \"content_block_start\") {\n hasReceivedContent = true;\n if (chunk.content_block.type === \"text\") {\n didOutputText = false;\n filterThinkingTextBuffer.reset();\n mode = \"message\";\n } else if (chunk.content_block.type === \"tool_use\") {\n currentToolCallId = chunk.content_block.id;\n eventStream$.sendActionExecutionStart({\n actionExecutionId: currentToolCallId,\n actionName: chunk.content_block.name,\n parentMessageId: currentMessageId,\n });\n mode = \"function\";\n }\n } else if (chunk.type === \"content_block_delta\") {\n if (chunk.delta.type === \"text_delta\") {\n const text = filterThinkingTextBuffer.onTextChunk(\n chunk.delta.text,\n );\n if (text.length > 0) {\n if (!didOutputText) {\n eventStream$.sendTextMessageStart({\n messageId: currentMessageId,\n });\n didOutputText = true;\n }\n eventStream$.sendTextMessageContent({\n messageId: currentMessageId,\n content: text,\n });\n }\n } else if (chunk.delta.type === \"input_json_delta\") {\n eventStream$.sendActionExecutionArgs({\n actionExecutionId: currentToolCallId,\n args: chunk.delta.partial_json,\n });\n }\n } else if (chunk.type === \"content_block_stop\") {\n if (mode === \"message\") {\n if (didOutputText) {\n eventStream$.sendTextMessageEnd({\n messageId: currentMessageId,\n });\n }\n } else if (mode === \"function\") {\n eventStream$.sendActionExecutionEnd({\n actionExecutionId: currentToolCallId,\n });\n }\n }\n }\n } catch (error) {\n throw convertServiceAdapterError(error, \"Anthropic\");\n }\n\n // Generate fallback response only if Anthropic produced no content\n if (\n !hasReceivedContent &&\n this.shouldGenerateFallbackResponse(cachedMessages)\n ) {\n // Extract the tool result content for a more contextual response\n let fallbackContent = \"Task completed successfully.\";\n const lastMessage = cachedMessages[cachedMessages.length - 1];\n if (\n lastMessage?.role === \"user\" &&\n Array.isArray(lastMessage.content)\n ) {\n const toolResult = lastMessage.content.find(\n (c: any) => c.type === \"tool_result\",\n );\n if (\n toolResult?.content &&\n toolResult.content !== \"Action completed successfully\"\n ) {\n fallbackContent = toolResult.content;\n }\n }\n\n currentMessageId = randomId();\n eventStream$.sendTextMessageStart({ messageId: currentMessageId });\n eventStream$.sendTextMessageContent({\n messageId: currentMessageId,\n content: fallbackContent,\n });\n eventStream$.sendTextMessageEnd({ messageId: currentMessageId });\n }\n\n eventStream$.complete();\n });\n } catch (error) {\n throw convertServiceAdapterError(error, \"Anthropic\");\n }\n\n return {\n threadId: threadId || randomUUID(),\n };\n }\n}\n\nconst THINKING_TAG = \"<thinking>\";\nconst THINKING_TAG_END = \"</thinking>\";\n\nclass FilterThinkingTextBuffer {\n private buffer: string;\n private didFilterThinkingTag: boolean = false;\n\n constructor() {\n this.buffer = \"\";\n }\n\n onTextChunk(text: string): string {\n this.buffer += text;\n if (this.didFilterThinkingTag) {\n return text;\n }\n const potentialTag = this.buffer.slice(0, THINKING_TAG.length);\n if (THINKING_TAG.startsWith(potentialTag)) {\n if (this.buffer.includes(THINKING_TAG_END)) {\n const end = this.buffer.indexOf(THINKING_TAG_END);\n const filteredText = this.buffer.slice(end + THINKING_TAG_END.length);\n this.buffer = filteredText;\n this.didFilterThinkingTag = true;\n return filteredText;\n } else {\n return \"\";\n }\n }\n return text;\n }\n\n reset() {\n this.buffer = \"\";\n this.didFilterThinkingTag = false;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { CopilotRuntimeChatCompletionRequest, CopilotRuntimeChatCompletionResponse, CopilotServiceAdapter } from "../service-adapter.cjs";
|
|
3
|
+
import { LanguageModel } from "ai";
|
|
3
4
|
import Anthropic from "@anthropic-ai/sdk";
|
|
4
5
|
|
|
5
6
|
//#region src/service-adapters/anthropic/anthropic-adapter.d.ts
|
|
@@ -37,6 +38,7 @@ declare class AnthropicAdapter implements CopilotServiceAdapter {
|
|
|
37
38
|
get anthropic(): Anthropic;
|
|
38
39
|
get name(): string;
|
|
39
40
|
constructor(params?: AnthropicAdapterParams);
|
|
41
|
+
getLanguageModel(): LanguageModel;
|
|
40
42
|
private ensureAnthropic;
|
|
41
43
|
/**
|
|
42
44
|
* Adds cache control to system prompt
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-adapter.d.cts","names":[],"sources":["../../../src/service-adapters/anthropic/anthropic-adapter.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"anthropic-adapter.d.cts","names":[],"sources":["../../../src/service-adapters/anthropic/anthropic-adapter.ts"],"mappings":";;;;;;UA2CiB,4BAAA;EAiMJ;;;EA7LX,OAAA;EA2B4D;;;EAtB5D,KAAA;AAAA;AAAA,UAGe,sBAAA;EAyBJ;;;;EApBX,SAAA,GAAY,SAAA;EA2BA;;;EAtBZ,KAAA;EAyDQ;;;;EAnDR,aAAA,GAAgB,4BAAA;AAAA;AAAA,cAGL,gBAAA,YAA4B,qBAAA;EAChC,KAAA;EACA,QAAA;EAAA,QACC,aAAA;EAAA,QAEA,UAAA;EAAA,IACG,SAAA,CAAA,GAAa,SAAA;EAAA,IAGb,IAAA,CAAA;cAIC,MAAA,GAAS,sBAAA;EAWrB,gBAAA,CAAA,GAAoB,aAAA;EAAA,QAYZ,eAAA;;;;UAYA,sBAAA;;;;UAkCA,4BAAA;EAAA,QAwCA,8BAAA;EAsCF,OAAA,CACJ,OAAA,EAAS,mCAAA,GACR,OAAA,CAAQ,oCAAA;AAAA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
2
|
import { CopilotRuntimeChatCompletionRequest, CopilotRuntimeChatCompletionResponse, CopilotServiceAdapter } from "../service-adapter.mjs";
|
|
3
3
|
import Anthropic from "@anthropic-ai/sdk";
|
|
4
|
+
import { LanguageModel } from "ai";
|
|
4
5
|
|
|
5
6
|
//#region src/service-adapters/anthropic/anthropic-adapter.d.ts
|
|
6
7
|
interface AnthropicPromptCachingConfig {
|
|
@@ -37,6 +38,7 @@ declare class AnthropicAdapter implements CopilotServiceAdapter {
|
|
|
37
38
|
get anthropic(): Anthropic;
|
|
38
39
|
get name(): string;
|
|
39
40
|
constructor(params?: AnthropicAdapterParams);
|
|
41
|
+
getLanguageModel(): LanguageModel;
|
|
40
42
|
private ensureAnthropic;
|
|
41
43
|
/**
|
|
42
44
|
* Adds cache control to system prompt
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-adapter.d.mts","names":[],"sources":["../../../src/service-adapters/anthropic/anthropic-adapter.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"anthropic-adapter.d.mts","names":[],"sources":["../../../src/service-adapters/anthropic/anthropic-adapter.ts"],"mappings":";;;;;;UA2CiB,4BAAA;EAiMJ;;;EA7LX,OAAA;EA2B4D;;;EAtB5D,KAAA;AAAA;AAAA,UAGe,sBAAA;EAyBJ;;;;EApBX,SAAA,GAAY,SAAA;EA2BA;;;EAtBZ,KAAA;EAyDQ;;;;EAnDR,aAAA,GAAgB,4BAAA;AAAA;AAAA,cAGL,gBAAA,YAA4B,qBAAA;EAChC,KAAA;EACA,QAAA;EAAA,QACC,aAAA;EAAA,QAEA,UAAA;EAAA,IACG,SAAA,CAAA,GAAa,SAAA;EAAA,IAGb,IAAA,CAAA;cAIC,MAAA,GAAS,sBAAA;EAWrB,gBAAA,CAAA,GAAoB,aAAA;EAAA,QAYZ,eAAA;;;;UAYA,sBAAA;;;;UAkCA,4BAAA;EAAA,QAwCA,8BAAA;EAsCF,OAAA,CACJ,OAAA,EAAS,mCAAA,GACR,OAAA,CAAQ,oCAAA;AAAA"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
2
|
import { __require } from "../../_virtual/_rolldown/runtime.mjs";
|
|
3
3
|
import { convertServiceAdapterError } from "../shared/error-utils.mjs";
|
|
4
|
+
import { getSdkClientOptions } from "../shared/sdk-client-utils.mjs";
|
|
4
5
|
import { convertActionInputToAnthropicTool, convertMessageToAnthropicMessage, limitMessagesToTokenCount } from "./utils.mjs";
|
|
5
6
|
import { randomId, randomUUID } from "@copilotkit/shared";
|
|
7
|
+
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
6
8
|
|
|
7
9
|
//#region src/service-adapters/anthropic/anthropic-adapter.ts
|
|
8
10
|
const DEFAULT_MODEL = "claude-3-5-sonnet-latest";
|
|
@@ -20,6 +22,16 @@ var AnthropicAdapter = class {
|
|
|
20
22
|
if (params?.model) this.model = params.model;
|
|
21
23
|
this.promptCaching = params?.promptCaching || { enabled: false };
|
|
22
24
|
}
|
|
25
|
+
getLanguageModel() {
|
|
26
|
+
const anthropic = this.ensureAnthropic();
|
|
27
|
+
const options = getSdkClientOptions(anthropic);
|
|
28
|
+
return createAnthropic({
|
|
29
|
+
baseURL: anthropic.baseURL,
|
|
30
|
+
apiKey: anthropic.apiKey,
|
|
31
|
+
headers: options.defaultHeaders,
|
|
32
|
+
fetch: options.fetch
|
|
33
|
+
})(this.model);
|
|
34
|
+
}
|
|
23
35
|
ensureAnthropic() {
|
|
24
36
|
if (!this._anthropic) {
|
|
25
37
|
const Anthropic = __require("@anthropic-ai/sdk").default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-adapter.mjs","names":[],"sources":["../../../src/service-adapters/anthropic/anthropic-adapter.ts"],"sourcesContent":["/**\n * Copilot Runtime adapter for Anthropic.\n *\n * ## Example\n *\n * ```ts\n * import { CopilotRuntime, AnthropicAdapter } from \"@copilotkit/runtime\";\n * import Anthropic from \"@anthropic-ai/sdk\";\n *\n * const copilotKit = new CopilotRuntime();\n *\n * const anthropic = new Anthropic({\n * apiKey: \"<your-api-key>\",\n * });\n *\n * return new AnthropicAdapter({\n * anthropic,\n * promptCaching: {\n * enabled: true,\n * debug: true\n * }\n * });\n * ```\n */\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport {\n CopilotServiceAdapter,\n CopilotRuntimeChatCompletionRequest,\n CopilotRuntimeChatCompletionResponse,\n} from \"../service-adapter\";\nimport {\n convertActionInputToAnthropicTool,\n convertMessageToAnthropicMessage,\n limitMessagesToTokenCount,\n} from \"./utils\";\n\nimport { randomId, randomUUID } from \"@copilotkit/shared\";\nimport { convertServiceAdapterError } from \"../shared\";\n\nconst DEFAULT_MODEL = \"claude-3-5-sonnet-latest\";\n\nexport interface AnthropicPromptCachingConfig {\n /**\n * Whether to enable prompt caching.\n */\n enabled: boolean;\n\n /**\n * Whether to enable debug logging for cache operations.\n */\n debug?: boolean;\n}\n\nexport interface AnthropicAdapterParams {\n /**\n * An optional Anthropic instance to use. If not provided, a new instance will be\n * created.\n */\n anthropic?: Anthropic;\n\n /**\n * The model to use.\n */\n model?: string;\n\n /**\n * Configuration for prompt caching.\n * See: https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching\n */\n promptCaching?: AnthropicPromptCachingConfig;\n}\n\nexport class AnthropicAdapter implements CopilotServiceAdapter {\n public model: string = DEFAULT_MODEL;\n public provider = \"anthropic\";\n private promptCaching: AnthropicPromptCachingConfig;\n\n private _anthropic: Anthropic;\n public get anthropic(): Anthropic {\n return this._anthropic;\n }\n public get name() {\n return \"AnthropicAdapter\";\n }\n\n constructor(params?: AnthropicAdapterParams) {\n if (params?.anthropic) {\n this._anthropic = params.anthropic;\n }\n // If no instance provided, we'll lazy-load in ensureAnthropic()\n if (params?.model) {\n this.model = params.model;\n }\n this.promptCaching = params?.promptCaching || { enabled: false };\n }\n\n private ensureAnthropic(): Anthropic {\n if (!this._anthropic) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const Anthropic = require(\"@anthropic-ai/sdk\").default;\n this._anthropic = new Anthropic({});\n }\n return this._anthropic;\n }\n\n /**\n * Adds cache control to system prompt\n */\n private addSystemPromptCaching(\n system: string,\n debug: boolean = false,\n ):\n | string\n | Array<{\n type: \"text\";\n text: string;\n cache_control?: { type: \"ephemeral\" };\n }> {\n if (!this.promptCaching.enabled || !system) {\n return system;\n }\n\n const originalTextLength = system.length;\n\n if (debug) {\n console.log(\n `[ANTHROPIC CACHE DEBUG] Added cache control to system prompt (${originalTextLength} chars).`,\n );\n }\n\n return [\n {\n type: \"text\",\n text: system,\n cache_control: { type: \"ephemeral\" },\n },\n ];\n }\n\n /**\n * Adds cache control to the final message\n */\n private addIncrementalMessageCaching(\n messages: Anthropic.Messages.MessageParam[],\n debug: boolean = false,\n ): any[] {\n if (!this.promptCaching.enabled || messages.length === 0) {\n return messages;\n }\n\n const finalMessage = messages[messages.length - 1];\n const messageNumber = messages.length;\n\n if (\n Array.isArray(finalMessage.content) &&\n finalMessage.content.length > 0\n ) {\n const finalBlock = finalMessage.content[finalMessage.content.length - 1];\n\n const updatedMessages = [\n ...messages.slice(0, -1),\n {\n ...finalMessage,\n content: [\n ...finalMessage.content.slice(0, -1),\n { ...finalBlock, cache_control: { type: \"ephemeral\" } } as any,\n ],\n },\n ];\n\n if (debug) {\n console.log(\n `[ANTHROPIC CACHE DEBUG] Added cache control to final message (message ${messageNumber}).`,\n );\n }\n\n return updatedMessages;\n }\n\n return messages;\n }\n\n private shouldGenerateFallbackResponse(\n messages: Anthropic.Messages.MessageParam[],\n ): boolean {\n if (messages.length === 0) return false;\n\n const lastMessage = messages[messages.length - 1];\n\n // Check if the last message is a tool result\n const endsWithToolResult =\n lastMessage.role === \"user\" &&\n Array.isArray(lastMessage.content) &&\n lastMessage.content.some(\n (content: any) => content.type === \"tool_result\",\n );\n\n // Also check if we have a recent pattern of user message -> assistant tool use -> user tool result\n // This indicates a completed action that might not need a response\n if (messages.length >= 3 && endsWithToolResult) {\n const lastThree = messages.slice(-3);\n const hasRecentToolPattern =\n lastThree[0]?.role === \"user\" && // Initial user message\n lastThree[1]?.role === \"assistant\" && // Assistant tool use\n Array.isArray(lastThree[1].content) &&\n lastThree[1].content.some(\n (content: any) => content.type === \"tool_use\",\n ) &&\n lastThree[2]?.role === \"user\" && // Tool result\n Array.isArray(lastThree[2].content) &&\n lastThree[2].content.some(\n (content: any) => content.type === \"tool_result\",\n );\n\n return hasRecentToolPattern;\n }\n\n return endsWithToolResult;\n }\n\n async process(\n request: CopilotRuntimeChatCompletionRequest,\n ): Promise<CopilotRuntimeChatCompletionResponse> {\n const {\n threadId,\n model = this.model,\n messages: rawMessages,\n actions,\n eventSource,\n forwardedParameters,\n } = request;\n const tools = actions.map(convertActionInputToAnthropicTool);\n\n const messages = [...rawMessages];\n\n // get the instruction message\n const instructionsMessage = messages.shift();\n const instructions = instructionsMessage.isTextMessage()\n ? instructionsMessage.content\n : \"\";\n\n // ALLOWLIST APPROACH:\n // 1. First, identify all valid tool_use calls (from assistant)\n // 2. Then, only keep tool_result blocks that correspond to these valid tool_use IDs\n // 3. Discard any other tool_result blocks\n\n // Step 1: Extract valid tool_use 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: Map each message to an Anthropic message, eliminating invalid tool_results\n const processedToolResultIds = new Set<string>();\n const anthropicMessages = messages\n .map((message) => {\n // For tool results, only include if they match a valid tool_use ID AND haven't been processed\n if (message.isResultMessage()) {\n // Skip if there's no corresponding tool_use\n if (!validToolUseIds.has(message.actionExecutionId)) {\n return null; // Will be filtered out later\n }\n\n // Skip if we've already processed a result for this tool_use ID\n if (processedToolResultIds.has(message.actionExecutionId)) {\n return null; // Will be filtered out later\n }\n\n // Mark this tool result as processed\n processedToolResultIds.add(message.actionExecutionId);\n\n return {\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n content: message.result || \"Action completed successfully\",\n tool_use_id: message.actionExecutionId,\n },\n ],\n };\n }\n\n // For non-tool-result messages, convert normally\n return convertMessageToAnthropicMessage(message);\n })\n .filter(Boolean) // Remove nulls\n .filter((msg) => {\n // Filter out assistant messages with empty text content\n if (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n const hasEmptyTextOnly =\n msg.content.length === 1 &&\n msg.content[0].type === \"text\" &&\n (!(msg.content[0] as any).text ||\n (msg.content[0] as any).text.trim() === \"\");\n\n // Keep messages that have tool_use or non-empty text\n return !hasEmptyTextOnly;\n }\n return true;\n }) as Anthropic.Messages.MessageParam[];\n\n // Apply token limits\n const limitedMessages = limitMessagesToTokenCount(\n anthropicMessages,\n tools,\n model,\n );\n\n // Apply prompt caching if enabled\n const cachedSystemPrompt = this.addSystemPromptCaching(\n instructions,\n this.promptCaching.debug,\n );\n const cachedMessages = this.addIncrementalMessageCaching(\n limitedMessages,\n this.promptCaching.debug,\n );\n\n // We'll check if we need a fallback response after seeing what Anthropic returns\n // We skip grouping by role since we've already ensured uniqueness of tool_results\n\n let toolChoice: any = forwardedParameters?.toolChoice;\n if (forwardedParameters?.toolChoice === \"function\") {\n toolChoice = {\n type: \"tool\",\n name: forwardedParameters.toolChoiceFunctionName,\n };\n }\n\n try {\n const createParams = {\n system: cachedSystemPrompt,\n model: this.model,\n messages: cachedMessages,\n max_tokens: forwardedParameters?.maxTokens || 1024,\n ...(forwardedParameters?.temperature\n ? { temperature: forwardedParameters.temperature }\n : {}),\n ...(tools.length > 0 && { tools }),\n ...(toolChoice && { tool_choice: toolChoice }),\n stream: true,\n };\n\n const anthropic = this.ensureAnthropic();\n const stream = await anthropic.messages.create(createParams);\n\n eventSource.stream(async (eventStream$) => {\n let mode: \"function\" | \"message\" | null = null;\n let didOutputText = false;\n let currentMessageId = randomId();\n let currentToolCallId = randomId();\n let filterThinkingTextBuffer = new FilterThinkingTextBuffer();\n let hasReceivedContent = false;\n\n try {\n for await (const chunk of stream as AsyncIterable<any>) {\n if (chunk.type === \"message_start\") {\n currentMessageId = chunk.message.id;\n } else if (chunk.type === \"content_block_start\") {\n hasReceivedContent = true;\n if (chunk.content_block.type === \"text\") {\n didOutputText = false;\n filterThinkingTextBuffer.reset();\n mode = \"message\";\n } else if (chunk.content_block.type === \"tool_use\") {\n currentToolCallId = chunk.content_block.id;\n eventStream$.sendActionExecutionStart({\n actionExecutionId: currentToolCallId,\n actionName: chunk.content_block.name,\n parentMessageId: currentMessageId,\n });\n mode = \"function\";\n }\n } else if (chunk.type === \"content_block_delta\") {\n if (chunk.delta.type === \"text_delta\") {\n const text = filterThinkingTextBuffer.onTextChunk(\n chunk.delta.text,\n );\n if (text.length > 0) {\n if (!didOutputText) {\n eventStream$.sendTextMessageStart({\n messageId: currentMessageId,\n });\n didOutputText = true;\n }\n eventStream$.sendTextMessageContent({\n messageId: currentMessageId,\n content: text,\n });\n }\n } else if (chunk.delta.type === \"input_json_delta\") {\n eventStream$.sendActionExecutionArgs({\n actionExecutionId: currentToolCallId,\n args: chunk.delta.partial_json,\n });\n }\n } else if (chunk.type === \"content_block_stop\") {\n if (mode === \"message\") {\n if (didOutputText) {\n eventStream$.sendTextMessageEnd({\n messageId: currentMessageId,\n });\n }\n } else if (mode === \"function\") {\n eventStream$.sendActionExecutionEnd({\n actionExecutionId: currentToolCallId,\n });\n }\n }\n }\n } catch (error) {\n throw convertServiceAdapterError(error, \"Anthropic\");\n }\n\n // Generate fallback response only if Anthropic produced no content\n if (\n !hasReceivedContent &&\n this.shouldGenerateFallbackResponse(cachedMessages)\n ) {\n // Extract the tool result content for a more contextual response\n let fallbackContent = \"Task completed successfully.\";\n const lastMessage = cachedMessages[cachedMessages.length - 1];\n if (\n lastMessage?.role === \"user\" &&\n Array.isArray(lastMessage.content)\n ) {\n const toolResult = lastMessage.content.find(\n (c: any) => c.type === \"tool_result\",\n );\n if (\n toolResult?.content &&\n toolResult.content !== \"Action completed successfully\"\n ) {\n fallbackContent = toolResult.content;\n }\n }\n\n currentMessageId = randomId();\n eventStream$.sendTextMessageStart({ messageId: currentMessageId });\n eventStream$.sendTextMessageContent({\n messageId: currentMessageId,\n content: fallbackContent,\n });\n eventStream$.sendTextMessageEnd({ messageId: currentMessageId });\n }\n\n eventStream$.complete();\n });\n } catch (error) {\n throw convertServiceAdapterError(error, \"Anthropic\");\n }\n\n return {\n threadId: threadId || randomUUID(),\n };\n }\n}\n\nconst THINKING_TAG = \"<thinking>\";\nconst THINKING_TAG_END = \"</thinking>\";\n\nclass FilterThinkingTextBuffer {\n private buffer: string;\n private didFilterThinkingTag: boolean = false;\n\n constructor() {\n this.buffer = \"\";\n }\n\n onTextChunk(text: string): string {\n this.buffer += text;\n if (this.didFilterThinkingTag) {\n return text;\n }\n const potentialTag = this.buffer.slice(0, THINKING_TAG.length);\n if (THINKING_TAG.startsWith(potentialTag)) {\n if (this.buffer.includes(THINKING_TAG_END)) {\n const end = this.buffer.indexOf(THINKING_TAG_END);\n const filteredText = this.buffer.slice(end + THINKING_TAG_END.length);\n this.buffer = filteredText;\n this.didFilterThinkingTag = true;\n return filteredText;\n } else {\n return \"\";\n }\n }\n return text;\n }\n\n reset() {\n this.buffer = \"\";\n this.didFilterThinkingTag = false;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"anthropic-adapter.mjs","names":[],"sources":["../../../src/service-adapters/anthropic/anthropic-adapter.ts"],"sourcesContent":["/**\n * Copilot Runtime adapter for Anthropic.\n *\n * ## Example\n *\n * ```ts\n * import { CopilotRuntime, AnthropicAdapter } from \"@copilotkit/runtime\";\n * import Anthropic from \"@anthropic-ai/sdk\";\n *\n * const copilotKit = new CopilotRuntime();\n *\n * const anthropic = new Anthropic({\n * apiKey: \"<your-api-key>\",\n * });\n *\n * return new AnthropicAdapter({\n * anthropic,\n * promptCaching: {\n * enabled: true,\n * debug: true\n * }\n * });\n * ```\n */\nimport type { LanguageModel } from \"ai\";\nimport { createAnthropic } from \"@ai-sdk/anthropic\";\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport {\n CopilotServiceAdapter,\n CopilotRuntimeChatCompletionRequest,\n CopilotRuntimeChatCompletionResponse,\n} from \"../service-adapter\";\nimport {\n convertActionInputToAnthropicTool,\n convertMessageToAnthropicMessage,\n limitMessagesToTokenCount,\n} from \"./utils\";\n\nimport { randomId, randomUUID } from \"@copilotkit/shared\";\nimport { convertServiceAdapterError, getSdkClientOptions } from \"../shared\";\n\nconst DEFAULT_MODEL = \"claude-3-5-sonnet-latest\";\n\nexport interface AnthropicPromptCachingConfig {\n /**\n * Whether to enable prompt caching.\n */\n enabled: boolean;\n\n /**\n * Whether to enable debug logging for cache operations.\n */\n debug?: boolean;\n}\n\nexport interface AnthropicAdapterParams {\n /**\n * An optional Anthropic instance to use. If not provided, a new instance will be\n * created.\n */\n anthropic?: Anthropic;\n\n /**\n * The model to use.\n */\n model?: string;\n\n /**\n * Configuration for prompt caching.\n * See: https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching\n */\n promptCaching?: AnthropicPromptCachingConfig;\n}\n\nexport class AnthropicAdapter implements CopilotServiceAdapter {\n public model: string = DEFAULT_MODEL;\n public provider = \"anthropic\";\n private promptCaching: AnthropicPromptCachingConfig;\n\n private _anthropic: Anthropic;\n public get anthropic(): Anthropic {\n return this._anthropic;\n }\n public get name() {\n return \"AnthropicAdapter\";\n }\n\n constructor(params?: AnthropicAdapterParams) {\n if (params?.anthropic) {\n this._anthropic = params.anthropic;\n }\n // If no instance provided, we'll lazy-load in ensureAnthropic()\n if (params?.model) {\n this.model = params.model;\n }\n this.promptCaching = params?.promptCaching || { enabled: false };\n }\n\n getLanguageModel(): LanguageModel {\n const anthropic = this.ensureAnthropic();\n const options = getSdkClientOptions(anthropic);\n const provider = createAnthropic({\n baseURL: anthropic.baseURL,\n apiKey: anthropic.apiKey,\n headers: options.defaultHeaders,\n fetch: options.fetch,\n });\n return provider(this.model);\n }\n\n private ensureAnthropic(): Anthropic {\n if (!this._anthropic) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const Anthropic = require(\"@anthropic-ai/sdk\").default;\n this._anthropic = new Anthropic({});\n }\n return this._anthropic;\n }\n\n /**\n * Adds cache control to system prompt\n */\n private addSystemPromptCaching(\n system: string,\n debug: boolean = false,\n ):\n | string\n | Array<{\n type: \"text\";\n text: string;\n cache_control?: { type: \"ephemeral\" };\n }> {\n if (!this.promptCaching.enabled || !system) {\n return system;\n }\n\n const originalTextLength = system.length;\n\n if (debug) {\n console.log(\n `[ANTHROPIC CACHE DEBUG] Added cache control to system prompt (${originalTextLength} chars).`,\n );\n }\n\n return [\n {\n type: \"text\",\n text: system,\n cache_control: { type: \"ephemeral\" },\n },\n ];\n }\n\n /**\n * Adds cache control to the final message\n */\n private addIncrementalMessageCaching(\n messages: Anthropic.Messages.MessageParam[],\n debug: boolean = false,\n ): any[] {\n if (!this.promptCaching.enabled || messages.length === 0) {\n return messages;\n }\n\n const finalMessage = messages[messages.length - 1];\n const messageNumber = messages.length;\n\n if (\n Array.isArray(finalMessage.content) &&\n finalMessage.content.length > 0\n ) {\n const finalBlock = finalMessage.content[finalMessage.content.length - 1];\n\n const updatedMessages = [\n ...messages.slice(0, -1),\n {\n ...finalMessage,\n content: [\n ...finalMessage.content.slice(0, -1),\n { ...finalBlock, cache_control: { type: \"ephemeral\" } } as any,\n ],\n },\n ];\n\n if (debug) {\n console.log(\n `[ANTHROPIC CACHE DEBUG] Added cache control to final message (message ${messageNumber}).`,\n );\n }\n\n return updatedMessages;\n }\n\n return messages;\n }\n\n private shouldGenerateFallbackResponse(\n messages: Anthropic.Messages.MessageParam[],\n ): boolean {\n if (messages.length === 0) return false;\n\n const lastMessage = messages[messages.length - 1];\n\n // Check if the last message is a tool result\n const endsWithToolResult =\n lastMessage.role === \"user\" &&\n Array.isArray(lastMessage.content) &&\n lastMessage.content.some(\n (content: any) => content.type === \"tool_result\",\n );\n\n // Also check if we have a recent pattern of user message -> assistant tool use -> user tool result\n // This indicates a completed action that might not need a response\n if (messages.length >= 3 && endsWithToolResult) {\n const lastThree = messages.slice(-3);\n const hasRecentToolPattern =\n lastThree[0]?.role === \"user\" && // Initial user message\n lastThree[1]?.role === \"assistant\" && // Assistant tool use\n Array.isArray(lastThree[1].content) &&\n lastThree[1].content.some(\n (content: any) => content.type === \"tool_use\",\n ) &&\n lastThree[2]?.role === \"user\" && // Tool result\n Array.isArray(lastThree[2].content) &&\n lastThree[2].content.some(\n (content: any) => content.type === \"tool_result\",\n );\n\n return hasRecentToolPattern;\n }\n\n return endsWithToolResult;\n }\n\n async process(\n request: CopilotRuntimeChatCompletionRequest,\n ): Promise<CopilotRuntimeChatCompletionResponse> {\n const {\n threadId,\n model = this.model,\n messages: rawMessages,\n actions,\n eventSource,\n forwardedParameters,\n } = request;\n const tools = actions.map(convertActionInputToAnthropicTool);\n\n const messages = [...rawMessages];\n\n // get the instruction message\n const instructionsMessage = messages.shift();\n const instructions = instructionsMessage.isTextMessage()\n ? instructionsMessage.content\n : \"\";\n\n // ALLOWLIST APPROACH:\n // 1. First, identify all valid tool_use calls (from assistant)\n // 2. Then, only keep tool_result blocks that correspond to these valid tool_use IDs\n // 3. Discard any other tool_result blocks\n\n // Step 1: Extract valid tool_use 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: Map each message to an Anthropic message, eliminating invalid tool_results\n const processedToolResultIds = new Set<string>();\n const anthropicMessages = messages\n .map((message) => {\n // For tool results, only include if they match a valid tool_use ID AND haven't been processed\n if (message.isResultMessage()) {\n // Skip if there's no corresponding tool_use\n if (!validToolUseIds.has(message.actionExecutionId)) {\n return null; // Will be filtered out later\n }\n\n // Skip if we've already processed a result for this tool_use ID\n if (processedToolResultIds.has(message.actionExecutionId)) {\n return null; // Will be filtered out later\n }\n\n // Mark this tool result as processed\n processedToolResultIds.add(message.actionExecutionId);\n\n return {\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n content: message.result || \"Action completed successfully\",\n tool_use_id: message.actionExecutionId,\n },\n ],\n };\n }\n\n // For non-tool-result messages, convert normally\n return convertMessageToAnthropicMessage(message);\n })\n .filter(Boolean) // Remove nulls\n .filter((msg) => {\n // Filter out assistant messages with empty text content\n if (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n const hasEmptyTextOnly =\n msg.content.length === 1 &&\n msg.content[0].type === \"text\" &&\n (!(msg.content[0] as any).text ||\n (msg.content[0] as any).text.trim() === \"\");\n\n // Keep messages that have tool_use or non-empty text\n return !hasEmptyTextOnly;\n }\n return true;\n }) as Anthropic.Messages.MessageParam[];\n\n // Apply token limits\n const limitedMessages = limitMessagesToTokenCount(\n anthropicMessages,\n tools,\n model,\n );\n\n // Apply prompt caching if enabled\n const cachedSystemPrompt = this.addSystemPromptCaching(\n instructions,\n this.promptCaching.debug,\n );\n const cachedMessages = this.addIncrementalMessageCaching(\n limitedMessages,\n this.promptCaching.debug,\n );\n\n // We'll check if we need a fallback response after seeing what Anthropic returns\n // We skip grouping by role since we've already ensured uniqueness of tool_results\n\n let toolChoice: any = forwardedParameters?.toolChoice;\n if (forwardedParameters?.toolChoice === \"function\") {\n toolChoice = {\n type: \"tool\",\n name: forwardedParameters.toolChoiceFunctionName,\n };\n }\n\n try {\n const createParams = {\n system: cachedSystemPrompt,\n model: this.model,\n messages: cachedMessages,\n max_tokens: forwardedParameters?.maxTokens || 1024,\n ...(forwardedParameters?.temperature\n ? { temperature: forwardedParameters.temperature }\n : {}),\n ...(tools.length > 0 && { tools }),\n ...(toolChoice && { tool_choice: toolChoice }),\n stream: true,\n };\n\n const anthropic = this.ensureAnthropic();\n const stream = await anthropic.messages.create(createParams);\n\n eventSource.stream(async (eventStream$) => {\n let mode: \"function\" | \"message\" | null = null;\n let didOutputText = false;\n let currentMessageId = randomId();\n let currentToolCallId = randomId();\n let filterThinkingTextBuffer = new FilterThinkingTextBuffer();\n let hasReceivedContent = false;\n\n try {\n for await (const chunk of stream as AsyncIterable<any>) {\n if (chunk.type === \"message_start\") {\n currentMessageId = chunk.message.id;\n } else if (chunk.type === \"content_block_start\") {\n hasReceivedContent = true;\n if (chunk.content_block.type === \"text\") {\n didOutputText = false;\n filterThinkingTextBuffer.reset();\n mode = \"message\";\n } else if (chunk.content_block.type === \"tool_use\") {\n currentToolCallId = chunk.content_block.id;\n eventStream$.sendActionExecutionStart({\n actionExecutionId: currentToolCallId,\n actionName: chunk.content_block.name,\n parentMessageId: currentMessageId,\n });\n mode = \"function\";\n }\n } else if (chunk.type === \"content_block_delta\") {\n if (chunk.delta.type === \"text_delta\") {\n const text = filterThinkingTextBuffer.onTextChunk(\n chunk.delta.text,\n );\n if (text.length > 0) {\n if (!didOutputText) {\n eventStream$.sendTextMessageStart({\n messageId: currentMessageId,\n });\n didOutputText = true;\n }\n eventStream$.sendTextMessageContent({\n messageId: currentMessageId,\n content: text,\n });\n }\n } else if (chunk.delta.type === \"input_json_delta\") {\n eventStream$.sendActionExecutionArgs({\n actionExecutionId: currentToolCallId,\n args: chunk.delta.partial_json,\n });\n }\n } else if (chunk.type === \"content_block_stop\") {\n if (mode === \"message\") {\n if (didOutputText) {\n eventStream$.sendTextMessageEnd({\n messageId: currentMessageId,\n });\n }\n } else if (mode === \"function\") {\n eventStream$.sendActionExecutionEnd({\n actionExecutionId: currentToolCallId,\n });\n }\n }\n }\n } catch (error) {\n throw convertServiceAdapterError(error, \"Anthropic\");\n }\n\n // Generate fallback response only if Anthropic produced no content\n if (\n !hasReceivedContent &&\n this.shouldGenerateFallbackResponse(cachedMessages)\n ) {\n // Extract the tool result content for a more contextual response\n let fallbackContent = \"Task completed successfully.\";\n const lastMessage = cachedMessages[cachedMessages.length - 1];\n if (\n lastMessage?.role === \"user\" &&\n Array.isArray(lastMessage.content)\n ) {\n const toolResult = lastMessage.content.find(\n (c: any) => c.type === \"tool_result\",\n );\n if (\n toolResult?.content &&\n toolResult.content !== \"Action completed successfully\"\n ) {\n fallbackContent = toolResult.content;\n }\n }\n\n currentMessageId = randomId();\n eventStream$.sendTextMessageStart({ messageId: currentMessageId });\n eventStream$.sendTextMessageContent({\n messageId: currentMessageId,\n content: fallbackContent,\n });\n eventStream$.sendTextMessageEnd({ messageId: currentMessageId });\n }\n\n eventStream$.complete();\n });\n } catch (error) {\n throw convertServiceAdapterError(error, \"Anthropic\");\n }\n\n return {\n threadId: threadId || randomUUID(),\n };\n }\n}\n\nconst THINKING_TAG = \"<thinking>\";\nconst THINKING_TAG_END = \"</thinking>\";\n\nclass FilterThinkingTextBuffer {\n private buffer: string;\n private didFilterThinkingTag: boolean = false;\n\n constructor() {\n this.buffer = \"\";\n }\n\n onTextChunk(text: string): string {\n this.buffer += text;\n if (this.didFilterThinkingTag) {\n return text;\n }\n const potentialTag = this.buffer.slice(0, THINKING_TAG.length);\n if (THINKING_TAG.startsWith(potentialTag)) {\n if (this.buffer.includes(THINKING_TAG_END)) {\n const end = this.buffer.indexOf(THINKING_TAG_END);\n const filteredText = this.buffer.slice(end + THINKING_TAG_END.length);\n this.buffer = filteredText;\n this.didFilterThinkingTag = true;\n return filteredText;\n } else {\n return \"\";\n }\n }\n return text;\n }\n\n reset() {\n this.buffer = \"\";\n this.didFilterThinkingTag = false;\n }\n}\n"],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@ require("reflect-metadata");
|
|
|
2
2
|
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
|
|
3
3
|
const require_utils = require('../openai/utils.cjs');
|
|
4
4
|
const require_error_utils = require('../shared/error-utils.cjs');
|
|
5
|
+
const require_sdk_client_utils = require('../shared/sdk-client-utils.cjs');
|
|
6
|
+
let _ai_sdk_openai = require("@ai-sdk/openai");
|
|
5
7
|
let _copilotkit_shared = require("@copilotkit/shared");
|
|
6
8
|
|
|
7
9
|
//#region src/service-adapters/groq/groq-adapter.ts
|
|
@@ -21,6 +23,17 @@ var GroqAdapter = class {
|
|
|
21
23
|
if (params?.model) this.model = params.model;
|
|
22
24
|
this.disableParallelToolCalls = params?.disableParallelToolCalls || false;
|
|
23
25
|
}
|
|
26
|
+
getLanguageModel() {
|
|
27
|
+
const groq = this.ensureGroq();
|
|
28
|
+
const options = require_sdk_client_utils.getSdkClientOptions(groq);
|
|
29
|
+
return (0, _ai_sdk_openai.createOpenAI)({
|
|
30
|
+
baseURL: groq.baseURL,
|
|
31
|
+
apiKey: groq.apiKey,
|
|
32
|
+
headers: options.defaultHeaders,
|
|
33
|
+
fetch: options.fetch,
|
|
34
|
+
name: "groq"
|
|
35
|
+
})(this.model);
|
|
36
|
+
}
|
|
24
37
|
ensureGroq() {
|
|
25
38
|
if (!this._groq) {
|
|
26
39
|
const { Groq } = require("groq-sdk");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groq-adapter.cjs","names":["convertActionInputToOpenAITool","convertMessageToOpenAIMessage","limitMessagesToTokenCount","convertServiceAdapterError"],"sources":["../../../src/service-adapters/groq/groq-adapter.ts"],"sourcesContent":["/**\n * Copilot Runtime adapter for Groq.\n *\n * ## Example\n *\n * ```ts\n * import { CopilotRuntime, GroqAdapter } from \"@copilotkit/runtime\";\n * import { Groq } from \"groq-sdk\";\n *\n * const groq = new Groq({ apiKey: process.env[\"GROQ_API_KEY\"] });\n *\n * const copilotKit = new CopilotRuntime();\n *\n * return new GroqAdapter({ groq, model: \"<model-name>\" });\n * ```\n */\nimport type { Groq } from \"groq-sdk\";\nimport type { ChatCompletionMessageParam } from \"groq-sdk/resources/chat\";\nimport {\n CopilotServiceAdapter,\n CopilotRuntimeChatCompletionRequest,\n CopilotRuntimeChatCompletionResponse,\n} from \"../service-adapter\";\nimport {\n convertActionInputToOpenAITool,\n convertMessageToOpenAIMessage,\n limitMessagesToTokenCount,\n} from \"../openai/utils\";\nimport { randomUUID } from \"@copilotkit/shared\";\nimport { convertServiceAdapterError } from \"../shared\";\n\nconst DEFAULT_MODEL = \"llama-3.3-70b-versatile\";\n\nexport interface GroqAdapterParams {\n /**\n * An optional Groq instance to use.\n */\n groq?: Groq;\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\nexport class GroqAdapter implements CopilotServiceAdapter {\n public model: string = DEFAULT_MODEL;\n public provider = \"groq\";\n\n private disableParallelToolCalls: boolean = false;\n private _groq: Groq;\n public get groq(): Groq {\n return this._groq;\n }\n public get name() {\n return \"GroqAdapter\";\n }\n\n constructor(params?: GroqAdapterParams) {\n if (params?.groq) {\n this._groq = params.groq;\n }\n // If no instance provided, we'll lazy-load in ensureGroq()\n if (params?.model) {\n this.model = params.model;\n }\n this.disableParallelToolCalls = params?.disableParallelToolCalls || false;\n }\n\n private ensureGroq(): Groq {\n if (!this._groq) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { Groq } = require(\"groq-sdk\");\n this._groq = new Groq({});\n }\n return this._groq;\n }\n\n async process(\n request: CopilotRuntimeChatCompletionRequest,\n ): Promise<CopilotRuntimeChatCompletionResponse> {\n const {\n threadId,\n model = this.model,\n messages,\n actions,\n eventSource,\n forwardedParameters,\n } = request;\n const tools = actions.map(convertActionInputToOpenAITool);\n\n let openaiMessages = messages.map((m) =>\n convertMessageToOpenAIMessage(m, { keepSystemRole: true }),\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 let stream;\n try {\n const groq = this.ensureGroq();\n stream = await groq.chat.completions.create({\n model: model,\n stream: true,\n messages: openaiMessages as unknown as ChatCompletionMessageParam[],\n ...(tools.length > 0 && { tools }),\n ...(forwardedParameters?.maxTokens && {\n max_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 } catch (error) {\n throw convertServiceAdapterError(error, \"Groq\");\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 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 actionName: toolCall!.function!.name,\n parentMessageId: chunk.id,\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,\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 throw convertServiceAdapterError(error, \"Groq\");\n }\n\n eventStream$.complete();\n });\n\n return {\n threadId: request.threadId || randomUUID(),\n };\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"groq-adapter.cjs","names":["getSdkClientOptions","convertActionInputToOpenAITool","convertMessageToOpenAIMessage","limitMessagesToTokenCount","convertServiceAdapterError"],"sources":["../../../src/service-adapters/groq/groq-adapter.ts"],"sourcesContent":["/**\n * Copilot Runtime adapter for Groq.\n *\n * ## Example\n *\n * ```ts\n * import { CopilotRuntime, GroqAdapter } from \"@copilotkit/runtime\";\n * import { Groq } from \"groq-sdk\";\n *\n * const groq = new Groq({ apiKey: process.env[\"GROQ_API_KEY\"] });\n *\n * const copilotKit = new CopilotRuntime();\n *\n * return new GroqAdapter({ groq, model: \"<model-name>\" });\n * ```\n */\nimport type { LanguageModel } from \"ai\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\nimport type { Groq } from \"groq-sdk\";\nimport type { ChatCompletionMessageParam } from \"groq-sdk/resources/chat\";\nimport {\n CopilotServiceAdapter,\n CopilotRuntimeChatCompletionRequest,\n CopilotRuntimeChatCompletionResponse,\n} from \"../service-adapter\";\nimport {\n convertActionInputToOpenAITool,\n convertMessageToOpenAIMessage,\n limitMessagesToTokenCount,\n} from \"../openai/utils\";\nimport { randomUUID } from \"@copilotkit/shared\";\nimport { convertServiceAdapterError, getSdkClientOptions } from \"../shared\";\n\nconst DEFAULT_MODEL = \"llama-3.3-70b-versatile\";\n\nexport interface GroqAdapterParams {\n /**\n * An optional Groq instance to use.\n */\n groq?: Groq;\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\nexport class GroqAdapter implements CopilotServiceAdapter {\n public model: string = DEFAULT_MODEL;\n public provider = \"groq\";\n\n private disableParallelToolCalls: boolean = false;\n private _groq: Groq;\n public get groq(): Groq {\n return this._groq;\n }\n public get name() {\n return \"GroqAdapter\";\n }\n\n constructor(params?: GroqAdapterParams) {\n if (params?.groq) {\n this._groq = params.groq;\n }\n // If no instance provided, we'll lazy-load in ensureGroq()\n if (params?.model) {\n this.model = params.model;\n }\n this.disableParallelToolCalls = params?.disableParallelToolCalls || false;\n }\n\n getLanguageModel(): LanguageModel {\n const groq = this.ensureGroq();\n const options = getSdkClientOptions(groq);\n const provider = createOpenAI({\n baseURL: groq.baseURL,\n apiKey: groq.apiKey,\n headers: options.defaultHeaders,\n fetch: options.fetch,\n name: \"groq\",\n });\n return provider(this.model);\n }\n\n private ensureGroq(): Groq {\n if (!this._groq) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { Groq } = require(\"groq-sdk\");\n this._groq = new Groq({});\n }\n return this._groq;\n }\n\n async process(\n request: CopilotRuntimeChatCompletionRequest,\n ): Promise<CopilotRuntimeChatCompletionResponse> {\n const {\n threadId,\n model = this.model,\n messages,\n actions,\n eventSource,\n forwardedParameters,\n } = request;\n const tools = actions.map(convertActionInputToOpenAITool);\n\n let openaiMessages = messages.map((m) =>\n convertMessageToOpenAIMessage(m, { keepSystemRole: true }),\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 let stream;\n try {\n const groq = this.ensureGroq();\n stream = await groq.chat.completions.create({\n model: model,\n stream: true,\n messages: openaiMessages as unknown as ChatCompletionMessageParam[],\n ...(tools.length > 0 && { tools }),\n ...(forwardedParameters?.maxTokens && {\n max_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 } catch (error) {\n throw convertServiceAdapterError(error, \"Groq\");\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 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 actionName: toolCall!.function!.name,\n parentMessageId: chunk.id,\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,\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 throw convertServiceAdapterError(error, \"Groq\");\n }\n\n eventStream$.complete();\n });\n\n return {\n threadId: request.threadId || randomUUID(),\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { CopilotRuntimeChatCompletionRequest, CopilotRuntimeChatCompletionResponse, CopilotServiceAdapter } from "../service-adapter.cjs";
|
|
3
|
+
import { LanguageModel } from "ai";
|
|
3
4
|
import { Groq } from "groq-sdk";
|
|
4
5
|
|
|
5
6
|
//#region src/service-adapters/groq/groq-adapter.d.ts
|
|
@@ -30,6 +31,7 @@ declare class GroqAdapter implements CopilotServiceAdapter {
|
|
|
30
31
|
get groq(): Groq;
|
|
31
32
|
get name(): string;
|
|
32
33
|
constructor(params?: GroqAdapterParams);
|
|
34
|
+
getLanguageModel(): LanguageModel;
|
|
33
35
|
private ensureGroq;
|
|
34
36
|
process(request: CopilotRuntimeChatCompletionRequest): Promise<CopilotRuntimeChatCompletionResponse>;
|
|
35
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groq-adapter.d.cts","names":[],"sources":["../../../src/service-adapters/groq/groq-adapter.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"groq-adapter.d.cts","names":[],"sources":["../../../src/service-adapters/groq/groq-adapter.ts"],"mappings":";;;;;;UAmCiB,iBAAA;EA8CK;;;EA1CpB,IAAA,GAAO,IAAA;EAkB2B;;;EAblC,KAAA;EAcO;;;;;;;;EAJP,wBAAA;AAAA;AAAA,cAGW,WAAA,YAAuB,qBAAA;EAC3B,KAAA;EACA,QAAA;EAAA,QAEC,wBAAA;EAAA,QACA,KAAA;EAAA,IACG,IAAA,CAAA,GAAQ,IAAA;EAAA,IAGR,IAAA,CAAA;cAIC,MAAA,GAAS,iBAAA;EAWrB,gBAAA,CAAA,GAAoB,aAAA;EAAA,QAaZ,UAAA;EASF,OAAA,CACJ,OAAA,EAAS,mCAAA,GACR,OAAA,CAAQ,oCAAA;AAAA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
2
|
import { CopilotRuntimeChatCompletionRequest, CopilotRuntimeChatCompletionResponse, CopilotServiceAdapter } from "../service-adapter.mjs";
|
|
3
3
|
import { Groq } from "groq-sdk";
|
|
4
|
+
import { LanguageModel } from "ai";
|
|
4
5
|
|
|
5
6
|
//#region src/service-adapters/groq/groq-adapter.d.ts
|
|
6
7
|
interface GroqAdapterParams {
|
|
@@ -30,6 +31,7 @@ declare class GroqAdapter implements CopilotServiceAdapter {
|
|
|
30
31
|
get groq(): Groq;
|
|
31
32
|
get name(): string;
|
|
32
33
|
constructor(params?: GroqAdapterParams);
|
|
34
|
+
getLanguageModel(): LanguageModel;
|
|
33
35
|
private ensureGroq;
|
|
34
36
|
process(request: CopilotRuntimeChatCompletionRequest): Promise<CopilotRuntimeChatCompletionResponse>;
|
|
35
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groq-adapter.d.mts","names":[],"sources":["../../../src/service-adapters/groq/groq-adapter.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"groq-adapter.d.mts","names":[],"sources":["../../../src/service-adapters/groq/groq-adapter.ts"],"mappings":";;;;;;UAmCiB,iBAAA;EA8CK;;;EA1CpB,IAAA,GAAO,IAAA;EAkB2B;;;EAblC,KAAA;EAcO;;;;;;;;EAJP,wBAAA;AAAA;AAAA,cAGW,WAAA,YAAuB,qBAAA;EAC3B,KAAA;EACA,QAAA;EAAA,QAEC,wBAAA;EAAA,QACA,KAAA;EAAA,IACG,IAAA,CAAA,GAAQ,IAAA;EAAA,IAGR,IAAA,CAAA;cAIC,MAAA,GAAS,iBAAA;EAWrB,gBAAA,CAAA,GAAoB,aAAA;EAAA,QAaZ,UAAA;EASF,OAAA,CACJ,OAAA,EAAS,mCAAA,GACR,OAAA,CAAQ,oCAAA;AAAA"}
|
|
@@ -2,6 +2,8 @@ import "reflect-metadata";
|
|
|
2
2
|
import { __require } from "../../_virtual/_rolldown/runtime.mjs";
|
|
3
3
|
import { convertActionInputToOpenAITool, convertMessageToOpenAIMessage, limitMessagesToTokenCount } from "../openai/utils.mjs";
|
|
4
4
|
import { convertServiceAdapterError } from "../shared/error-utils.mjs";
|
|
5
|
+
import { getSdkClientOptions } from "../shared/sdk-client-utils.mjs";
|
|
6
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
5
7
|
import { randomUUID } from "@copilotkit/shared";
|
|
6
8
|
|
|
7
9
|
//#region src/service-adapters/groq/groq-adapter.ts
|
|
@@ -21,6 +23,17 @@ var GroqAdapter = class {
|
|
|
21
23
|
if (params?.model) this.model = params.model;
|
|
22
24
|
this.disableParallelToolCalls = params?.disableParallelToolCalls || false;
|
|
23
25
|
}
|
|
26
|
+
getLanguageModel() {
|
|
27
|
+
const groq = this.ensureGroq();
|
|
28
|
+
const options = getSdkClientOptions(groq);
|
|
29
|
+
return createOpenAI({
|
|
30
|
+
baseURL: groq.baseURL,
|
|
31
|
+
apiKey: groq.apiKey,
|
|
32
|
+
headers: options.defaultHeaders,
|
|
33
|
+
fetch: options.fetch,
|
|
34
|
+
name: "groq"
|
|
35
|
+
})(this.model);
|
|
36
|
+
}
|
|
24
37
|
ensureGroq() {
|
|
25
38
|
if (!this._groq) {
|
|
26
39
|
const { Groq } = __require("groq-sdk");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groq-adapter.mjs","names":[],"sources":["../../../src/service-adapters/groq/groq-adapter.ts"],"sourcesContent":["/**\n * Copilot Runtime adapter for Groq.\n *\n * ## Example\n *\n * ```ts\n * import { CopilotRuntime, GroqAdapter } from \"@copilotkit/runtime\";\n * import { Groq } from \"groq-sdk\";\n *\n * const groq = new Groq({ apiKey: process.env[\"GROQ_API_KEY\"] });\n *\n * const copilotKit = new CopilotRuntime();\n *\n * return new GroqAdapter({ groq, model: \"<model-name>\" });\n * ```\n */\nimport type { Groq } from \"groq-sdk\";\nimport type { ChatCompletionMessageParam } from \"groq-sdk/resources/chat\";\nimport {\n CopilotServiceAdapter,\n CopilotRuntimeChatCompletionRequest,\n CopilotRuntimeChatCompletionResponse,\n} from \"../service-adapter\";\nimport {\n convertActionInputToOpenAITool,\n convertMessageToOpenAIMessage,\n limitMessagesToTokenCount,\n} from \"../openai/utils\";\nimport { randomUUID } from \"@copilotkit/shared\";\nimport { convertServiceAdapterError } from \"../shared\";\n\nconst DEFAULT_MODEL = \"llama-3.3-70b-versatile\";\n\nexport interface GroqAdapterParams {\n /**\n * An optional Groq instance to use.\n */\n groq?: Groq;\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\nexport class GroqAdapter implements CopilotServiceAdapter {\n public model: string = DEFAULT_MODEL;\n public provider = \"groq\";\n\n private disableParallelToolCalls: boolean = false;\n private _groq: Groq;\n public get groq(): Groq {\n return this._groq;\n }\n public get name() {\n return \"GroqAdapter\";\n }\n\n constructor(params?: GroqAdapterParams) {\n if (params?.groq) {\n this._groq = params.groq;\n }\n // If no instance provided, we'll lazy-load in ensureGroq()\n if (params?.model) {\n this.model = params.model;\n }\n this.disableParallelToolCalls = params?.disableParallelToolCalls || false;\n }\n\n private ensureGroq(): Groq {\n if (!this._groq) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { Groq } = require(\"groq-sdk\");\n this._groq = new Groq({});\n }\n return this._groq;\n }\n\n async process(\n request: CopilotRuntimeChatCompletionRequest,\n ): Promise<CopilotRuntimeChatCompletionResponse> {\n const {\n threadId,\n model = this.model,\n messages,\n actions,\n eventSource,\n forwardedParameters,\n } = request;\n const tools = actions.map(convertActionInputToOpenAITool);\n\n let openaiMessages = messages.map((m) =>\n convertMessageToOpenAIMessage(m, { keepSystemRole: true }),\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 let stream;\n try {\n const groq = this.ensureGroq();\n stream = await groq.chat.completions.create({\n model: model,\n stream: true,\n messages: openaiMessages as unknown as ChatCompletionMessageParam[],\n ...(tools.length > 0 && { tools }),\n ...(forwardedParameters?.maxTokens && {\n max_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 } catch (error) {\n throw convertServiceAdapterError(error, \"Groq\");\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 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 actionName: toolCall!.function!.name,\n parentMessageId: chunk.id,\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,\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 throw convertServiceAdapterError(error, \"Groq\");\n }\n\n eventStream$.complete();\n });\n\n return {\n threadId: request.threadId || randomUUID(),\n };\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"groq-adapter.mjs","names":[],"sources":["../../../src/service-adapters/groq/groq-adapter.ts"],"sourcesContent":["/**\n * Copilot Runtime adapter for Groq.\n *\n * ## Example\n *\n * ```ts\n * import { CopilotRuntime, GroqAdapter } from \"@copilotkit/runtime\";\n * import { Groq } from \"groq-sdk\";\n *\n * const groq = new Groq({ apiKey: process.env[\"GROQ_API_KEY\"] });\n *\n * const copilotKit = new CopilotRuntime();\n *\n * return new GroqAdapter({ groq, model: \"<model-name>\" });\n * ```\n */\nimport type { LanguageModel } from \"ai\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\nimport type { Groq } from \"groq-sdk\";\nimport type { ChatCompletionMessageParam } from \"groq-sdk/resources/chat\";\nimport {\n CopilotServiceAdapter,\n CopilotRuntimeChatCompletionRequest,\n CopilotRuntimeChatCompletionResponse,\n} from \"../service-adapter\";\nimport {\n convertActionInputToOpenAITool,\n convertMessageToOpenAIMessage,\n limitMessagesToTokenCount,\n} from \"../openai/utils\";\nimport { randomUUID } from \"@copilotkit/shared\";\nimport { convertServiceAdapterError, getSdkClientOptions } from \"../shared\";\n\nconst DEFAULT_MODEL = \"llama-3.3-70b-versatile\";\n\nexport interface GroqAdapterParams {\n /**\n * An optional Groq instance to use.\n */\n groq?: Groq;\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\nexport class GroqAdapter implements CopilotServiceAdapter {\n public model: string = DEFAULT_MODEL;\n public provider = \"groq\";\n\n private disableParallelToolCalls: boolean = false;\n private _groq: Groq;\n public get groq(): Groq {\n return this._groq;\n }\n public get name() {\n return \"GroqAdapter\";\n }\n\n constructor(params?: GroqAdapterParams) {\n if (params?.groq) {\n this._groq = params.groq;\n }\n // If no instance provided, we'll lazy-load in ensureGroq()\n if (params?.model) {\n this.model = params.model;\n }\n this.disableParallelToolCalls = params?.disableParallelToolCalls || false;\n }\n\n getLanguageModel(): LanguageModel {\n const groq = this.ensureGroq();\n const options = getSdkClientOptions(groq);\n const provider = createOpenAI({\n baseURL: groq.baseURL,\n apiKey: groq.apiKey,\n headers: options.defaultHeaders,\n fetch: options.fetch,\n name: \"groq\",\n });\n return provider(this.model);\n }\n\n private ensureGroq(): Groq {\n if (!this._groq) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { Groq } = require(\"groq-sdk\");\n this._groq = new Groq({});\n }\n return this._groq;\n }\n\n async process(\n request: CopilotRuntimeChatCompletionRequest,\n ): Promise<CopilotRuntimeChatCompletionResponse> {\n const {\n threadId,\n model = this.model,\n messages,\n actions,\n eventSource,\n forwardedParameters,\n } = request;\n const tools = actions.map(convertActionInputToOpenAITool);\n\n let openaiMessages = messages.map((m) =>\n convertMessageToOpenAIMessage(m, { keepSystemRole: true }),\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 let stream;\n try {\n const groq = this.ensureGroq();\n stream = await groq.chat.completions.create({\n model: model,\n stream: true,\n messages: openaiMessages as unknown as ChatCompletionMessageParam[],\n ...(tools.length > 0 && { tools }),\n ...(forwardedParameters?.maxTokens && {\n max_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 } catch (error) {\n throw convertServiceAdapterError(error, \"Groq\");\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 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 actionName: toolCall!.function!.name,\n parentMessageId: chunk.id,\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,\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 throw convertServiceAdapterError(error, \"Groq\");\n }\n\n eventStream$.complete();\n });\n\n return {\n threadId: request.threadId || randomUUID(),\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -8,6 +8,7 @@ import { UnifyAdapter, UnifyAdapterParams } from "./unify/unify-adapter.cjs";
|
|
|
8
8
|
import { GroqAdapter, GroqAdapterParams } from "./groq/groq-adapter.cjs";
|
|
9
9
|
import { RemoteChain, RemoteChainParameters } from "./langchain/langserve.cjs";
|
|
10
10
|
import { convertServiceAdapterError } from "./shared/error-utils.cjs";
|
|
11
|
+
import { getSdkClientOptions } from "./shared/sdk-client-utils.cjs";
|
|
11
12
|
import { AnthropicAdapter, AnthropicAdapterParams, AnthropicPromptCachingConfig } from "./anthropic/anthropic-adapter.cjs";
|
|
12
13
|
import { ExperimentalOllamaAdapter } from "./experimental/ollama/ollama-adapter.cjs";
|
|
13
14
|
import { BedrockAdapter, BedrockAdapterParams } from "./bedrock/bedrock-adapter.cjs";
|
|
@@ -8,6 +8,7 @@ import { UnifyAdapter, UnifyAdapterParams } from "./unify/unify-adapter.mjs";
|
|
|
8
8
|
import { GroqAdapter, GroqAdapterParams } from "./groq/groq-adapter.mjs";
|
|
9
9
|
import { RemoteChain, RemoteChainParameters } from "./langchain/langserve.mjs";
|
|
10
10
|
import { convertServiceAdapterError } from "./shared/error-utils.mjs";
|
|
11
|
+
import { getSdkClientOptions } from "./shared/sdk-client-utils.mjs";
|
|
11
12
|
import "./shared/index.mjs";
|
|
12
13
|
import { AnthropicAdapter, AnthropicAdapterParams, AnthropicPromptCachingConfig } from "./anthropic/anthropic-adapter.mjs";
|
|
13
14
|
import { ExperimentalOllamaAdapter } from "./experimental/ollama/ollama-adapter.mjs";
|
|
@@ -2,6 +2,8 @@ require("reflect-metadata");
|
|
|
2
2
|
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
|
|
3
3
|
const require_utils = require('./utils.cjs');
|
|
4
4
|
const require_error_utils = require('../shared/error-utils.cjs');
|
|
5
|
+
const require_sdk_client_utils = require('../shared/sdk-client-utils.cjs');
|
|
6
|
+
let _ai_sdk_openai = require("@ai-sdk/openai");
|
|
5
7
|
let _copilotkit_shared = require("@copilotkit/shared");
|
|
6
8
|
|
|
7
9
|
//#region src/service-adapters/openai/openai-adapter.ts
|
|
@@ -23,6 +25,18 @@ var OpenAIAdapter = class {
|
|
|
23
25
|
this.disableParallelToolCalls = params?.disableParallelToolCalls || false;
|
|
24
26
|
this.keepSystemRole = params?.keepSystemRole ?? false;
|
|
25
27
|
}
|
|
28
|
+
getLanguageModel() {
|
|
29
|
+
const openai = this.ensureOpenAI();
|
|
30
|
+
const options = require_sdk_client_utils.getSdkClientOptions(openai);
|
|
31
|
+
return (0, _ai_sdk_openai.createOpenAI)({
|
|
32
|
+
baseURL: openai.baseURL,
|
|
33
|
+
apiKey: openai.apiKey,
|
|
34
|
+
organization: openai.organization ?? void 0,
|
|
35
|
+
project: openai.project ?? void 0,
|
|
36
|
+
headers: options.defaultHeaders,
|
|
37
|
+
fetch: options.fetch
|
|
38
|
+
})(this.model);
|
|
39
|
+
}
|
|
26
40
|
ensureOpenAI() {
|
|
27
41
|
if (!this._openai) {
|
|
28
42
|
const OpenAI = require("openai").default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-adapter.cjs","names":["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 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 } 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 private ensureOpenAI(): OpenAI {\n if (!this._openai) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const OpenAI = require(\"openai\").default;\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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"}
|
|
1
|
+
{"version":3,"file":"openai-adapter.cjs","names":["getSdkClientOptions","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 {\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 // eslint-disable-next-line @typescript-eslint/no-var-requires\n const OpenAI = require(\"openai\").default;\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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"}
|