@copilotkit/runtime 0.0.0-0.0.0-max-changeset-10101010101010-20260109191632

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 (122) hide show
  1. package/.eslintrc.js +7 -0
  2. package/CHANGELOG.md +2905 -0
  3. package/LICENSE +21 -0
  4. package/README.md +76 -0
  5. package/__snapshots__/schema/schema.graphql +371 -0
  6. package/dist/index.d.ts +1495 -0
  7. package/dist/index.js +5644 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/index.mjs +5601 -0
  10. package/dist/index.mjs.map +1 -0
  11. package/dist/langgraph.d.ts +284 -0
  12. package/dist/langgraph.js +211 -0
  13. package/dist/langgraph.js.map +1 -0
  14. package/dist/langgraph.mjs +206 -0
  15. package/dist/langgraph.mjs.map +1 -0
  16. package/dist/v2/index.d.ts +2 -0
  17. package/dist/v2/index.js +22 -0
  18. package/dist/v2/index.js.map +1 -0
  19. package/dist/v2/index.mjs +5 -0
  20. package/dist/v2/index.mjs.map +1 -0
  21. package/jest.config.js +10 -0
  22. package/package.json +143 -0
  23. package/scripts/generate-gql-schema.ts +13 -0
  24. package/src/agents/langgraph/event-source.ts +329 -0
  25. package/src/agents/langgraph/events.ts +377 -0
  26. package/src/graphql/inputs/action.input.ts +16 -0
  27. package/src/graphql/inputs/agent-session.input.ts +13 -0
  28. package/src/graphql/inputs/agent-state.input.ts +13 -0
  29. package/src/graphql/inputs/cloud-guardrails.input.ts +16 -0
  30. package/src/graphql/inputs/cloud.input.ts +8 -0
  31. package/src/graphql/inputs/context-property.input.ts +10 -0
  32. package/src/graphql/inputs/copilot-context.input.ts +10 -0
  33. package/src/graphql/inputs/custom-property.input.ts +15 -0
  34. package/src/graphql/inputs/extensions.input.ts +21 -0
  35. package/src/graphql/inputs/forwarded-parameters.input.ts +22 -0
  36. package/src/graphql/inputs/frontend.input.ts +14 -0
  37. package/src/graphql/inputs/generate-copilot-response.input.ts +59 -0
  38. package/src/graphql/inputs/load-agent-state.input.ts +10 -0
  39. package/src/graphql/inputs/message.input.ts +110 -0
  40. package/src/graphql/inputs/meta-event.input.ts +18 -0
  41. package/src/graphql/message-conversion/agui-to-gql.test.ts +1263 -0
  42. package/src/graphql/message-conversion/agui-to-gql.ts +333 -0
  43. package/src/graphql/message-conversion/gql-to-agui.test.ts +1580 -0
  44. package/src/graphql/message-conversion/gql-to-agui.ts +278 -0
  45. package/src/graphql/message-conversion/index.ts +2 -0
  46. package/src/graphql/message-conversion/roundtrip-conversion.test.ts +526 -0
  47. package/src/graphql/resolvers/copilot.resolver.ts +708 -0
  48. package/src/graphql/resolvers/state.resolver.ts +27 -0
  49. package/src/graphql/types/agents-response.type.ts +19 -0
  50. package/src/graphql/types/base/index.ts +10 -0
  51. package/src/graphql/types/converted/index.ts +176 -0
  52. package/src/graphql/types/copilot-response.type.ts +138 -0
  53. package/src/graphql/types/enums.ts +38 -0
  54. package/src/graphql/types/extensions-response.type.ts +23 -0
  55. package/src/graphql/types/guardrails-result.type.ts +20 -0
  56. package/src/graphql/types/load-agent-state-response.type.ts +17 -0
  57. package/src/graphql/types/message-status.type.ts +42 -0
  58. package/src/graphql/types/meta-events.type.ts +71 -0
  59. package/src/graphql/types/response-status.type.ts +66 -0
  60. package/src/index.ts +4 -0
  61. package/src/langgraph.ts +1 -0
  62. package/src/lib/cloud/index.ts +4 -0
  63. package/src/lib/error-messages.ts +200 -0
  64. package/src/lib/index.ts +52 -0
  65. package/src/lib/integrations/index.ts +6 -0
  66. package/src/lib/integrations/nest/index.ts +14 -0
  67. package/src/lib/integrations/nextjs/app-router.ts +38 -0
  68. package/src/lib/integrations/nextjs/pages-router.ts +39 -0
  69. package/src/lib/integrations/node-express/index.ts +14 -0
  70. package/src/lib/integrations/node-http/index.ts +143 -0
  71. package/src/lib/integrations/node-http/request-handler.ts +111 -0
  72. package/src/lib/integrations/shared.ts +161 -0
  73. package/src/lib/logger.ts +28 -0
  74. package/src/lib/observability.ts +160 -0
  75. package/src/lib/runtime/__tests__/copilot-runtime-error.test.ts +169 -0
  76. package/src/lib/runtime/__tests__/mcp-tools-utils.test.ts +464 -0
  77. package/src/lib/runtime/agent-integrations/langgraph/agent.ts +209 -0
  78. package/src/lib/runtime/agent-integrations/langgraph/consts.ts +34 -0
  79. package/src/lib/runtime/agent-integrations/langgraph/index.ts +2 -0
  80. package/src/lib/runtime/copilot-runtime.ts +710 -0
  81. package/src/lib/runtime/mcp-tools-utils.ts +254 -0
  82. package/src/lib/runtime/retry-utils.ts +96 -0
  83. package/src/lib/runtime/telemetry-agent-runner.ts +139 -0
  84. package/src/lib/runtime/types.ts +49 -0
  85. package/src/lib/runtime/utils.ts +87 -0
  86. package/src/lib/streaming.ts +202 -0
  87. package/src/lib/telemetry-client.ts +64 -0
  88. package/src/service-adapters/anthropic/anthropic-adapter.ts +452 -0
  89. package/src/service-adapters/anthropic/utils.ts +152 -0
  90. package/src/service-adapters/bedrock/bedrock-adapter.ts +73 -0
  91. package/src/service-adapters/conversion.ts +67 -0
  92. package/src/service-adapters/empty/empty-adapter.ts +38 -0
  93. package/src/service-adapters/events.ts +294 -0
  94. package/src/service-adapters/experimental/ollama/ollama-adapter.ts +84 -0
  95. package/src/service-adapters/google/google-genai-adapter.test.ts +104 -0
  96. package/src/service-adapters/google/google-genai-adapter.ts +88 -0
  97. package/src/service-adapters/groq/groq-adapter.ts +203 -0
  98. package/src/service-adapters/index.ts +18 -0
  99. package/src/service-adapters/langchain/langchain-adapter.ts +111 -0
  100. package/src/service-adapters/langchain/langserve.ts +88 -0
  101. package/src/service-adapters/langchain/types.ts +14 -0
  102. package/src/service-adapters/langchain/utils.ts +313 -0
  103. package/src/service-adapters/openai/openai-adapter.ts +283 -0
  104. package/src/service-adapters/openai/openai-assistant-adapter.ts +344 -0
  105. package/src/service-adapters/openai/utils.ts +199 -0
  106. package/src/service-adapters/service-adapter.ts +41 -0
  107. package/src/service-adapters/shared/error-utils.ts +61 -0
  108. package/src/service-adapters/shared/index.ts +1 -0
  109. package/src/service-adapters/unify/unify-adapter.ts +151 -0
  110. package/src/utils/failed-response-status-reasons.ts +70 -0
  111. package/src/utils/index.ts +1 -0
  112. package/src/v2/index.ts +3 -0
  113. package/tests/global.d.ts +13 -0
  114. package/tests/service-adapters/anthropic/allowlist-approach.test.ts +226 -0
  115. package/tests/service-adapters/anthropic/anthropic-adapter.test.ts +389 -0
  116. package/tests/service-adapters/openai/allowlist-approach.test.ts +238 -0
  117. package/tests/service-adapters/openai/openai-adapter.test.ts +301 -0
  118. package/tests/setup.jest.ts +21 -0
  119. package/tests/tsconfig.json +10 -0
  120. package/tsconfig.json +13 -0
  121. package/tsup.config.ts +20 -0
  122. package/typedoc.json +4 -0
