@copilotkit/runtime 1.56.0 → 1.56.2

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.
Files changed (71) hide show
  1. package/dist/agent/index.cjs +2 -2
  2. package/dist/agent/index.cjs.map +1 -1
  3. package/dist/agent/index.d.cts.map +1 -1
  4. package/dist/agent/index.d.mts.map +1 -1
  5. package/dist/agent/index.mjs +2 -2
  6. package/dist/agent/index.mjs.map +1 -1
  7. package/dist/lib/integrations/node-http/index.cjs +4 -1
  8. package/dist/lib/integrations/node-http/index.cjs.map +1 -1
  9. package/dist/lib/integrations/node-http/index.d.cts.map +1 -1
  10. package/dist/lib/integrations/node-http/index.d.mts.map +1 -1
  11. package/dist/lib/integrations/node-http/index.mjs +4 -1
  12. package/dist/lib/integrations/node-http/index.mjs.map +1 -1
  13. package/dist/lib/runtime/copilot-runtime.cjs +11 -1
  14. package/dist/lib/runtime/copilot-runtime.cjs.map +1 -1
  15. package/dist/lib/runtime/copilot-runtime.d.cts.map +1 -1
  16. package/dist/lib/runtime/copilot-runtime.d.mts.map +1 -1
  17. package/dist/lib/runtime/copilot-runtime.mjs +11 -1
  18. package/dist/lib/runtime/copilot-runtime.mjs.map +1 -1
  19. package/dist/lib/runtime/mcp-tools-utils.cjs +21 -4
  20. package/dist/lib/runtime/mcp-tools-utils.cjs.map +1 -1
  21. package/dist/lib/runtime/mcp-tools-utils.d.cts.map +1 -1
  22. package/dist/lib/runtime/mcp-tools-utils.d.mts.map +1 -1
  23. package/dist/lib/runtime/mcp-tools-utils.mjs +21 -4
  24. package/dist/lib/runtime/mcp-tools-utils.mjs.map +1 -1
  25. package/dist/package.cjs +1 -1
  26. package/dist/package.mjs +1 -1
  27. package/dist/service-adapters/anthropic/anthropic-adapter.cjs +11 -3
  28. package/dist/service-adapters/anthropic/anthropic-adapter.cjs.map +1 -1
  29. package/dist/service-adapters/anthropic/anthropic-adapter.d.cts +6 -0
  30. package/dist/service-adapters/anthropic/anthropic-adapter.d.cts.map +1 -1
  31. package/dist/service-adapters/anthropic/anthropic-adapter.d.mts +6 -0
  32. package/dist/service-adapters/anthropic/anthropic-adapter.d.mts.map +1 -1
  33. package/dist/service-adapters/anthropic/anthropic-adapter.mjs +11 -3
  34. package/dist/service-adapters/anthropic/anthropic-adapter.mjs.map +1 -1
  35. package/dist/service-adapters/anthropic/utils.cjs +27 -1
  36. package/dist/service-adapters/anthropic/utils.cjs.map +1 -1
  37. package/dist/service-adapters/anthropic/utils.mjs +27 -1
  38. package/dist/service-adapters/anthropic/utils.mjs.map +1 -1
  39. package/dist/service-adapters/langchain/utils.cjs +1 -1
  40. package/dist/service-adapters/langchain/utils.cjs.map +1 -1
  41. package/dist/service-adapters/langchain/utils.mjs +1 -1
  42. package/dist/service-adapters/langchain/utils.mjs.map +1 -1
  43. package/dist/service-adapters/openai/openai-adapter.cjs +2 -1
  44. package/dist/service-adapters/openai/openai-adapter.cjs.map +1 -1
  45. package/dist/service-adapters/openai/openai-adapter.d.cts +6 -0
  46. package/dist/service-adapters/openai/openai-adapter.d.cts.map +1 -1
  47. package/dist/service-adapters/openai/openai-adapter.d.mts +6 -0
  48. package/dist/service-adapters/openai/openai-adapter.d.mts.map +1 -1
  49. package/dist/service-adapters/openai/openai-adapter.mjs +2 -1
  50. package/dist/service-adapters/openai/openai-adapter.mjs.map +1 -1
  51. package/dist/v2/runtime/core/middleware-sse-parser.cjs +5 -2
  52. package/dist/v2/runtime/core/middleware-sse-parser.cjs.map +1 -1
  53. package/dist/v2/runtime/core/middleware-sse-parser.mjs +5 -2
  54. package/dist/v2/runtime/core/middleware-sse-parser.mjs.map +1 -1
  55. package/package.json +2 -2
  56. package/src/agent/__tests__/provider-id-collision.test.ts +195 -0
  57. package/src/agent/index.ts +19 -11
  58. package/src/lib/integrations/node-http/__tests__/request-duck-type.test.ts +66 -0
  59. package/src/lib/integrations/node-http/index.ts +15 -1
  60. package/src/lib/runtime/__tests__/mcp-tools-utils.test.ts +30 -1
  61. package/src/lib/runtime/__tests__/on-after-request.test.ts +122 -0
  62. package/src/lib/runtime/copilot-runtime.ts +16 -3
  63. package/src/lib/runtime/mcp-tools-utils.ts +41 -6
  64. package/src/service-adapters/anthropic/anthropic-adapter.ts +22 -2
  65. package/src/service-adapters/anthropic/utils.ts +60 -1
  66. package/src/service-adapters/langchain/utils.ts +1 -1
  67. package/src/service-adapters/openai/openai-adapter.ts +14 -1
  68. package/src/v2/runtime/__tests__/middleware-sse-parser.test.ts +50 -0
  69. package/src/v2/runtime/core/middleware-sse-parser.ts +12 -2
  70. package/tests/service-adapters/anthropic/anthropic-adapter.test.ts +268 -0
  71. package/tests/service-adapters/anthropic/utils-token-trimming.test.ts +301 -0
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-tools-utils.mjs","names":[],"sources":["../../../src/lib/runtime/mcp-tools-utils.ts"],"sourcesContent":["import { Action, Parameter } from \"@copilotkit/shared\";\n\n/**\n * Represents a tool provided by an MCP server.\n */\nexport interface MCPTool {\n description?: string;\n /** Schema defining parameters, mirroring the MCP structure. */\n schema?: {\n parameters?: {\n properties?: Record<string, any>;\n required?: string[];\n jsonSchema?: Record<string, any>;\n };\n };\n /** The function to call to execute the tool on the MCP server. */\n execute(params: any): Promise<any>;\n}\n\n/**\n * Defines the contract for *any* MCP client implementation the user might provide.\n */\nexport interface MCPClient {\n /** A method that returns a map of tool names to MCPTool objects available from the connected MCP server. */\n tools(): Promise<Record<string, MCPTool>>;\n /** An optional method for cleanup if the underlying client requires explicit disconnection. */\n close?(): Promise<void>;\n}\n\n/**\n * Configuration for connecting to an MCP endpoint.\n */\nexport interface MCPEndpointConfig {\n endpoint: string;\n apiKey?: string;\n}\n\n/**\n * Extracts CopilotKit-compatible parameters from an MCP tool schema.\n * @param toolOrSchema The schema object from an MCPTool or the full MCPTool object.\n * @returns An array of Parameter objects.\n */\nexport function extractParametersFromSchema(\n toolOrSchema?: MCPTool | MCPTool[\"schema\"],\n): Parameter[] {\n const parameters: Parameter[] = [];\n\n // Handle either full tool object or just schema\n const schema =\n \"schema\" in (toolOrSchema || {})\n ? (toolOrSchema as MCPTool).schema\n : (toolOrSchema as MCPTool[\"schema\"]);\n\n const toolParameters = schema?.parameters?.jsonSchema || schema?.parameters;\n const properties = toolParameters?.properties;\n const requiredParams = new Set(toolParameters?.required || []);\n\n if (!properties) {\n return parameters;\n }\n\n for (const paramName in properties) {\n if (Object.prototype.hasOwnProperty.call(properties, paramName)) {\n const paramDef = properties[paramName];\n\n // Enhanced type extraction with support for complex types\n let type = paramDef.type || \"string\";\n let description = paramDef.description || \"\";\n\n // Handle arrays with items\n if (type === \"array\" && paramDef.items) {\n const itemType = paramDef.items.type || \"object\";\n if (itemType === \"object\" && paramDef.items.properties) {\n // For arrays of objects, describe the structure\n const itemProperties = Object.keys(paramDef.items.properties).join(\n \", \",\n );\n description =\n description +\n (description ? \" \" : \"\") +\n `Array of objects with properties: ${itemProperties}`;\n } else {\n // For arrays of primitives\n type = `array<${itemType}>`;\n }\n }\n\n // Handle enums\n if (paramDef.enum && Array.isArray(paramDef.enum)) {\n const enumValues = paramDef.enum.join(\" | \");\n description =\n description +\n (description ? \" \" : \"\") +\n `Allowed values: ${enumValues}`;\n }\n\n // Handle objects with properties\n if (type === \"object\" && paramDef.properties) {\n const objectProperties = Object.keys(paramDef.properties).join(\", \");\n description =\n description +\n (description ? \" \" : \"\") +\n `Object with properties: ${objectProperties}`;\n }\n\n parameters.push({\n name: paramName,\n type: type,\n description: description,\n required: requiredParams.has(paramName),\n });\n }\n }\n\n return parameters;\n}\n\n/**\n * Converts a map of MCPTools into an array of CopilotKit Actions.\n * @param mcpTools A record mapping tool names to MCPTool objects.\n * @param mcpEndpoint The endpoint URL from which these tools were fetched.\n * @returns An array of Action<any> objects.\n */\nexport function convertMCPToolsToActions(\n mcpTools: Record<string, MCPTool>,\n mcpEndpoint: string,\n): Action<any>[] {\n const actions: Action<any>[] = [];\n\n for (const [toolName, tool] of Object.entries(mcpTools)) {\n const parameters = extractParametersFromSchema(tool);\n\n const handler = async (params: any): Promise<any> => {\n try {\n const result = await tool.execute(params);\n // Ensure the result is a string or stringify it, as required by many LLMs.\n // This might need adjustment depending on how different LLMs handle tool results.\n return typeof result === \"string\" ? result : JSON.stringify(result);\n } catch (error) {\n console.error(\n `Error executing MCP tool '${toolName}' from endpoint ${mcpEndpoint}:`,\n error,\n );\n // Re-throw or format the error for the LLM\n throw new Error(\n `Execution failed for MCP tool '${toolName}': ${\n error instanceof Error ? error.message : String(error)\n }`, { cause: error },\n );\n }\n };\n\n actions.push({\n name: toolName,\n description:\n tool.description || `MCP tool: ${toolName} (from ${mcpEndpoint})`,\n parameters: parameters,\n handler: handler,\n // Add metadata for easier identification/debugging\n _isMCPTool: true,\n _mcpEndpoint: mcpEndpoint,\n } as Action<any> & { _isMCPTool: boolean; _mcpEndpoint: string }); // Type assertion for metadata\n }\n\n return actions;\n}\n\n/**\n * Generate better instructions for using MCP tools\n * This is used to enhance the system prompt with tool documentation\n */\nexport function generateMcpToolInstructions(\n toolsMap: Record<string, MCPTool>,\n): string {\n if (!toolsMap || Object.keys(toolsMap).length === 0) {\n return \"\";\n }\n\n const toolEntries = Object.entries(toolsMap);\n\n // Generate documentation for each tool\n const toolsDoc = toolEntries\n .map(([name, tool]) => {\n // Extract schema information if available\n let paramsDoc = \" No parameters required\";\n\n try {\n if (tool.schema && typeof tool.schema === \"object\") {\n const schema = tool.schema as any;\n\n // Extract parameters from JSON Schema - check both schema.parameters.properties and schema.properties\n const toolParameters =\n schema.parameters?.jsonSchema || schema.parameters;\n const properties = toolParameters?.properties || schema.properties;\n const requiredParams =\n toolParameters?.required || schema.required || [];\n\n if (properties) {\n // Build parameter documentation from properties with enhanced type information\n const paramsList = Object.entries(properties).map(\n ([paramName, propSchema]) => {\n const propDetails = propSchema as any;\n const requiredMark = requiredParams.includes(paramName)\n ? \"*\"\n : \"\";\n let typeInfo = propDetails.type || \"any\";\n let description = propDetails.description\n ? ` - ${propDetails.description}`\n : \"\";\n\n // Enhanced type display for complex schemas\n if (typeInfo === \"array\" && propDetails.items) {\n const itemType = propDetails.items.type || \"object\";\n if (itemType === \"object\" && propDetails.items.properties) {\n const itemProps = Object.keys(\n propDetails.items.properties,\n ).join(\", \");\n typeInfo = `array<object>`;\n description =\n description +\n (description ? \" \" : \" - \") +\n `Array of objects with properties: ${itemProps}`;\n } else {\n typeInfo = `array<${itemType}>`;\n }\n }\n\n // Handle enums\n if (propDetails.enum && Array.isArray(propDetails.enum)) {\n const enumValues = propDetails.enum.join(\" | \");\n description =\n description +\n (description ? \" \" : \" - \") +\n `Allowed values: ${enumValues}`;\n }\n\n // Handle objects\n if (typeInfo === \"object\" && propDetails.properties) {\n const objectProps = Object.keys(propDetails.properties).join(\n \", \",\n );\n description =\n description +\n (description ? \" \" : \" - \") +\n `Object with properties: ${objectProps}`;\n }\n\n return ` - ${paramName}${requiredMark} (${typeInfo})${description}`;\n },\n );\n\n if (paramsList.length > 0) {\n paramsDoc = paramsList.join(\"\\n\");\n }\n }\n }\n } catch (e) {\n console.error(`Error parsing schema for tool ${name}:`, e);\n }\n\n return `- ${name}: ${tool.description || \"\"}\n${paramsDoc}`;\n })\n .join(\"\\n\\n\");\n\n return `You have access to the following external tools provided by Model Context Protocol (MCP) servers:\n\n${toolsDoc}\n\nWhen using these tools:\n1. Only provide valid parameters according to their type requirements\n2. Required parameters are marked with *\n3. For array parameters, provide data in the correct array format\n4. For object parameters, include all required nested properties\n5. For enum parameters, use only the allowed values listed\n6. Format API calls correctly with the expected parameter structure\n7. Always check tool responses to determine your next action`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mcp-tools-utils.mjs","names":[],"sources":["../../../src/lib/runtime/mcp-tools-utils.ts"],"sourcesContent":["import { Action, Parameter } from \"@copilotkit/shared\";\n\n/**\n * Represents a tool provided by an MCP server.\n */\nexport interface MCPTool {\n description?: string;\n /** Schema defining parameters, mirroring the MCP structure. */\n schema?: {\n parameters?: {\n properties?: Record<string, any>;\n required?: string[];\n jsonSchema?: Record<string, any>;\n };\n };\n /** The function to call to execute the tool on the MCP server. */\n execute(params: any): Promise<any>;\n}\n\n/**\n * Defines the contract for *any* MCP client implementation the user might provide.\n */\nexport interface MCPClient {\n /** A method that returns a map of tool names to MCPTool objects available from the connected MCP server. */\n tools(): Promise<Record<string, MCPTool>>;\n /** An optional method for cleanup if the underlying client requires explicit disconnection. */\n close?(): Promise<void>;\n}\n\n/**\n * Configuration for connecting to an MCP endpoint.\n */\nexport interface MCPEndpointConfig {\n endpoint: string;\n apiKey?: string;\n}\n\n/**\n * Extracts CopilotKit-compatible parameters from an MCP tool schema.\n * @param toolOrSchema The schema object from an MCPTool or the full MCPTool object.\n * @returns An array of Parameter objects.\n */\nexport function extractParametersFromSchema(\n toolOrSchema?: MCPTool | MCPTool[\"schema\"],\n): Parameter[] {\n const parameters: Parameter[] = [];\n\n // Handle either full tool object or just schema\n const schema =\n \"schema\" in (toolOrSchema || {})\n ? (toolOrSchema as MCPTool).schema\n : (toolOrSchema as MCPTool[\"schema\"]);\n\n const toolParameters = schema?.parameters?.jsonSchema || schema?.parameters;\n const properties = toolParameters?.properties;\n const requiredParams = new Set(toolParameters?.required || []);\n\n if (!properties) {\n return parameters;\n }\n\n for (const paramName in properties) {\n if (Object.prototype.hasOwnProperty.call(properties, paramName)) {\n const paramDef = properties[paramName];\n\n // Enhanced type extraction with support for complex types\n let type = paramDef.type || \"string\";\n let description = paramDef.description || \"\";\n\n // Handle arrays with items\n if (type === \"array\" && paramDef.items) {\n const itemType = paramDef.items.type || \"object\";\n if (itemType === \"object\" && paramDef.items.properties) {\n // For arrays of objects, describe the structure\n const itemProperties = Object.keys(paramDef.items.properties).join(\n \", \",\n );\n description =\n description +\n (description ? \" \" : \"\") +\n `Array of objects with properties: ${itemProperties}`;\n } else {\n // For arrays of primitives\n type = `array<${itemType}>`;\n }\n }\n\n // Handle enums — preserve as structured data for Zod conversion\n let enumValues: string[] | undefined;\n if (paramDef.enum && Array.isArray(paramDef.enum)) {\n enumValues = paramDef.enum.map(String);\n const enumDisplay = enumValues.join(\" | \");\n description =\n description +\n (description ? \" \" : \"\") +\n `Allowed values: ${enumDisplay}`;\n }\n\n // Handle objects with properties — recurse to preserve nested structure\n let attributes: Parameter[] | undefined;\n if (type === \"object\" && paramDef.properties) {\n const objectProperties = Object.keys(paramDef.properties).join(\", \");\n description =\n description +\n (description ? \" \" : \"\") +\n `Object with properties: ${objectProperties}`;\n // Recursively extract nested parameters\n attributes = extractParametersFromSchema({\n parameters: {\n properties: paramDef.properties,\n required: paramDef.required || [],\n },\n });\n }\n\n // Handle object arrays — recurse into item schema\n if (type === \"array\" && paramDef.items?.type === \"object\" && paramDef.items?.properties) {\n attributes = extractParametersFromSchema({\n parameters: {\n properties: paramDef.items.properties,\n required: paramDef.items.required || [],\n },\n });\n }\n\n const param: any = {\n name: paramName,\n type: type,\n description: description,\n required: requiredParams.has(paramName),\n };\n\n // Preserve enum values for string parameters\n if (type === \"string\" && enumValues) {\n param.enum = enumValues;\n }\n\n // Preserve nested attributes for object and object[] types\n if (attributes && attributes.length > 0) {\n param.attributes = attributes;\n if (type === \"array\") {\n param.type = \"object[]\";\n }\n }\n\n parameters.push(param);\n }\n }\n\n return parameters;\n}\n\n/**\n * Converts a map of MCPTools into an array of CopilotKit Actions.\n * @param mcpTools A record mapping tool names to MCPTool objects.\n * @param mcpEndpoint The endpoint URL from which these tools were fetched.\n * @returns An array of Action<any> objects.\n */\nexport function convertMCPToolsToActions(\n mcpTools: Record<string, MCPTool>,\n mcpEndpoint: string,\n): Action<any>[] {\n const actions: Action<any>[] = [];\n\n for (const [toolName, tool] of Object.entries(mcpTools)) {\n const parameters = extractParametersFromSchema(tool);\n\n const handler = async (params: any): Promise<any> => {\n try {\n const result = await tool.execute(params);\n // Ensure the result is a string or stringify it, as required by many LLMs.\n // This might need adjustment depending on how different LLMs handle tool results.\n return typeof result === \"string\" ? result : JSON.stringify(result);\n } catch (error) {\n console.error(\n `Error executing MCP tool '${toolName}' from endpoint ${mcpEndpoint}:`,\n error,\n );\n // Re-throw or format the error for the LLM\n throw new Error(\n `Execution failed for MCP tool '${toolName}': ${\n error instanceof Error ? error.message : String(error)\n }`, { cause: error },\n );\n }\n };\n\n actions.push({\n name: toolName,\n description:\n tool.description || `MCP tool: ${toolName} (from ${mcpEndpoint})`,\n parameters: parameters,\n handler: handler,\n // Add metadata for easier identification/debugging\n _isMCPTool: true,\n _mcpEndpoint: mcpEndpoint,\n } as Action<any> & { _isMCPTool: boolean; _mcpEndpoint: string }); // Type assertion for metadata\n }\n\n return actions;\n}\n\n/**\n * Generate better instructions for using MCP tools\n * This is used to enhance the system prompt with tool documentation\n */\nexport function generateMcpToolInstructions(\n toolsMap: Record<string, MCPTool>,\n): string {\n if (!toolsMap || Object.keys(toolsMap).length === 0) {\n return \"\";\n }\n\n const toolEntries = Object.entries(toolsMap);\n\n // Generate documentation for each tool\n const toolsDoc = toolEntries\n .map(([name, tool]) => {\n // Extract schema information if available\n let paramsDoc = \" No parameters required\";\n\n try {\n if (tool.schema && typeof tool.schema === \"object\") {\n const schema = tool.schema as any;\n\n // Extract parameters from JSON Schema - check both schema.parameters.properties and schema.properties\n const toolParameters =\n schema.parameters?.jsonSchema || schema.parameters;\n const properties = toolParameters?.properties || schema.properties;\n const requiredParams =\n toolParameters?.required || schema.required || [];\n\n if (properties) {\n // Build parameter documentation from properties with enhanced type information\n const paramsList = Object.entries(properties).map(\n ([paramName, propSchema]) => {\n const propDetails = propSchema as any;\n const requiredMark = requiredParams.includes(paramName)\n ? \"*\"\n : \"\";\n let typeInfo = propDetails.type || \"any\";\n let description = propDetails.description\n ? ` - ${propDetails.description}`\n : \"\";\n\n // Enhanced type display for complex schemas\n if (typeInfo === \"array\" && propDetails.items) {\n const itemType = propDetails.items.type || \"object\";\n if (itemType === \"object\" && propDetails.items.properties) {\n const itemProps = Object.keys(\n propDetails.items.properties,\n ).join(\", \");\n typeInfo = `array<object>`;\n description =\n description +\n (description ? \" \" : \" - \") +\n `Array of objects with properties: ${itemProps}`;\n } else {\n typeInfo = `array<${itemType}>`;\n }\n }\n\n // Handle enums\n if (propDetails.enum && Array.isArray(propDetails.enum)) {\n const enumValues = propDetails.enum.join(\" | \");\n description =\n description +\n (description ? \" \" : \" - \") +\n `Allowed values: ${enumValues}`;\n }\n\n // Handle objects\n if (typeInfo === \"object\" && propDetails.properties) {\n const objectProps = Object.keys(propDetails.properties).join(\n \", \",\n );\n description =\n description +\n (description ? \" \" : \" - \") +\n `Object with properties: ${objectProps}`;\n }\n\n return ` - ${paramName}${requiredMark} (${typeInfo})${description}`;\n },\n );\n\n if (paramsList.length > 0) {\n paramsDoc = paramsList.join(\"\\n\");\n }\n }\n }\n } catch (e) {\n console.error(`Error parsing schema for tool ${name}:`, e);\n }\n\n return `- ${name}: ${tool.description || \"\"}\n${paramsDoc}`;\n })\n .join(\"\\n\\n\");\n\n return `You have access to the following external tools provided by Model Context Protocol (MCP) servers:\n\n${toolsDoc}\n\nWhen using these tools:\n1. Only provide valid parameters according to their type requirements\n2. Required parameters are marked with *\n3. For array parameters, provide data in the correct array format\n4. For object parameters, include all required nested properties\n5. For enum parameters, use only the allowed values listed\n6. Format API calls correctly with the expected parameter structure\n7. Always check tool responses to determine your next action`;\n}\n"],"mappings":"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"}
package/dist/package.cjs CHANGED
@@ -5,7 +5,7 @@ const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
5
5
  var require_package = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