@@ -0,0 +1,452 @@
1
+ /**
2
+ * Copilot Runtime adapter for Anthropic.
3
+ *
4
+ * ## Example
5
+ *
6
+ * ```ts
7
+ * import { CopilotRuntime, AnthropicAdapter } from "@copilotkit/runtime";
8
+ * import Anthropic from "@anthropic-ai/sdk";
9
+ *
10
+ * const copilotKit = new CopilotRuntime();
11
+ *
12
+ * const anthropic = new Anthropic({
13
+ * apiKey: "<your-api-key>",
14
+ * });
15
+ *
16
+ * return new AnthropicAdapter({
17
+ * anthropic,
18
+ * promptCaching: {
19
+ * enabled: true,
20
+ * debug: true
21
+ * }
22
+ * });
23
+ * ```
24
+ */
25
+ import type Anthropic from "@anthropic-ai/sdk";
26
+ import {
27
+ CopilotServiceAdapter,
28
+ CopilotRuntimeChatCompletionRequest,
29
+ CopilotRuntimeChatCompletionResponse,
30
+ } from "../service-adapter";
31
+ import {
32
+ convertActionInputToAnthropicTool,
33
+ convertMessageToAnthropicMessage,
34
+ limitMessagesToTokenCount,
35
+ } from "./utils";
36
+
37
+ import { randomId, randomUUID } from "@copilotkit/shared";
38
+ import { convertServiceAdapterError } from "../shared";
39
+
40
+ const DEFAULT_MODEL = "claude-3-5-sonnet-latest";
41
+
42
+ export interface AnthropicPromptCachingConfig {
43
+ /**
44
+ * Whether to enable prompt caching.
45
+ */
46
+ enabled: boolean;
47
+
48
+ /**
49
+ * Whether to enable debug logging for cache operations.
50
+ */
51
+ debug?: boolean;
52
+ }
53
+
54
+ export interface AnthropicAdapterParams {
55
+ /**
56
+ * An optional Anthropic instance to use. If not provided, a new instance will be
57
+ * created.
58
+ */
59
+ anthropic?: Anthropic;
60
+
61
+ /**
62
+ * The model to use.
63
+ */
64
+ model?: string;
65
+
66
+ /**
67
+ * Configuration for prompt caching.
68
+ * See: https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching
69
+ */
70
+ promptCaching?: AnthropicPromptCachingConfig;
71
+ }
72
+
73
+ export class AnthropicAdapter implements CopilotServiceAdapter {
74
+ public model: string = DEFAULT_MODEL;
75
+ public provider = "anthropic";
76
+ private promptCaching: AnthropicPromptCachingConfig;
77
+
78
+ private _anthropic: Anthropic;
79
+ public get anthropic(): Anthropic {
80
+ return this._anthropic;
81
+ }
82
+ public get name() {
83
+ return "AnthropicAdapter";
84
+ }
85
+
86
+ constructor(params?: AnthropicAdapterParams) {
87
+ if (params?.anthropic) {
88
+ this._anthropic = params.anthropic;
89
+ }
90
+ // If no instance provided, we'll lazy-load in ensureAnthropic()
91
+ if (params?.model) {
92
+ this.model = params.model;
93
+ }
94
+ this.promptCaching = params?.promptCaching || { enabled: false };
95
+ }
96
+
97
+ private ensureAnthropic(): Anthropic {
98
+ if (!this._anthropic) {
99
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
100
+ const Anthropic = require("@anthropic-ai/sdk").default;
101
+ this._anthropic = new Anthropic({});
102
+ }
103
+ return this._anthropic;
104
+ }
105
+
106
+ /**
107
+ * Adds cache control to system prompt
108
+ */
109
+ private addSystemPromptCaching(
110
+ system: string,
111
+ debug: boolean = false,
112
+ ): string | Array<{ type: "text"; text: string; cache_control?: { type: "ephemeral" } }> {
113
+ if (!this.promptCaching.enabled || !system) {
114
+ return system;
115
+ }
116
+
117
+ const originalTextLength = system.length;
118
+
119
+ if (debug) {
120
+ console.log(
121
+ `[ANTHROPIC CACHE DEBUG] Added cache control to system prompt (${originalTextLength} chars).`,
122
+ );
123
+ }
124
+
125
+ return [
126
+ {
127
+ type: "text",
128
+ text: system,
129
+ cache_control: { type: "ephemeral" },
130
+ },
131
+ ];
132
+ }
133
+
134
+ /**
135
+ * Adds cache control to the final message
136
+ */
137
+ private addIncrementalMessageCaching(
138
+ messages: Anthropic.Messages.MessageParam[],
139
+ debug: boolean = false,
140
+ ): any[] {
141
+ if (!this.promptCaching.enabled || messages.length === 0) {
142
+ return messages;
143
+ }
144
+
145
+ const finalMessage = messages[messages.length - 1];
146
+ const messageNumber = messages.length;
147
+
148
+ if (Array.isArray(finalMessage.content) && finalMessage.content.length > 0) {
149
+ const finalBlock = finalMessage.content[finalMessage.content.length - 1];
150
+
151
+ const updatedMessages = [
152
+ ...messages.slice(0, -1),
153
+ {
154
+ ...finalMessage,
155
+ content: [
156
+ ...finalMessage.content.slice(0, -1),
157
+ { ...finalBlock, cache_control: { type: "ephemeral" } } as any,
158
+ ],
159
+ },
160
+ ];
161
+
162
+ if (debug) {
163
+ console.log(
164
+ `[ANTHROPIC CACHE DEBUG] Added cache control to final message (message ${messageNumber}).`,
165
+ );
166
+ }
167
+
168
+ return updatedMessages;
169
+ }
170
+
171
+ return messages;
172
+ }
173
+
174
+ private shouldGenerateFallbackResponse(messages: Anthropic.Messages.MessageParam[]): boolean {
175
+ if (messages.length === 0) return false;
176
+
177
+ const lastMessage = messages[messages.length - 1];
178
+
179
+ // Check if the last message is a tool result
180
+ const endsWithToolResult =
181
+ lastMessage.role === "user" &&
182
+ Array.isArray(lastMessage.content) &&
183
+ lastMessage.content.some((content: any) => content.type === "tool_result");
184
+
185
+ // Also check if we have a recent pattern of user message -> assistant tool use -> user tool result
186
+ // This indicates a completed action that might not need a response
187
+ if (messages.length >= 3 && endsWithToolResult) {
188
+ const lastThree = messages.slice(-3);
189
+ const hasRecentToolPattern =
190
+ lastThree[0]?.role === "user" && // Initial user message
191
+ lastThree[1]?.role === "assistant" && // Assistant tool use
192
+ Array.isArray(lastThree[1].content) &&
193
+ lastThree[1].content.some((content: any) => content.type === "tool_use") &&
194
+ lastThree[2]?.role === "user" && // Tool result
195
+ Array.isArray(lastThree[2].content) &&
196
+ lastThree[2].content.some((content: any) => content.type === "tool_result");
197
+
198
+ return hasRecentToolPattern;
199
+ }
200
+
201
+ return endsWithToolResult;
202
+ }
203
+
204
+ async process(
205
+ request: CopilotRuntimeChatCompletionRequest,
206
+ ): Promise<CopilotRuntimeChatCompletionResponse> {
207
+ const {
208
+ threadId,
209
+ model = this.model,
210
+ messages: rawMessages,
211
+ actions,
212
+ eventSource,
213
+ forwardedParameters,
214
+ } = request;
215
+ const tools = actions.map(convertActionInputToAnthropicTool);
216
+
217
+ const messages = [...rawMessages];
218
+
219
+ // get the instruction message
220
+ const instructionsMessage = messages.shift();
221
+ const instructions = instructionsMessage.isTextMessage() ? instructionsMessage.content : "";
222
+
223
+ // ALLOWLIST APPROACH:
224
+ // 1. First, identify all valid tool_use calls (from assistant)
225
+ // 2. Then, only keep tool_result blocks that correspond to these valid tool_use IDs
226
+ // 3. Discard any other tool_result blocks
227
+
228
+ // Step 1: Extract valid tool_use IDs
229
+ const validToolUseIds = new Set<string>();
230
+
231
+ for (const message of messages) {
232
+ if (message.isActionExecutionMessage()) {
233
+ validToolUseIds.add(message.id);
234
+ }
235
+ }
236
+
237
+ // Step 2: Map each message to an Anthropic message, eliminating invalid tool_results
238
+ const processedToolResultIds = new Set<string>();
239
+ const anthropicMessages = messages
240
+ .map((message) => {
241
+ // For tool results, only include if they match a valid tool_use ID AND haven't been processed
242
+ if (message.isResultMessage()) {
243
+ // Skip if there's no corresponding tool_use
244
+ if (!validToolUseIds.has(message.actionExecutionId)) {
245
+ return null; // Will be filtered out later
246
+ }
247
+
248
+ // Skip if we've already processed a result for this tool_use ID
249
+ if (processedToolResultIds.has(message.actionExecutionId)) {
250
+ return null; // Will be filtered out later
251
+ }
252
+
253
+ // Mark this tool result as processed
254
+ processedToolResultIds.add(message.actionExecutionId);
255
+
256
+ return {
257
+ role: "user",
258
+ content: [
259
+ {
260
+ type: "tool_result",
261
+ content: message.result || "Action completed successfully",
262
+ tool_use_id: message.actionExecutionId,
263
+ },
264
+ ],
265
+ };
266
+ }
267
+
268
+ // For non-tool-result messages, convert normally
269
+ return convertMessageToAnthropicMessage(message);
270
+ })
271
+ .filter(Boolean) // Remove nulls
272
+ .filter((msg) => {
273
+ // Filter out assistant messages with empty text content
274
+ if (msg.role === "assistant" && Array.isArray(msg.content)) {
275
+ const hasEmptyTextOnly =
276
+ msg.content.length === 1 &&
277
+ msg.content[0].type === "text" &&
278
+ (!(msg.content[0] as any).text || (msg.content[0] as any).text.trim() === "");
279
+
280
+ // Keep messages that have tool_use or non-empty text
281
+ return !hasEmptyTextOnly;
282
+ }
283
+ return true;
284
+ }) as Anthropic.Messages.MessageParam[];
285
+
286
+ // Apply token limits
287
+ const limitedMessages = limitMessagesToTokenCount(anthropicMessages, tools, model);
288
+
289
+ // Apply prompt caching if enabled
290
+ const cachedSystemPrompt = this.addSystemPromptCaching(instructions, this.promptCaching.debug);
291
+ const cachedMessages = this.addIncrementalMessageCaching(
292
+ limitedMessages,
293
+ this.promptCaching.debug,
294
+ );
295
+
296
+ // We'll check if we need a fallback response after seeing what Anthropic returns
297
+ // We skip grouping by role since we've already ensured uniqueness of tool_results
298
+
299
+ let toolChoice: any = forwardedParameters?.toolChoice;
300
+ if (forwardedParameters?.toolChoice === "function") {
301
+ toolChoice = {
302
+ type: "tool",
303
+ name: forwardedParameters.toolChoiceFunctionName,
304
+ };
305
+ }
306
+
307
+ try {
308
+ const createParams = {
309
+ system: cachedSystemPrompt,
310
+ model: this.model,
311
+ messages: cachedMessages,
312
+ max_tokens: forwardedParameters?.maxTokens || 1024,
313
+ ...(forwardedParameters?.temperature
314
+ ? { temperature: forwardedParameters.temperature }
315
+ : {}),
316
+ ...(tools.length > 0 && { tools }),
317
+ ...(toolChoice && { tool_choice: toolChoice }),
318
+ stream: true,
319
+ };
320
+
321
+ const anthropic = this.ensureAnthropic();
322
+ const stream = await anthropic.messages.create(createParams);
323
+
324
+ eventSource.stream(async (eventStream$) => {
325
+ let mode: "function" | "message" | null = null;
326
+ let didOutputText = false;
327
+ let currentMessageId = randomId();
328
+ let currentToolCallId = randomId();
329
+ let filterThinkingTextBuffer = new FilterThinkingTextBuffer();
330
+ let hasReceivedContent = false;
331
+
332
+ try {
333
+ for await (const chunk of stream as AsyncIterable<any>) {
334
+ if (chunk.type === "message_start") {
335
+ currentMessageId = chunk.message.id;
336
+ } else if (chunk.type === "content_block_start") {
337
+ hasReceivedContent = true;
338
+ if (chunk.content_block.type === "text") {
339
+ didOutputText = false;
340
+ filterThinkingTextBuffer.reset();
341
+ mode = "message";
342
+ } else if (chunk.content_block.type === "tool_use") {
343
+ currentToolCallId = chunk.content_block.id;
344
+ eventStream$.sendActionExecutionStart({
345
+ actionExecutionId: currentToolCallId,
346
+ actionName: chunk.content_block.name,
347
+ parentMessageId: currentMessageId,
348
+ });
349
+ mode = "function";
350
+ }
351
+ } else if (chunk.type === "content_block_delta") {
352
+ if (chunk.delta.type === "text_delta") {
353
+ const text = filterThinkingTextBuffer.onTextChunk(chunk.delta.text);
354
+ if (text.length > 0) {
355
+ if (!didOutputText) {
356
+ eventStream$.sendTextMessageStart({ messageId: currentMessageId });
357
+ didOutputText = true;
358
+ }
359
+ eventStream$.sendTextMessageContent({
360
+ messageId: currentMessageId,
361
+ content: text,
362
+ });
363
+ }
364
+ } else if (chunk.delta.type === "input_json_delta") {
365
+ eventStream$.sendActionExecutionArgs({
366
+ actionExecutionId: currentToolCallId,
367
+ args: chunk.delta.partial_json,
368
+ });
369
+ }
370
+ } else if (chunk.type === "content_block_stop") {
371
+ if (mode === "message") {
372
+ if (didOutputText) {
373
+ eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
374
+ }
375
+ } else if (mode === "function") {
376
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: currentToolCallId });
377
+ }
378
+ }
379
+ }
380
+ } catch (error) {
381
+ throw convertServiceAdapterError(error, "Anthropic");
382
+ }
383
+
384
+ // Generate fallback response only if Anthropic produced no content
385
+ if (!hasReceivedContent && this.shouldGenerateFallbackResponse(cachedMessages)) {
386
+ // Extract the tool result content for a more contextual response
387
+ let fallbackContent = "Task completed successfully.";
388
+ const lastMessage = cachedMessages[cachedMessages.length - 1];
389
+ if (lastMessage?.role === "user" && Array.isArray(lastMessage.content)) {
390
+ const toolResult = lastMessage.content.find((c: any) => c.type === "tool_result");
391
+ if (toolResult?.content && toolResult.content !== "Action completed successfully") {
392
+ fallbackContent = toolResult.content;
393
+ }
394
+ }
395
+
396
+ currentMessageId = randomId();
397
+ eventStream$.sendTextMessageStart({ messageId: currentMessageId });
398
+ eventStream$.sendTextMessageContent({
399
+ messageId: currentMessageId,
400
+ content: fallbackContent,
401
+ });
402
+ eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
403
+ }
404
+
405
+ eventStream$.complete();
406
+ });
407
+ } catch (error) {
408
+ throw convertServiceAdapterError(error, "Anthropic");
409
+ }
410
+
411
+ return {
412
+ threadId: threadId || randomUUID(),
413
+ };
414
+ }
415
+ }
416
+
417
+ const THINKING_TAG = "<thinking>";
418
+ const THINKING_TAG_END = "</thinking>";
419
+
420
+ class FilterThinkingTextBuffer {
421
+ private buffer: string;
422
+ private didFilterThinkingTag: boolean = false;
423
+
424
+ constructor() {
425
+ this.buffer = "";
426
+ }
427
+
428
+ onTextChunk(text: string): string {
429
+ this.buffer += text;
430
+ if (this.didFilterThinkingTag) {
431
+ return text;
432
+ }
433
+ const potentialTag = this.buffer.slice(0, THINKING_TAG.length);
434
+ if (THINKING_TAG.startsWith(potentialTag)) {
435
+ if (this.buffer.includes(THINKING_TAG_END)) {
436
+ const end = this.buffer.indexOf(THINKING_TAG_END);
437
+ const filteredText = this.buffer.slice(end + THINKING_TAG_END.length);
438
+ this.buffer = filteredText;
439
+ this.didFilterThinkingTag = true;
440
+ return filteredText;
441
+ } else {
442
+ return "";
443
+ }
444
+ }
445
+ return text;
446
+ }
447
+
448
+ reset() {
449
+ this.buffer = "";
450
+ this.didFilterThinkingTag = false;
451
+ }
452
+ }
@@ -0,0 +1,152 @@
1
+ import { Anthropic } from "@anthropic-ai/sdk";
2
+ import { ActionInput } from "../../graphql/inputs/action.input";
3
+ import { Message } from "../../graphql/types/converted";
4
+
5
+ export function limitMessagesToTokenCount(
6
+ messages: any[],
7
+ tools: any[],
8
+ model: string,
9
+ maxTokens?: number,
10
+ ): any[] {
11
+ maxTokens ||= MAX_TOKENS;
12
+
13
+ const result: any[] = [];
14
+ const toolsNumTokens = countToolsTokens(model, tools);
15
+ if (toolsNumTokens > maxTokens) {
16
+ throw new Error(`Too many tokens in function definitions: ${toolsNumTokens} > ${maxTokens}`);
17
+ }
18
+ maxTokens -= toolsNumTokens;
19
+
20
+ for (const message of messages) {
21
+ if (message.role === "system") {
22
+ const numTokens = countMessageTokens(model, message);
23
+ maxTokens -= numTokens;
24
+
25
+ if (maxTokens < 0) {
26
+ throw new Error("Not enough tokens for system message.");
27
+ }
28
+ }
29
+ }
30
+
31
+ let cutoff: boolean = false;
32
+
33
+ const reversedMessages = [...messages].reverse();
34
+ for (const message of reversedMessages) {
35
+ if (message.role === "system") {
36
+ result.unshift(message);
37
+ continue;
38
+ } else if (cutoff) {
39
+ continue;
40
+ }
41
+ let numTokens = countMessageTokens(model, message);
42
+ if (maxTokens < numTokens) {
43
+ cutoff = true;
44
+ continue;
45
+ }
46
+ result.unshift(message);
47
+ maxTokens -= numTokens;
48
+ }
49
+
50
+ return result;
51
+ }
52
+
53
+ const MAX_TOKENS = 128000;
54
+
55
+ function countToolsTokens(model: string, tools: any[]): number {
56
+ if (tools.length === 0) {
57
+ return 0;
58
+ }
59
+ const json = JSON.stringify(tools);
60
+ return countTokens(model, json);
61
+ }
62
+
63
+ function countMessageTokens(model: string, message: any): number {
64
+ return countTokens(model, JSON.stringify(message.content) || "");
65
+ }
66
+
67
+ function countTokens(model: string, text: string): number {
68
+ return text.length / 3;
69
+ }
70
+
71
+ export function convertActionInputToAnthropicTool(action: ActionInput): Anthropic.Messages.Tool {
72
+ return {
73
+ name: action.name,
74
+ description: action.description,
75
+ input_schema: JSON.parse(action.jsonSchema),
76
+ };
77
+ }
78
+
79
+ export function convertMessageToAnthropicMessage(
80
+ message: Message,
81
+ ): Anthropic.Messages.MessageParam {
82
+ if (message.isTextMessage()) {
83
+ if (message.role === "system") {
84
+ return {
85
+ role: "assistant",
86
+ content: [
87
+ { type: "text", text: "THE FOLLOWING MESSAGE IS A SYSTEM MESSAGE: " + message.content },
88
+ ],
89
+ };
90
+ } else {
91
+ return {
92
+ role: message.role === "user" ? "user" : "assistant",
93
+ content: [{ type: "text", text: message.content }],
94
+ };
95
+ }
96
+ } else if (message.isImageMessage()) {
97
+ let mediaType: "image/jpeg" | "image/png" | "image/webp" | "image/gif";
98
+ switch (message.format) {
99
+ case "jpeg":
100
+ mediaType = "image/jpeg";
101
+ break;
102
+ case "png":
103
+ mediaType = "image/png";
104
+ break;
105
+ case "webp":
106
+ mediaType = "image/webp";
107
+ break;
108
+ case "gif":
109
+ mediaType = "image/gif";
110
+ break;
111
+ default:
112
+ throw new Error(`Unsupported image format: ${message.format}`);
113
+ }
114
+
115
+ return {
116
+ role: "user",
117
+ content: [
118
+ {
119
+ type: "image",
120
+ source: {
121
+ type: "base64",
122
+ media_type: mediaType,
123
+ data: message.bytes,
124
+ },
125
+ },
126
+ ],
127
+ };
128
+ } else if (message.isActionExecutionMessage()) {
129
+ return {
130
+ role: "assistant",
131
+ content: [
132
+ {
133
+ id: message.id,
134
+ type: "tool_use",
135
+ input: message.arguments,
136
+ name: message.name,
137
+ },
138
+ ],
139
+ };
140
+ } else if (message.isResultMessage()) {
141
+ return {
142
+ role: "user",
143
+ content: [
144
+ {
145
+ type: "tool_result",
146
+ content: message.result || "Action completed successfully",
147
+ tool_use_id: message.actionExecutionId,
148
+ },
149
+ ],
150
+ };
151
+ }
152
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Copilot Runtime adapter for AWS Bedrock.
3
+ *
4
+ * ## Example
5
+ *
6
+ * ```ts
7
+ * import { CopilotRuntime, BedrockAdapter } from "@copilotkit/runtime";
8
+ *
9
+ * const copilotKit = new CopilotRuntime();
10
+ *
11
+ * return new BedrockAdapter({
12
+ * model: "amazon.nova-lite-v1:0",
13
+ * region: "us-east-1",
14
+ * credentials: {
15
+ * accessKeyId: process.env.AWS_ACCESS_KEY_ID,
16
+ * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
17
+ * }
18
+ * });
19
+ * ```
20
+ */
21
+
22
+ import { LangChainAdapter } from "../langchain/langchain-adapter";
23
+
24
+ export interface BedrockAdapterParams {
25
+ /**
26
+ * AWS Bedrock model ID to use.
27
+ * @default "amazon.nova-lite-v1:0"
28
+ */
29
+ model?: string;
30
+
31
+ /**
32
+ * AWS region where Bedrock is available.
33
+ * @default "us-east-1"
34
+ */
35
+ region?: string;
36
+
37
+ /**
38
+ * AWS credentials for Bedrock access.
39
+ */
40
+ credentials?: {
41
+ accessKeyId?: string;
42
+ secretAccessKey?: string;
43
+ };
44
+ }
45
+
46
+ const DEFAULT_MODEL = "amazon.nova-lite-v1:0";
47
+
48
+ export class BedrockAdapter extends LangChainAdapter {
49
+ public provider = "bedrock";
50
+ public model: string = DEFAULT_MODEL;
51
+ constructor(options?: BedrockAdapterParams) {
52
+ super({
53
+ chainFn: async ({ messages, tools, threadId }) => {
54
+ // Lazy require for optional peer dependency
55
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
56
+ const { ChatBedrockConverse } = require("@langchain/aws");
57
+
58
+ this.model = options?.model ?? "amazon.nova-lite-v1:0";
59
+ const model = new ChatBedrockConverse({
60
+ model: this.model,
61
+ region: options?.region ?? "us-east-1",
62
+ credentials: options?.credentials
63
+ ? {
64
+ accessKeyId: options.credentials.accessKeyId,
65
+ secretAccessKey: options.credentials.secretAccessKey,
66
+ }
67
+ : undefined,
68
+ }).bindTools(tools);
69
+ return model.stream(messages);
70
+ },
71
+ });
72
+ }
73
+ }