6
6
  module.exports = {
7
7
  "name": "@copilotkit/runtime",
8
- "version": "1.56.0",
8
+ "version": "1.56.2",
9
9
  "private": false,
10
10
  "keywords": [
11
11
  "ai",
package/dist/package.mjs CHANGED
@@ -5,7 +5,7 @@ import { __commonJSMin } from "./_virtual/_rolldown/runtime.mjs";
5
5
  var require_package = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6
6
  module.exports = {
7
7
  "name": "@copilotkit/runtime",
8
- "version": "1.56.0",
8
+ "version": "1.56.2",
9
9
  "private": false,
10
10
  "keywords": [
11
11
  "ai",
@@ -21,6 +21,7 @@ var AnthropicAdapter = class {
21
21
  if (params?.anthropic) this._anthropic = params.anthropic;
22
22
  if (params?.model) this.model = params.model;
23
23
  this.promptCaching = params?.promptCaching || { enabled: false };
24
+ this.maxInputTokens = params?.maxInputTokens;
24
25
  }
25
26
  getLanguageModel() {
26
27
  const anthropic = this.ensureAnthropic();
@@ -86,6 +87,7 @@ var AnthropicAdapter = class {
86
87
  async process(request) {
87
88
  const { threadId, model = this.model, messages: rawMessages, actions, eventSource, forwardedParameters } = request;
88
89
  const tools = actions.map(require_utils.convertActionInputToAnthropicTool);
90
+ const knownActionNames = new Set(actions.map((a) => a.name));
89
91
  const messages = [...rawMessages];
90
92
  const instructionsMessage = messages.shift();
91
93
  const instructions = instructionsMessage.isTextMessage() ? instructionsMessage.content : "";
@@ -110,7 +112,7 @@ var AnthropicAdapter = class {
110
112
  }).filter(Boolean).filter((msg) => {
111
113
  if (msg.role === "assistant" && Array.isArray(msg.content)) return !(msg.content.length === 1 && msg.content[0].type === "text" && (!msg.content[0].text || msg.content[0].text.trim() === ""));
112
114
  return true;
113
- }), tools, model);
115
+ }), tools, model, this.maxInputTokens);
114
116
  const cachedSystemPrompt = this.addSystemPromptCaching(instructions, this.promptCaching.debug);
115
117
  const cachedMessages = this.addIncrementalMessageCaching(limitedMessages, this.promptCaching.debug);
116
118
  let toolChoice = forwardedParameters?.toolChoice;
@@ -123,7 +125,7 @@ var AnthropicAdapter = class {
123
125
  system: cachedSystemPrompt,
124
126
  model: this.model,
125
127
  messages: cachedMessages,
126
- max_tokens: forwardedParameters?.maxTokens || 1024,
128
+ max_tokens: forwardedParameters?.maxTokens || 4096,
127
129
  ...forwardedParameters?.temperature ? { temperature: forwardedParameters.temperature } : {},
128
130
  ...tools.length > 0 && { tools },
129
131
  ...toolChoice && { tool_choice: toolChoice },
@@ -140,12 +142,17 @@ var AnthropicAdapter = class {
140
142
  try {
141
143
  for await (const chunk of stream) if (chunk.type === "message_start") currentMessageId = chunk.message.id;
142
144
  else if (chunk.type === "content_block_start") {
143
- hasReceivedContent = true;
144
145
  if (chunk.content_block.type === "text") {
146
+ hasReceivedContent = true;
145
147
  didOutputText = false;
146
148
  filterThinkingTextBuffer.reset();
147
149
  mode = "message";
148
150
  } else if (chunk.content_block.type === "tool_use") {
151
+ if (!knownActionNames.has(chunk.content_block.name)) {
152
+ mode = null;
153
+ continue;
154
+ }
155
+ hasReceivedContent = true;
149
156
  currentToolCallId = chunk.content_block.id;
150
157
  eventStream$.sendActionExecutionStart({
151
158
  actionExecutionId: currentToolCallId,
@@ -155,6 +162,7 @@ var AnthropicAdapter = class {
155
162
  mode = "function";
156
163
  }
157
164
  } else if (chunk.type === "content_block_delta") {
165
+ if (mode === null) continue;
158
166
  if (chunk.delta.type === "text_delta") {
159
167
  const text = filterThinkingTextBuffer.onTextChunk(chunk.delta.text);
160
168
  if (text.length > 0) {
@@ -1 +1 @@
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
+ {"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 /**\n * Optional maximum input token limit. Overrides the default limit\n * used when trimming messages to fit the context window.\n */\n maxInputTokens?: number;\n}\n\nexport class AnthropicAdapter implements CopilotServiceAdapter {\n public model: string = DEFAULT_MODEL;\n public provider = \"anthropic\";\n private promptCaching: AnthropicPromptCachingConfig;\n private maxInputTokens?: number;\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 this.maxInputTokens = params?.maxInputTokens;\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 const knownActionNames = new Set(actions.map((a) => a.name));\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 this.maxInputTokens,\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 || 4096,\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 if (chunk.content_block.type === \"text\") {\n hasReceivedContent = true;\n didOutputText = false;\n filterThinkingTextBuffer.reset();\n mode = \"message\";\n } else if (chunk.content_block.type === \"tool_use\") {\n if (!knownActionNames.has(chunk.content_block.name)) {\n // Unknown tool - skip execution to prevent crashes\n mode = null;\n continue;\n }\n hasReceivedContent = true;\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 (mode === null) {\n // Skip deltas for unknown/skipped content blocks\n continue;\n }\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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"}
@@ -29,11 +29,17 @@ interface AnthropicAdapterParams {
29
29
  * See: https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching
30
30
  */
31
31
  promptCaching?: AnthropicPromptCachingConfig;
32
+ /**
33
+ * Optional maximum input token limit. Overrides the default limit
34
+ * used when trimming messages to fit the context window.
35
+ */
36
+ maxInputTokens?: number;
32
37
  }
33
38
  declare class AnthropicAdapter implements CopilotServiceAdapter {
34
39
  model: string;
35
40
  provider: string;
36
41
  private promptCaching;
42
+ private maxInputTokens?;
37
43
  private _anthropic;
38
44
  get anthropic(): Anthropic;
39
45
  get name(): string;
@@ -1 +1 @@
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
+ {"version":3,"file":"anthropic-adapter.d.cts","names":[],"sources":["../../../src/service-adapters/anthropic/anthropic-adapter.ts"],"mappings":";;;;;;UA2CiB,4BAAA;EAwMJ;;;EApMX,OAAA;EAiC4D;;;EA5B5D,KAAA;AAAA;AAAA,UAGe,sBAAA;EA6BP;;;;EAxBR,SAAA,GAAY,SAAA;;;;EAKZ,KAAA;EAyCoB;;;;EAnCpB,aAAA,GAAgB,4BAAA;EA2KV;;;;EArKN,cAAA;AAAA;AAAA,cAGW,gBAAA,YAA4B,qBAAA;EAChC,KAAA;EACA,QAAA;EAAA,QACC,aAAA;EAAA,QACA,cAAA;EAAA,QAEA,UAAA;EAAA,IACG,SAAA,CAAA,GAAa,SAAA;EAAA,IAGb,IAAA,CAAA;cAIC,MAAA,GAAS,sBAAA;EAYrB,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"}
@@ -29,11 +29,17 @@ interface AnthropicAdapterParams {
29
29
  * See: https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching
30
30
  */
31
31
  promptCaching?: AnthropicPromptCachingConfig;
32
+ /**
33
+ * Optional maximum input token limit. Overrides the default limit
34
+ * used when trimming messages to fit the context window.
35
+ */
36
+ maxInputTokens?: number;
32
37
  }
33
38
  declare class AnthropicAdapter implements CopilotServiceAdapter {
34
39
  model: string;
35
40
  provider: string;
36
41
  private promptCaching;
42
+ private maxInputTokens?;
37
43
  private _anthropic;
38
44
  get anthropic(): Anthropic;
39
45
  get name(): string;
@@ -1 +1 @@
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
+ {"version":3,"file":"anthropic-adapter.d.mts","names":[],"sources":["../../../src/service-adapters/anthropic/anthropic-adapter.ts"],"mappings":";;;;;;UA2CiB,4BAAA;EAwMJ;;;EApMX,OAAA;EAiC4D;;;EA5B5D,KAAA;AAAA;AAAA,UAGe,sBAAA;EA6BP;;;;EAxBR,SAAA,GAAY,SAAA;;;;EAKZ,KAAA;EAyCoB;;;;EAnCpB,aAAA,GAAgB,4BAAA;EA2KV;;;;EArKN,cAAA;AAAA;AAAA,cAGW,gBAAA,YAA4B,qBAAA;EAChC,KAAA;EACA,QAAA;EAAA,QACC,aAAA;EAAA,QACA,cAAA;EAAA,QAEA,UAAA;EAAA,IACG,SAAA,CAAA,GAAa,SAAA;EAAA,IAGb,IAAA,CAAA;cAIC,MAAA,GAAS,sBAAA;EAYrB,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"}
@@ -21,6 +21,7 @@ var AnthropicAdapter = class {
21
21
  if (params?.anthropic) this._anthropic = params.anthropic;
22
22
  if (params?.model) this.model = params.model;
23
23
  this.promptCaching = params?.promptCaching || { enabled: false };
24
+ this.maxInputTokens = params?.maxInputTokens;
24
25
  }
25
26
  getLanguageModel() {
26
27
  const anthropic = this.ensureAnthropic();
@@ -86,6 +87,7 @@ var AnthropicAdapter = class {
86
87
  async process(request) {
87
88
  const { threadId, model = this.model, messages: rawMessages, actions, eventSource, forwardedParameters } = request;
88
89
  const tools = actions.map(convertActionInputToAnthropicTool);
90
+ const knownActionNames = new Set(actions.map((a) => a.name));
89
91
  const messages = [...rawMessages];
90
92
  const instructionsMessage = messages.shift();
91
93
  const instructions = instructionsMessage.isTextMessage() ? instructionsMessage.content : "";
@@ -110,7 +112,7 @@ var AnthropicAdapter = class {
110
112
  }).filter(Boolean).filter((msg) => {
111
113
  if (msg.role === "assistant" && Array.isArray(msg.content)) return !(msg.content.length === 1 && msg.content[0].type === "text" && (!msg.content[0].text || msg.content[0].text.trim() === ""));
112
114
  return true;
113
- }), tools, model);
115
+ }), tools, model, this.maxInputTokens);
114
116
  const cachedSystemPrompt = this.addSystemPromptCaching(instructions, this.promptCaching.debug);
115
117
  const cachedMessages = this.addIncrementalMessageCaching(limitedMessages, this.promptCaching.debug);
116
118
  let toolChoice = forwardedParameters?.toolChoice;
@@ -123,7 +125,7 @@ var AnthropicAdapter = class {
123
125
  system: cachedSystemPrompt,
124
126
  model: this.model,
125
127
  messages: cachedMessages,
126
- max_tokens: forwardedParameters?.maxTokens || 1024,
128
+ max_tokens: forwardedParameters?.maxTokens || 4096,
127
129
  ...forwardedParameters?.temperature ? { temperature: forwardedParameters.temperature } : {},
128
130
  ...tools.length > 0 && { tools },
129
131
  ...toolChoice && { tool_choice: toolChoice },
@@ -140,12 +142,17 @@ var AnthropicAdapter = class {
140
142
  try {
141
143
  for await (const chunk of stream) if (chunk.type === "message_start") currentMessageId = chunk.message.id;
142
144
  else if (chunk.type === "content_block_start") {
143
- hasReceivedContent = true;
144
145
  if (chunk.content_block.type === "text") {
146
+ hasReceivedContent = true;
145
147
  didOutputText = false;
146
148
  filterThinkingTextBuffer.reset();
147
149
  mode = "message";
148
150
  } else if (chunk.content_block.type === "tool_use") {
151
+ if (!knownActionNames.has(chunk.content_block.name)) {
152
+ mode = null;
153
+ continue;
154
+ }
155
+ hasReceivedContent = true;
149
156
  currentToolCallId = chunk.content_block.id;
150
157
  eventStream$.sendActionExecutionStart({
151
158
  actionExecutionId: currentToolCallId,
@@ -155,6 +162,7 @@ var AnthropicAdapter = class {
155
162
  mode = "function";
156
163
  }
157
164
  } else if (chunk.type === "content_block_delta") {
165
+ if (mode === null) continue;
158
166
  if (chunk.delta.type === "text_delta") {
159
167
  const text = filterThinkingTextBuffer.onTextChunk(chunk.delta.text);
160
168
  if (text.length > 0) {
@@ -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 { 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
+ {"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 /**\n * Optional maximum input token limit. Overrides the default limit\n * used when trimming messages to fit the context window.\n */\n maxInputTokens?: number;\n}\n\nexport class AnthropicAdapter implements CopilotServiceAdapter {\n public model: string = DEFAULT_MODEL;\n public provider = \"anthropic\";\n private promptCaching: AnthropicPromptCachingConfig;\n private maxInputTokens?: number;\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 this.maxInputTokens = params?.maxInputTokens;\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 const knownActionNames = new Set(actions.map((a) => a.name));\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 this.maxInputTokens,\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 || 4096,\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 if (chunk.content_block.type === \"text\") {\n hasReceivedContent = true;\n didOutputText = false;\n filterThinkingTextBuffer.reset();\n mode = \"message\";\n } else if (chunk.content_block.type === \"tool_use\") {\n if (!knownActionNames.has(chunk.content_block.name)) {\n // Unknown tool - skip execution to prevent crashes\n mode = null;\n continue;\n }\n hasReceivedContent = true;\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 (mode === null) {\n // Skip deltas for unknown/skipped content blocks\n continue;\n }\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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"}
@@ -27,7 +27,33 @@ function limitMessagesToTokenCount(messages, tools, model, maxTokens) {
27
27
  result.unshift(message);
28
28
  maxTokens -= numTokens;
29
29
  }
30
- return result;
30
+ const toolUseIds = /* @__PURE__ */ new Set();
31
+ for (const msg of result) if (msg.role === "assistant" && Array.isArray(msg.content)) {
32
+ for (const block of msg.content) if (block.type === "tool_use") toolUseIds.add(block.id);
33
+ }
34
+ const toolResultIds = /* @__PURE__ */ new Set();
35
+ for (const msg of result) if (msg.role === "user" && Array.isArray(msg.content)) {
36
+ for (const block of msg.content) if (block.type === "tool_result") toolResultIds.add(block.tool_use_id);
37
+ }
38
+ const filtered = [];
39
+ for (const msg of result) if (msg.role === "user" && Array.isArray(msg.content)) {
40
+ const remaining = msg.content.filter((block) => block.type !== "tool_result" || toolUseIds.has(block.tool_use_id));
41
+ if (remaining.length === 0) continue;
42
+ if (remaining.length !== msg.content.length) filtered.push({
43
+ ...msg,
44
+ content: remaining
45
+ });
46
+ else filtered.push(msg);
47
+ } else if (msg.role === "assistant" && Array.isArray(msg.content)) {
48
+ const remaining = msg.content.filter((block) => block.type !== "tool_use" || toolResultIds.has(block.id));
49
+ if (remaining.length === 0) continue;
50
+ if (remaining.length !== msg.content.length) filtered.push({
51
+ ...msg,
52
+ content: remaining
53
+ });
54
+ else filtered.push(msg);
55
+ } else filtered.push(msg);
56
+ return filtered;
31
57
  }
32
58
  const MAX_TOKENS = 128e3;
33
59
  function countToolsTokens(model, tools) {
@@ -1 +1 @@
1
- {"version":3,"file":"utils.cjs","names":[],"sources":["../../../src/service-adapters/anthropic/utils.ts"],"sourcesContent":["import { Anthropic } from \"@anthropic-ai/sdk\";\nimport { ActionInput } from \"../../graphql/inputs/action.input\";\nimport { Message } from \"../../graphql/types/converted\";\n\nexport function limitMessagesToTokenCount(\n messages: any[],\n tools: any[],\n model: string,\n maxTokens?: number,\n): any[] {\n maxTokens ||= MAX_TOKENS;\n\n const result: any[] = [];\n const toolsNumTokens = countToolsTokens(model, tools);\n if (toolsNumTokens > maxTokens) {\n throw new Error(\n `Too many tokens in function definitions: ${toolsNumTokens} > ${maxTokens}`,\n );\n }\n maxTokens -= toolsNumTokens;\n\n for (const message of messages) {\n if (message.role === \"system\") {\n const numTokens = countMessageTokens(model, message);\n maxTokens -= numTokens;\n\n if (maxTokens < 0) {\n throw new Error(\"Not enough tokens for system message.\");\n }\n }\n }\n\n let cutoff: boolean = false;\n\n const reversedMessages = [...messages].toReversed();\n for (const message of reversedMessages) {\n if (message.role === \"system\") {\n result.unshift(message);\n continue;\n } else if (cutoff) {\n continue;\n }\n let numTokens = countMessageTokens(model, message);\n if (maxTokens < numTokens) {\n cutoff = true;\n continue;\n }\n result.unshift(message);\n maxTokens -= numTokens;\n }\n\n return result;\n}\n\nconst MAX_TOKENS = 128000;\n\nfunction countToolsTokens(model: string, tools: any[]): number {\n if (tools.length === 0) {\n return 0;\n }\n const json = JSON.stringify(tools);\n return countTokens(model, json);\n}\n\nfunction countMessageTokens(model: string, message: any): number {\n return countTokens(model, JSON.stringify(message.content) || \"\");\n}\n\nfunction countTokens(model: string, text: string): number {\n return text.length / 3;\n}\n\nexport function convertActionInputToAnthropicTool(\n action: ActionInput,\n): Anthropic.Messages.Tool {\n return {\n name: action.name,\n description: action.description,\n input_schema: JSON.parse(action.jsonSchema),\n };\n}\n\nexport function convertMessageToAnthropicMessage(\n message: Message,\n): Anthropic.Messages.MessageParam {\n if (message.isTextMessage()) {\n if (message.role === \"system\") {\n return {\n role: \"assistant\",\n content: [\n {\n type: \"text\",\n text:\n \"THE FOLLOWING MESSAGE IS A SYSTEM MESSAGE: \" + message.content,\n },\n ],\n };\n } else {\n return {\n role: message.role === \"user\" ? \"user\" : \"assistant\",\n content: [{ type: \"text\", text: message.content }],\n };\n }\n } else if (message.isImageMessage()) {\n let mediaType: \"image/jpeg\" | \"image/png\" | \"image/webp\" | \"image/gif\";\n switch (message.format) {\n case \"jpeg\":\n mediaType = \"image/jpeg\";\n break;\n case \"png\":\n mediaType = \"image/png\";\n break;\n case \"webp\":\n mediaType = \"image/webp\";\n break;\n case \"gif\":\n mediaType = \"image/gif\";\n break;\n default:\n throw new Error(`Unsupported image format: ${message.format}`);\n }\n\n return {\n role: \"user\",\n content: [\n {\n type: \"image\",\n source: {\n type: \"base64\",\n media_type: mediaType,\n data: message.bytes,\n },\n },\n ],\n };\n } else if (message.isActionExecutionMessage()) {\n return {\n role: \"assistant\",\n content: [\n {\n id: message.id,\n type: \"tool_use\",\n input: message.arguments,\n name: message.name,\n },\n ],\n };\n } else if (message.isResultMessage()) {\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"],"mappings":";;;AAIA,SAAgB,0BACd,UACA,OACA,OACA,WACO;AACP,eAAc;CAEd,MAAM,SAAgB,EAAE;CACxB,MAAM,iBAAiB,iBAAiB,OAAO,MAAM;AACrD,KAAI,iBAAiB,UACnB,OAAM,IAAI,MACR,4CAA4C,eAAe,KAAK,YACjE;AAEH,cAAa;AAEb,MAAK,MAAM,WAAW,SACpB,KAAI,QAAQ,SAAS,UAAU;EAC7B,MAAM,YAAY,mBAAmB,OAAO,QAAQ;AACpD,eAAa;AAEb,MAAI,YAAY,EACd,OAAM,IAAI,MAAM,wCAAwC;;CAK9D,IAAI,SAAkB;CAEtB,MAAM,mBAAmB,CAAC,GAAG,SAAS,CAAC,YAAY;AACnD,MAAK,MAAM,WAAW,kBAAkB;AACtC,MAAI,QAAQ,SAAS,UAAU;AAC7B,UAAO,QAAQ,QAAQ;AACvB;aACS,OACT;EAEF,IAAI,YAAY,mBAAmB,OAAO,QAAQ;AAClD,MAAI,YAAY,WAAW;AACzB,YAAS;AACT;;AAEF,SAAO,QAAQ,QAAQ;AACvB,eAAa;;AAGf,QAAO;;AAGT,MAAM,aAAa;AAEnB,SAAS,iBAAiB,OAAe,OAAsB;AAC7D,KAAI,MAAM,WAAW,EACnB,QAAO;AAGT,QAAO,YAAY,OADN,KAAK,UAAU,MAAM,CACH;;AAGjC,SAAS,mBAAmB,OAAe,SAAsB;AAC/D,QAAO,YAAY,OAAO,KAAK,UAAU,QAAQ,QAAQ,IAAI,GAAG;;AAGlE,SAAS,YAAY,OAAe,MAAsB;AACxD,QAAO,KAAK,SAAS;;AAGvB,SAAgB,kCACd,QACyB;AACzB,QAAO;EACL,MAAM,OAAO;EACb,aAAa,OAAO;EACpB,cAAc,KAAK,MAAM,OAAO,WAAW;EAC5C;;AAGH,SAAgB,iCACd,SACiC;AACjC,KAAI,QAAQ,eAAe,CACzB,KAAI,QAAQ,SAAS,SACnB,QAAO;EACL,MAAM;EACN,SAAS,CACP;GACE,MAAM;GACN,MACE,gDAAgD,QAAQ;GAC3D,CACF;EACF;KAED,QAAO;EACL,MAAM,QAAQ,SAAS,SAAS,SAAS;EACzC,SAAS,CAAC;GAAE,MAAM;GAAQ,MAAM,QAAQ;GAAS,CAAC;EACnD;UAEM,QAAQ,gBAAgB,EAAE;EACnC,IAAI;AACJ,UAAQ,QAAQ,QAAhB;GACE,KAAK;AACH,gBAAY;AACZ;GACF,KAAK;AACH,gBAAY;AACZ;GACF,KAAK;AACH,gBAAY;AACZ;GACF,KAAK;AACH,gBAAY;AACZ;GACF,QACE,OAAM,IAAI,MAAM,6BAA6B,QAAQ,SAAS;;AAGlE,SAAO;GACL,MAAM;GACN,SAAS,CACP;IACE,MAAM;IACN,QAAQ;KACN,MAAM;KACN,YAAY;KACZ,MAAM,QAAQ;KACf;IACF,CACF;GACF;YACQ,QAAQ,0BAA0B,CAC3C,QAAO;EACL,MAAM;EACN,SAAS,CACP;GACE,IAAI,QAAQ;GACZ,MAAM;GACN,OAAO,QAAQ;GACf,MAAM,QAAQ;GACf,CACF;EACF;UACQ,QAAQ,iBAAiB,CAClC,QAAO;EACL,MAAM;EACN,SAAS,CACP;GACE,MAAM;GACN,SAAS,QAAQ,UAAU;GAC3B,aAAa,QAAQ;GACtB,CACF;EACF"}
1
+ {"version":3,"file":"utils.cjs","names":[],"sources":["../../../src/service-adapters/anthropic/utils.ts"],"sourcesContent":["import { Anthropic } from \"@anthropic-ai/sdk\";\nimport { ActionInput } from \"../../graphql/inputs/action.input\";\nimport { Message } from \"../../graphql/types/converted\";\n\nexport function limitMessagesToTokenCount(\n messages: any[],\n tools: any[],\n model: string,\n maxTokens?: number,\n): any[] {\n maxTokens ||= MAX_TOKENS;\n\n const result: any[] = [];\n const toolsNumTokens = countToolsTokens(model, tools);\n if (toolsNumTokens > maxTokens) {\n throw new Error(\n `Too many tokens in function definitions: ${toolsNumTokens} > ${maxTokens}`,\n );\n }\n maxTokens -= toolsNumTokens;\n\n for (const message of messages) {\n if (message.role === \"system\") {\n const numTokens = countMessageTokens(model, message);\n maxTokens -= numTokens;\n\n if (maxTokens < 0) {\n throw new Error(\"Not enough tokens for system message.\");\n }\n }\n }\n\n let cutoff: boolean = false;\n\n const reversedMessages = [...messages].toReversed();\n for (const message of reversedMessages) {\n if (message.role === \"system\") {\n result.unshift(message);\n continue;\n } else if (cutoff) {\n continue;\n }\n let numTokens = countMessageTokens(model, message);\n if (maxTokens < numTokens) {\n cutoff = true;\n continue;\n }\n result.unshift(message);\n maxTokens -= numTokens;\n }\n\n // Post-process: remove orphaned tool_result and tool_use blocks.\n // Token trimming may have removed the assistant message containing tool_use\n // while keeping the user message with tool_result (or vice versa),\n // which Anthropic rejects.\n\n // Collect all tool_use IDs from assistant messages\n const toolUseIds = new Set<string>();\n for (const msg of result) {\n if (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n for (const block of msg.content) {\n if (block.type === \"tool_use\") {\n toolUseIds.add(block.id);\n }\n }\n }\n }\n\n // Collect all tool_result IDs from user messages\n const toolResultIds = new Set<string>();\n for (const msg of result) {\n if (msg.role === \"user\" && Array.isArray(msg.content)) {\n for (const block of msg.content) {\n if (block.type === \"tool_result\") {\n toolResultIds.add(block.tool_use_id);\n }\n }\n }\n }\n\n // Filter orphaned blocks without mutating the original messages\n const filtered: any[] = [];\n for (const msg of result) {\n if (msg.role === \"user\" && Array.isArray(msg.content)) {\n const remaining = msg.content.filter(\n (block: any) =>\n block.type !== \"tool_result\" || toolUseIds.has(block.tool_use_id),\n );\n if (remaining.length === 0) continue;\n if (remaining.length !== msg.content.length) {\n filtered.push({ ...msg, content: remaining });\n } else {\n filtered.push(msg);\n }\n } else if (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n const remaining = msg.content.filter(\n (block: any) =>\n block.type !== \"tool_use\" || toolResultIds.has(block.id),\n );\n if (remaining.length === 0) continue;\n if (remaining.length !== msg.content.length) {\n filtered.push({ ...msg, content: remaining });\n } else {\n filtered.push(msg);\n }\n } else {\n filtered.push(msg);\n }\n }\n\n return filtered;\n}\n\nconst MAX_TOKENS = 128000;\n\nfunction countToolsTokens(model: string, tools: any[]): number {\n if (tools.length === 0) {\n return 0;\n }\n const json = JSON.stringify(tools);\n return countTokens(model, json);\n}\n\nfunction countMessageTokens(model: string, message: any): number {\n return countTokens(model, JSON.stringify(message.content) || \"\");\n}\n\nfunction countTokens(model: string, text: string): number {\n return text.length / 3;\n}\n\nexport function convertActionInputToAnthropicTool(\n action: ActionInput,\n): Anthropic.Messages.Tool {\n return {\n name: action.name,\n description: action.description,\n input_schema: JSON.parse(action.jsonSchema),\n };\n}\n\nexport function convertMessageToAnthropicMessage(\n message: Message,\n): Anthropic.Messages.MessageParam {\n if (message.isTextMessage()) {\n if (message.role === \"system\") {\n return {\n role: \"assistant\",\n content: [\n {\n type: \"text\",\n text:\n \"THE FOLLOWING MESSAGE IS A SYSTEM MESSAGE: \" + message.content,\n },\n ],\n };\n } else {\n return {\n role: message.role === \"user\" ? \"user\" : \"assistant\",\n content: [{ type: \"text\", text: message.content }],\n };\n }\n } else if (message.isImageMessage()) {\n let mediaType: \"image/jpeg\" | \"image/png\" | \"image/webp\" | \"image/gif\";\n switch (message.format) {\n case \"jpeg\":\n mediaType = \"image/jpeg\";\n break;\n case \"png\":\n mediaType = \"image/png\";\n break;\n case \"webp\":\n mediaType = \"image/webp\";\n break;\n case \"gif\":\n mediaType = \"image/gif\";\n break;\n default:\n throw new Error(`Unsupported image format: ${message.format}`);\n }\n\n return {\n role: \"user\",\n content: [\n {\n type: \"image\",\n source: {\n type: \"base64\",\n media_type: mediaType,\n data: message.bytes,\n },\n },\n ],\n };\n } else if (message.isActionExecutionMessage()) {\n return {\n role: \"assistant\",\n content: [\n {\n id: message.id,\n type: \"tool_use\",\n input: message.arguments,\n name: message.name,\n },\n ],\n };\n } else if (message.isResultMessage()) {\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"],"mappings":"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"}
@@ -26,7 +26,33 @@ function limitMessagesToTokenCount(messages, tools, model, maxTokens) {
26
26
  result.unshift(message);
27
27
  maxTokens -= numTokens;
28
28
  }
29
- return result;
29
+ const toolUseIds = /* @__PURE__ */ new Set();
30
+ for (const msg of result) if (msg.role === "assistant" && Array.isArray(msg.content)) {
31
+ for (const block of msg.content) if (block.type === "tool_use") toolUseIds.add(block.id);
32
+ }
33
+ const toolResultIds = /* @__PURE__ */ new Set();
34
+ for (const msg of result) if (msg.role === "user" && Array.isArray(msg.content)) {
35
+ for (const block of msg.content) if (block.type === "tool_result") toolResultIds.add(block.tool_use_id);
36
+ }
37
+ const filtered = [];
38
+ for (const msg of result) if (msg.role === "user" && Array.isArray(msg.content)) {
39
+ const remaining = msg.content.filter((block) => block.type !== "tool_result" || toolUseIds.has(block.tool_use_id));
40
+ if (remaining.length === 0) continue;
41
+ if (remaining.length !== msg.content.length) filtered.push({
42
+ ...msg,
43
+ content: remaining
44
+ });
45
+ else filtered.push(msg);
46
+ } else if (msg.role === "assistant" && Array.isArray(msg.content)) {
47
+ const remaining = msg.content.filter((block) => block.type !== "tool_use" || toolResultIds.has(block.id));
48
+ if (remaining.length === 0) continue;
49
+ if (remaining.length !== msg.content.length) filtered.push({
50
+ ...msg,
51
+ content: remaining
52
+ });
53
+ else filtered.push(msg);
54
+ } else filtered.push(msg);
55
+ return filtered;
30
56
  }
31
57
  const MAX_TOKENS = 128e3;
32
58
  function countToolsTokens(model, tools) {