@copilotkit/runtime 1.9.2-next.10 → 1.9.2-next.3

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 (84) hide show
  1. package/CHANGELOG.md +0 -53
  2. package/dist/{chunk-XWBDEXDA.mjs → chunk-5BIEM2UU.mjs} +3 -4
  3. package/dist/{chunk-XWBDEXDA.mjs.map → chunk-5BIEM2UU.mjs.map} +1 -1
  4. package/dist/{chunk-PMIAGZGS.mjs → chunk-GSYE3DGY.mjs} +2414 -2928
  5. package/dist/chunk-GSYE3DGY.mjs.map +1 -0
  6. package/dist/{chunk-GS7DO47Q.mjs → chunk-IIXJVVTV.mjs} +78 -155
  7. package/dist/chunk-IIXJVVTV.mjs.map +1 -0
  8. package/dist/{chunk-TOBFVWZU.mjs → chunk-MIPAKFI5.mjs} +2 -2
  9. package/dist/{chunk-VBXBFZEL.mjs → chunk-N24X5I3C.mjs} +2 -2
  10. package/dist/{chunk-6RUTA76W.mjs → chunk-WFYPJXWX.mjs} +2 -2
  11. package/dist/{chunk-5OK4GLKL.mjs → chunk-XDBXF3Q6.mjs} +2 -19
  12. package/dist/chunk-XDBXF3Q6.mjs.map +1 -0
  13. package/dist/{groq-adapter-172a2ca4.d.ts → groq-adapter-25a2bd35.d.ts} +1 -1
  14. package/dist/index.d.ts +3 -4
  15. package/dist/index.js +2598 -3250
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.mjs +8 -12
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/lib/index.d.ts +4 -5
  20. package/dist/lib/index.js +2730 -3339
  21. package/dist/lib/index.js.map +1 -1
  22. package/dist/lib/index.mjs +8 -9
  23. package/dist/lib/integrations/index.d.ts +3 -3
  24. package/dist/lib/integrations/index.js +96 -160
  25. package/dist/lib/integrations/index.js.map +1 -1
  26. package/dist/lib/integrations/index.mjs +6 -7
  27. package/dist/lib/integrations/nest/index.d.ts +2 -2
  28. package/dist/lib/integrations/nest/index.js +96 -160
  29. package/dist/lib/integrations/nest/index.js.map +1 -1
  30. package/dist/lib/integrations/nest/index.mjs +4 -5
  31. package/dist/lib/integrations/node-express/index.d.ts +2 -2
  32. package/dist/lib/integrations/node-express/index.js +96 -160
  33. package/dist/lib/integrations/node-express/index.js.map +1 -1
  34. package/dist/lib/integrations/node-express/index.mjs +4 -5
  35. package/dist/lib/integrations/node-http/index.d.ts +2 -2
  36. package/dist/lib/integrations/node-http/index.js +96 -160
  37. package/dist/lib/integrations/node-http/index.js.map +1 -1
  38. package/dist/lib/integrations/node-http/index.mjs +3 -4
  39. package/dist/service-adapters/index.d.ts +4 -6
  40. package/dist/service-adapters/index.js +107 -225
  41. package/dist/service-adapters/index.js.map +1 -1
  42. package/dist/service-adapters/index.mjs +2 -6
  43. package/dist/{shared-bd953ebf.d.ts → shared-e272b15a.d.ts} +5 -45
  44. package/dist/utils/index.d.ts +1 -17
  45. package/dist/utils/index.js +2 -3
  46. package/dist/utils/index.js.map +1 -1
  47. package/dist/utils/index.mjs +1 -1
  48. package/package.json +2 -2
  49. package/src/agents/langgraph/event-source.ts +38 -36
  50. package/src/agents/langgraph/events.ts +1 -19
  51. package/src/graphql/resolvers/copilot.resolver.ts +45 -108
  52. package/src/graphql/resolvers/state.resolver.ts +3 -3
  53. package/src/lib/integrations/shared.ts +0 -43
  54. package/src/lib/runtime/copilot-runtime.ts +83 -412
  55. package/src/lib/runtime/langgraph/langgraph-agent.ts +0 -12
  56. package/src/lib/runtime/remote-action-constructors.ts +3 -28
  57. package/src/lib/runtime/remote-lg-action.ts +40 -130
  58. package/src/lib/streaming.ts +36 -125
  59. package/src/service-adapters/anthropic/anthropic-adapter.ts +8 -67
  60. package/src/service-adapters/anthropic/utils.ts +8 -3
  61. package/src/service-adapters/events.ts +81 -37
  62. package/src/service-adapters/groq/groq-adapter.ts +56 -66
  63. package/src/service-adapters/index.ts +0 -1
  64. package/src/service-adapters/openai/openai-adapter.ts +3 -18
  65. package/src/utils/failed-response-status-reasons.ts +1 -23
  66. package/tests/service-adapters/anthropic/anthropic-adapter.test.ts +387 -172
  67. package/dist/chunk-5OK4GLKL.mjs.map +0 -1
  68. package/dist/chunk-AMUJQ6IR.mjs +0 -50
  69. package/dist/chunk-AMUJQ6IR.mjs.map +0 -1
  70. package/dist/chunk-GS7DO47Q.mjs.map +0 -1
  71. package/dist/chunk-PMIAGZGS.mjs.map +0 -1
  72. package/dist/service-adapters/shared/index.d.ts +0 -9
  73. package/dist/service-adapters/shared/index.js +0 -72
  74. package/dist/service-adapters/shared/index.js.map +0 -1
  75. package/dist/service-adapters/shared/index.mjs +0 -8
  76. package/dist/service-adapters/shared/index.mjs.map +0 -1
  77. package/src/lib/error-messages.ts +0 -200
  78. package/src/lib/runtime/__tests__/copilot-runtime-trace.test.ts +0 -169
  79. package/src/service-adapters/shared/error-utils.ts +0 -61
  80. package/src/service-adapters/shared/index.ts +0 -1
  81. package/dist/{chunk-TOBFVWZU.mjs.map → chunk-MIPAKFI5.mjs.map} +0 -0
  82. package/dist/{chunk-VBXBFZEL.mjs.map → chunk-N24X5I3C.mjs.map} +0 -0
  83. package/dist/{chunk-6RUTA76W.mjs.map → chunk-WFYPJXWX.mjs.map} +0 -0
  84. package/dist/{langserve-fc5cac89.d.ts → langserve-4a5c9217.d.ts} +7 -7
@@ -22,9 +22,6 @@ import { parseJson, tryMap } from "@copilotkit/shared";
22
22
  import { ActionInput } from "../../graphql/inputs/action.input";
23
23
  import { fetchWithRetry } from "./retry-utils";
24
24
 
25
- // Import the utility function from remote-lg-action
26
- import { isUserConfigurationError } from "./remote-lg-action";
27
-
28
25
  export function constructLGCRemoteAction({
29
26
  endpoint,
30
27
  graphqlContext,
@@ -96,33 +93,11 @@ export function constructLGCRemoteAction({
96
93
  writeJsonLineResponseToEventStream(response, eventSource.eventStream$);
97
94
  return eventSource.processLangGraphEvents();
98
95
  } catch (error) {
99
- // Preserve structured CopilotKit errors with semantic information
100
- if (error instanceof CopilotKitError || error instanceof CopilotKitLowLevelError) {
101
- // Distinguish between user errors and system errors for logging
102
- if (isUserConfigurationError(error)) {
103
- logger.debug(
104
- { url: endpoint.deploymentUrl, error: error.message, code: error.code },
105
- "User configuration error in LangGraph Platform agent",
106
- );
107
- } else {
108
- logger.error(
109
- { url: endpoint.deploymentUrl, error: error.message, type: error.constructor.name },
110
- "LangGraph Platform agent error",
111
- );
112
- }
113
- throw error; // Re-throw the structured error to preserve semantic information
114
- }
115
-
116
- // For other errors, log and wrap them
117
96
  logger.error(
118
97
  { url: endpoint.deploymentUrl, status: 500, body: error.message },
119
98
  "Failed to execute LangGraph Platform agent",
120
99
  );
121
- throw new CopilotKitLowLevelError({
122
- error: error instanceof Error ? error : new Error(String(error)),
123
- url: endpoint.deploymentUrl,
124
- message: "Failed to execute LangGraph Platform agent",
125
- });
100
+ throw new Error("Failed to execute LangGraph Platform agent");
126
101
  }
127
102
  },
128
103
  }));
@@ -205,7 +180,7 @@ export function constructRemoteActions({
205
180
  logger.debug({ actionName: action.name, result }, "Executed remote action");
206
181
  return result;
207
182
  } catch (error) {
208
- if (error instanceof CopilotKitError || error instanceof CopilotKitLowLevelError) {
183
+ if (error instanceof CopilotKitError) {
209
184
  throw error;
210
185
  }
211
186
  throw new CopilotKitLowLevelError({ error, url: fetchUrl });
@@ -300,7 +275,7 @@ export function constructRemoteActions({
300
275
  throw new Error("Unsupported agent type");
301
276
  }
302
277
  } catch (error) {
303
- if (error instanceof CopilotKitError || error instanceof CopilotKitLowLevelError) {
278
+ if (error instanceof CopilotKitError) {
304
279
  throw error;
305
280
  }
306
281
  throw new CopilotKitLowLevelError({ error, url: fetchUrl });
@@ -2,9 +2,7 @@ import {
2
2
  Client as LangGraphClient,
3
3
  EventsStreamEvent,
4
4
  GraphSchema,
5
- Interrupt,
6
5
  StreamMode,
7
- ThreadState,
8
6
  } from "@langchain/langgraph-sdk";
9
7
  import { createHash } from "node:crypto";
10
8
  import { isValidUUID, randomUUID } from "@copilotkit/shared";
@@ -19,28 +17,9 @@ import { CustomEventNames, LangGraphEventTypes } from "../../agents/langgraph/ev
19
17
  import telemetry from "../telemetry-client";
20
18
  import { MetaEventInput } from "../../graphql/inputs/meta-event.input";
21
19
  import { MetaEventName } from "../../graphql/types/meta-events.type";
22
- import {
23
- parseJson,
24
- CopilotKitMisuseError,
25
- CopilotKitLowLevelError,
26
- CopilotKitError,
27
- } from "@copilotkit/shared";
20
+ import { parseJson, CopilotKitMisuseError } from "@copilotkit/shared";
28
21
  import { RemoveMessage } from "@langchain/core/messages";
29
22
  import { RETRY_CONFIG, isRetryableError, sleep, calculateDelay } from "./retry-utils";
30
- import { generateHelpfulErrorMessage } from "../streaming";
31
-
32
- // Utility to determine if an error is a user configuration issue vs system error
33
- export function isUserConfigurationError(error: any): boolean {
34
- return (
35
- (error instanceof CopilotKitError || error instanceof CopilotKitLowLevelError) &&
36
- (error.code === "NETWORK_ERROR" ||
37
- error.code === "AUTHENTICATION_ERROR" ||
38
- error.statusCode === 401 ||
39
- error.statusCode === 403 ||
40
- error.message?.toLowerCase().includes("authentication") ||
41
- error.message?.toLowerCase().includes("api key"))
42
- );
43
- }
44
23
 
45
24
  type State = Record<string, any>;
46
25
 
@@ -105,6 +84,8 @@ type SchemaKeys = {
105
84
  config: string[] | null;
106
85
  } | null;
107
86
 
87
+ let activeInterruptEvent = false;
88
+
108
89
  export async function execute(args: ExecutionArgs): Promise<ReadableStream<Uint8Array>> {
109
90
  return new ReadableStream({
110
91
  async start(controller) {
@@ -147,15 +128,6 @@ export async function execute(args: ExecutionArgs): Promise<ReadableStream<Uint8
147
128
  See more: https://docs.copilotkit.ai/troubleshooting/common-issues`,
148
129
  });
149
130
  } else {
150
- // Preserve already structured CopilotKit errors with semantic information
151
- if (
152
- lastError instanceof CopilotKitError ||
153
- lastError instanceof CopilotKitLowLevelError ||
154
- (lastError instanceof Error && lastError.name && lastError.name.includes("CopilotKit"))
155
- ) {
156
- throw lastError; // Re-throw to preserve semantic information and visibility settings
157
- }
158
-
159
131
  throw new CopilotKitMisuseError({
160
132
  message: `
161
133
  The LangGraph client threw unhandled error ${lastError}.
@@ -216,7 +188,7 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
216
188
  await client.threads.create({ threadId });
217
189
  }
218
190
 
219
- let agentState = { values: {} } as ThreadState;
191
+ let agentState = { values: {} };
220
192
  if (wasInitiatedWithExistingThread) {
221
193
  agentState = await client.threads.getState(threadId);
222
194
  }
@@ -246,14 +218,16 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
246
218
  const lgInterruptMetaEvent = metaEvents?.find(
247
219
  (ev) => ev.name === MetaEventName.LangGraphInterruptEvent,
248
220
  );
249
-
221
+ if (activeInterruptEvent && !lgInterruptMetaEvent) {
222
+ // state.messages includes only messages that were not processed by the agent, which are the interrupt messages
223
+ payload.command = { resume: state.messages };
224
+ }
250
225
  if (lgInterruptMetaEvent?.response) {
251
226
  let response = lgInterruptMetaEvent.response;
252
227
  payload.command = { resume: parseJson(response, response) };
253
228
  }
254
229
 
255
- const interrupts = (agentState.tasks?.[0]?.interrupts ?? []) as Interrupt[];
256
- if (mode === "continue" && !interrupts.length) {
230
+ if (mode === "continue" && !activeInterruptEvent) {
257
231
  await client.threads.updateState(threadId, { values: state, asNode: nodeName });
258
232
  }
259
233
 
@@ -337,30 +311,17 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
337
311
  let shouldExit = false;
338
312
  let externalRunId = null;
339
313
 
340
- const emit = (message: string) => controller.enqueue(new TextEncoder().encode(message));
341
-
342
- // If there are still outstanding unresolved interrupts, we must force resolution of them before moving forward
343
- if (interrupts?.length && !payload.command?.resume) {
344
- // If the interrupt is "by message" we assume the upcoming user message is a resoluton for the interrupt
345
- if (!lgInterruptMetaEvent) {
346
- // state.messages includes only messages that were not processed by the agent, which are the interrupt messages
347
- payload.command = { resume: state.messages };
348
- } else {
349
- interrupts.forEach((interrupt) => {
350
- emitInterrupt(interrupt.value, emit);
351
- });
352
- return Promise.resolve();
353
- }
354
- }
355
-
356
314
  const streamResponse = client.runs.stream(threadId, assistantId, payload);
357
315
 
316
+ const emit = (message: string) => controller.enqueue(new TextEncoder().encode(message));
317
+
358
318
  let latestStateValues = {};
359
319
  let updatedState = state;
360
320
  // If a manual emittance happens, it is the ultimate source of truth of state, unless a node has exited.
361
321
  // Therefore, this value should either hold null, or the only edition of state that should be used.
362
322
  let manuallyEmittedState = null;
363
323
 
324
+ activeInterruptEvent = false;
364
325
  try {
365
326
  telemetry.capture("oss.runtime.agent_execution_stream_started", {
366
327
  hashedLgcKey: streamInfo.hashedLgcKey,
@@ -369,43 +330,7 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
369
330
  if (!["events", "values", "error", "updates"].includes(streamResponseChunk.event)) continue;
370
331
 
371
332
  if (streamResponseChunk.event === "error") {
372
- const errorData = streamResponseChunk.data;
373
-
374
- // Check if this is a structured error from our Python agent
375
- if (errorData && typeof errorData === "object" && "error_details" in errorData) {
376
- const errorDetails = (errorData as any).error_details;
377
-
378
- // Create a structured error with preserved semantic information
379
- const preservedError = new CopilotKitLowLevelError({
380
- error: new Error(errorDetails.message),
381
- url: "langgraph platform agent",
382
- message: `${errorDetails.type}: ${errorDetails.message}`,
383
- });
384
-
385
- // Add additional error context
386
- if (errorDetails.status_code) {
387
- (preservedError as any).statusCode = errorDetails.status_code;
388
- }
389
- if (errorDetails.response_data) {
390
- (preservedError as any).responseData = errorDetails.response_data;
391
- }
392
- (preservedError as any).agentName = errorDetails.agent_name;
393
- (preservedError as any).originalErrorType = errorDetails.type;
394
-
395
- throw preservedError;
396
- }
397
-
398
- // Fallback for generic error messages
399
- const helpfulMessage = generateHelpfulErrorMessage(
400
- new Error(errorData.message),
401
- "LangGraph Platform agent",
402
- );
403
-
404
- throw new CopilotKitLowLevelError({
405
- error: new Error(errorData.message),
406
- url: "langgraph platform agent",
407
- message: helpfulMessage,
408
- });
333
+ throw new Error(`Error event thrown: ${streamResponseChunk.data.message}`);
409
334
  }
410
335
 
411
336
  // Force event type, as data is not properly defined on the LG side.
@@ -420,8 +345,33 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
420
345
 
421
346
  const interruptEvents = chunk.data.__interrupt__;
422
347
  if (interruptEvents?.length) {
348
+ activeInterruptEvent = true;
423
349
  const interruptValue = interruptEvents?.[0].value;
424
- emitInterrupt(interruptValue, emit);
350
+ if (
351
+ typeof interruptValue != "string" &&
352
+ "__copilotkit_interrupt_value__" in interruptValue
353
+ ) {
354
+ const evValue = interruptValue.__copilotkit_interrupt_value__;
355
+ emit(
356
+ JSON.stringify({
357
+ event: LangGraphEventTypes.OnCopilotKitInterrupt,
358
+ data: {
359
+ value: typeof evValue === "string" ? evValue : JSON.stringify(evValue),
360
+ messages: langchainMessagesToCopilotKit(interruptValue.__copilotkit_messages__),
361
+ },
362
+ }) + "\n",
363
+ );
364
+ } else {
365
+ emit(
366
+ JSON.stringify({
367
+ event: LangGraphEventTypes.OnInterrupt,
368
+ value:
369
+ typeof interruptValue === "string"
370
+ ? interruptValue
371
+ : JSON.stringify(interruptValue),
372
+ }) + "\n",
373
+ );
374
+ }
425
375
  continue;
426
376
  }
427
377
  if (streamResponseChunk.event === "updates") continue;
@@ -571,29 +521,11 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
571
521
 
572
522
  return Promise.resolve();
573
523
  } catch (e) {
574
- // Distinguish between user errors and system errors for logging
575
- if (isUserConfigurationError(e)) {
576
- // Log user errors at debug level to reduce noise
577
- logger.debug({ error: e.message, code: e.code }, "User configuration error");
578
- } else {
579
- // Log actual system errors at error level
580
- logger.error(e);
581
- }
582
-
524
+ logger.error(e);
583
525
  telemetry.capture("oss.runtime.agent_execution_stream_errored", {
584
526
  ...streamInfo,
585
527
  error: e.message,
586
528
  });
587
-
588
- // Re-throw CopilotKit errors so they can be handled properly at higher levels
589
- if (
590
- e instanceof CopilotKitError ||
591
- e instanceof CopilotKitLowLevelError ||
592
- (e instanceof Error && e.name && e.name.includes("CopilotKit"))
593
- ) {
594
- throw e;
595
- }
596
-
597
529
  return Promise.resolve();
598
530
  }
599
531
  }
@@ -977,25 +909,3 @@ function getSchemaKeys(graphSchema: GraphSchema): SchemaKeys {
977
909
  function filterObjectBySchemaKeys(obj: Record<string, any>, schemaKeys: string[]) {
978
910
  return Object.fromEntries(Object.entries(obj).filter(([key]) => schemaKeys.includes(key)));
979
911
  }
980
-
981
- function emitInterrupt(interruptValue: any, emit: (data: string) => void) {
982
- if (typeof interruptValue != "string" && "__copilotkit_interrupt_value__" in interruptValue) {
983
- const evValue = interruptValue.__copilotkit_interrupt_value__;
984
- emit(
985
- JSON.stringify({
986
- event: LangGraphEventTypes.OnCopilotKitInterrupt,
987
- data: {
988
- value: typeof evValue === "string" ? evValue : JSON.stringify(evValue),
989
- messages: langchainMessagesToCopilotKit(interruptValue.__copilotkit_messages__),
990
- },
991
- }) + "\n",
992
- );
993
- } else {
994
- emit(
995
- JSON.stringify({
996
- event: LangGraphEventTypes.OnInterrupt,
997
- value: typeof interruptValue === "string" ? interruptValue : JSON.stringify(interruptValue),
998
- }) + "\n",
999
- );
1000
- }
1001
- }
@@ -1,11 +1,5 @@
1
1
  import { ReplaySubject } from "rxjs";
2
- import {
3
- CopilotKitLowLevelError,
4
- CopilotKitError,
5
- CopilotKitErrorCode,
6
- ensureStructuredError,
7
- } from "@copilotkit/shared";
8
- import { errorConfig, getFallbackMessage } from "./error-messages";
2
+ import { CopilotKitLowLevelError, CopilotKitError, CopilotKitErrorCode } from "@copilotkit/shared";
9
3
 
10
4
  export async function writeJsonLineResponseToEventStream<T>(
11
5
  response: ReadableStream<Uint8Array>,
@@ -58,8 +52,10 @@ export async function writeJsonLineResponseToEventStream<T>(
58
52
  }
59
53
  }
60
54
  } catch (error) {
61
- // Preserve already structured CopilotKit errors, only convert unstructured errors
62
- const structuredError = ensureStructuredError(error, convertStreamingErrorToStructured);
55
+ console.error("Error in stream", error);
56
+
57
+ // Convert network termination errors to structured errors
58
+ const structuredError = convertStreamingErrorToStructured(error);
63
59
  eventStream$.error(structuredError);
64
60
  return;
65
61
  }
@@ -67,135 +63,50 @@ export async function writeJsonLineResponseToEventStream<T>(
67
63
  }
68
64
 
69
65
  function convertStreamingErrorToStructured(error: any): CopilotKitError {
70
- // Determine a more helpful error message based on context
71
- let helpfulMessage = generateHelpfulErrorMessage(error);
72
-
73
- // For network-related errors, use CopilotKitLowLevelError to preserve the original error
66
+ // Handle network termination errors
74
67
  if (
75
- error?.message?.includes("fetch failed") ||
76
- error?.message?.includes("ECONNREFUSED") ||
77
- error?.message?.includes("ENOTFOUND") ||
78
- error?.message?.includes("ETIMEDOUT") ||
79
68
  error?.message?.includes("terminated") ||
80
69
  error?.cause?.code === "UND_ERR_SOCKET" ||
81
70
  error?.message?.includes("other side closed") ||
82
71
  error?.code === "UND_ERR_SOCKET"
72
+ ) {
73
+ return new CopilotKitError({
74
+ message:
75
+ "Connection to agent was unexpectedly terminated. This is likely due to the agent service being down or experiencing issues. Please check your agent logs and try again.",
76
+ code: CopilotKitErrorCode.NETWORK_ERROR,
77
+ });
78
+ }
79
+
80
+ // Handle other network-related errors
81
+ if (
82
+ error?.message?.includes("fetch failed") ||
83
+ error?.message?.includes("ECONNREFUSED") ||
84
+ error?.message?.includes("ENOTFOUND") ||
85
+ error?.message?.includes("ETIMEDOUT")
83
86
  ) {
84
87
  return new CopilotKitLowLevelError({
85
88
  error: error instanceof Error ? error : new Error(String(error)),
86
89
  url: "streaming connection",
87
- message: helpfulMessage,
90
+ message:
91
+ "Network error occurred during streaming. Please check your connection and try again.",
92
+ });
93
+ }
94
+
95
+ // Handle abort/cancellation errors (these are usually normal)
96
+ if (
97
+ error?.message?.includes("aborted") ||
98
+ error?.message?.includes("canceled") ||
99
+ error?.message?.includes("signal is aborted")
100
+ ) {
101
+ return new CopilotKitError({
102
+ message: "Request was cancelled",
103
+ code: CopilotKitErrorCode.UNKNOWN,
88
104
  });
89
105
  }
90
106
 
91
- // For all other errors, preserve the raw error in a basic CopilotKitError
107
+ // Default: convert unknown streaming errors
92
108
  return new CopilotKitError({
93
- message: helpfulMessage,
109
+ message: `Streaming error: ${error?.message || String(error)}`,
94
110
  code: CopilotKitErrorCode.UNKNOWN,
95
111
  });
96
112
  }
97
-
98
- /**
99
- * Generates a helpful error message based on error patterns and context
100
- */
101
- export function generateHelpfulErrorMessage(error: any, context: string = "connection"): string {
102
- const baseMessage = error?.message || String(error);
103
-
104
- // Check for preserved error information from Python agent
105
- const originalErrorType = error?.originalErrorType || error?.extensions?.originalErrorType;
106
- const statusCode = error?.statusCode || error?.extensions?.statusCode;
107
- const responseData = error?.responseData || error?.extensions?.responseData;
108
-
109
- // First, try to match by original error type if available (more specific)
110
- if (originalErrorType) {
111
- const typeConfig = errorConfig.errorPatterns[originalErrorType];
112
- if (typeConfig) {
113
- return typeConfig.message.replace("{context}", context);
114
- }
115
- }
116
-
117
- // Check for specific error patterns from configuration
118
- for (const [pattern, config] of Object.entries(errorConfig.errorPatterns)) {
119
- const shouldMatch =
120
- baseMessage?.includes(pattern) ||
121
- error?.cause?.code === pattern ||
122
- error?.code === pattern ||
123
- statusCode === parseInt(pattern) ||
124
- (pattern === "other_side_closed" && baseMessage?.includes("other side closed")) ||
125
- (pattern === "fetch_failed" && baseMessage?.includes("fetch failed")) ||
126
- (responseData && JSON.stringify(responseData).includes(pattern));
127
-
128
- if (shouldMatch) {
129
- // Replace {context} placeholder with actual context
130
- return config.message.replace("{context}", context);
131
- }
132
- }
133
-
134
- // Try to match by category for fallback messages
135
- if (isNetworkError(error)) {
136
- return getFallbackMessage("network");
137
- }
138
-
139
- if (isConnectionError(error)) {
140
- return getFallbackMessage("connection");
141
- }
142
-
143
- if (isAuthenticationError(error)) {
144
- return getFallbackMessage("authentication");
145
- }
146
-
147
- // Default fallback
148
- return getFallbackMessage("default");
149
- }
150
-
151
- /**
152
- * Determines if an error is network-related
153
- */
154
- function isNetworkError(error: any): boolean {
155
- const networkPatterns = ["ECONNREFUSED", "ENOTFOUND", "ETIMEDOUT", "fetch_failed"];
156
- return networkPatterns.some(
157
- (pattern) =>
158
- error?.message?.includes(pattern) ||
159
- error?.cause?.code === pattern ||
160
- error?.code === pattern,
161
- );
162
- }
163
-
164
- /**
165
- * Determines if an error is connection-related
166
- */
167
- function isConnectionError(error: any): boolean {
168
- const connectionPatterns = ["terminated", "UND_ERR_SOCKET", "other side closed"];
169
- return connectionPatterns.some(
170
- (pattern) =>
171
- error?.message?.includes(pattern) ||
172
- error?.cause?.code === pattern ||
173
- error?.code === pattern,
174
- );
175
- }
176
-
177
- /**
178
- * Determines if an error is authentication-related
179
- */
180
- function isAuthenticationError(error: any): boolean {
181
- const authPatterns = [
182
- "401",
183
- "api key",
184
- "unauthorized",
185
- "authentication",
186
- "AuthenticationError",
187
- "PermissionDeniedError",
188
- ];
189
- const baseMessage = error?.message || String(error);
190
- const originalErrorType = error?.originalErrorType || error?.extensions?.originalErrorType;
191
- const statusCode = error?.statusCode || error?.extensions?.statusCode;
192
-
193
- return authPatterns.some(
194
- (pattern) =>
195
- baseMessage?.toLowerCase().includes(pattern.toLowerCase()) ||
196
- originalErrorType === pattern ||
197
- statusCode === 401 ||
198
- error?.status === 401 ||
199
- error?.statusCode === 401,
200
- );
201
- }
@@ -29,7 +29,6 @@ import {
29
29
  } from "./utils";
30
30
 
31
31
  import { randomId, randomUUID } from "@copilotkit/shared";
32
- import { convertServiceAdapterError } from "../shared";
33
32
 
34
33
  const DEFAULT_MODEL = "claude-3-5-sonnet-latest";
35
34
 
@@ -61,36 +60,6 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
61
60
  }
62
61
  }
63
62
 
64
- private shouldGenerateFallbackResponse(messages: Anthropic.Messages.MessageParam[]): boolean {
65
- if (messages.length === 0) return false;
66
-
67
- const lastMessage = messages[messages.length - 1];
68
-
69
- // Check if the last message is a tool result
70
- const endsWithToolResult =
71
- lastMessage.role === "user" &&
72
- Array.isArray(lastMessage.content) &&
73
- lastMessage.content.some((content: any) => content.type === "tool_result");
74
-
75
- // Also check if we have a recent pattern of user message -> assistant tool use -> user tool result
76
- // This indicates a completed action that might not need a response
77
- if (messages.length >= 3 && endsWithToolResult) {
78
- const lastThree = messages.slice(-3);
79
- const hasRecentToolPattern =
80
- lastThree[0]?.role === "user" && // Initial user message
81
- lastThree[1]?.role === "assistant" && // Assistant tool use
82
- Array.isArray(lastThree[1].content) &&
83
- lastThree[1].content.some((content: any) => content.type === "tool_use") &&
84
- lastThree[2]?.role === "user" && // Tool result
85
- Array.isArray(lastThree[2].content) &&
86
- lastThree[2].content.some((content: any) => content.type === "tool_result");
87
-
88
- return hasRecentToolPattern;
89
- }
90
-
91
- return endsWithToolResult;
92
- }
93
-
94
63
  async process(
95
64
  request: CopilotRuntimeChatCompletionRequest,
96
65
  ): Promise<CopilotRuntimeChatCompletionResponse> {
@@ -125,30 +94,24 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
125
94
  }
126
95
 
127
96
  // Step 2: Map each message to an Anthropic message, eliminating invalid tool_results
128
- const processedToolResultIds = new Set<string>();
129
97
  const anthropicMessages = messages
130
98
  .map((message) => {
131
- // For tool results, only include if they match a valid tool_use ID AND haven't been processed
99
+ // For tool results, only include if they match a valid tool_use ID
132
100
  if (message.isResultMessage()) {
133
101
  // Skip if there's no corresponding tool_use
134
102
  if (!validToolUseIds.has(message.actionExecutionId)) {
135
103
  return null; // Will be filtered out later
136
104
  }
137
105
 
138
- // Skip if we've already processed a result for this tool_use ID
139
- if (processedToolResultIds.has(message.actionExecutionId)) {
140
- return null; // Will be filtered out later
141
- }
142
-
143
- // Mark this tool result as processed
144
- processedToolResultIds.add(message.actionExecutionId);
106
+ // Remove this ID from valid IDs so we don't process duplicates
107
+ validToolUseIds.delete(message.actionExecutionId);
145
108
 
146
109
  return {
147
110
  role: "user",
148
111
  content: [
149
112
  {
150
113
  type: "tool_result",
151
- content: message.result || "Action completed successfully",
114
+ content: message.result,
152
115
  tool_use_id: message.actionExecutionId,
153
116
  },
154
117
  ],
@@ -176,7 +139,6 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
176
139
  // Apply token limits
177
140
  const limitedMessages = limitMessagesToTokenCount(anthropicMessages, tools, model);
178
141
 
179
- // We'll check if we need a fallback response after seeing what Anthropic returns
180
142
  // We skip grouping by role since we've already ensured uniqueness of tool_results
181
143
 
182
144
  let toolChoice: any = forwardedParameters?.toolChoice;
@@ -209,14 +171,12 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
209
171
  let currentMessageId = randomId();
210
172
  let currentToolCallId = randomId();
211
173
  let filterThinkingTextBuffer = new FilterThinkingTextBuffer();
212
- let hasReceivedContent = false;
213
174
 
214
175
  try {
215
176
  for await (const chunk of stream as AsyncIterable<any>) {
216
177
  if (chunk.type === "message_start") {
217
178
  currentMessageId = chunk.message.id;
218
179
  } else if (chunk.type === "content_block_start") {
219
- hasReceivedContent = true;
220
180
  if (chunk.content_block.type === "text") {
221
181
  didOutputText = false;
222
182
  filterThinkingTextBuffer.reset();
@@ -260,34 +220,15 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
260
220
  }
261
221
  }
262
222
  } catch (error) {
263
- throw convertServiceAdapterError(error, "Anthropic");
264
- }
265
-
266
- // Generate fallback response only if Anthropic produced no content
267
- if (!hasReceivedContent && this.shouldGenerateFallbackResponse(limitedMessages)) {
268
- // Extract the tool result content for a more contextual response
269
- let fallbackContent = "Task completed successfully.";
270
- const lastMessage = limitedMessages[limitedMessages.length - 1];
271
- if (lastMessage?.role === "user" && Array.isArray(lastMessage.content)) {
272
- const toolResult = lastMessage.content.find((c: any) => c.type === "tool_result");
273
- if (toolResult?.content && toolResult.content !== "Action completed successfully") {
274
- fallbackContent = toolResult.content;
275
- }
276
- }
277
-
278
- currentMessageId = randomId();
279
- eventStream$.sendTextMessageStart({ messageId: currentMessageId });
280
- eventStream$.sendTextMessageContent({
281
- messageId: currentMessageId,
282
- content: fallbackContent,
283
- });
284
- eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
223
+ console.error("[Anthropic] Error processing stream:", error);
224
+ throw error;
285
225
  }
286
226
 
287
227
  eventStream$.complete();
288
228
  });
289
229
  } catch (error) {
290
- throw convertServiceAdapterError(error, "Anthropic");
230
+ console.error("[Anthropic] Error during API call:", error);
231
+ throw error;
291
232
  }
292
233
 
293
234
  return {
@@ -1,6 +1,11 @@
1
- import { Anthropic } from "@anthropic-ai/sdk";
1
+ import {
2
+ ActionExecutionMessage,
3
+ Message,
4
+ ResultMessage,
5
+ TextMessage,
6
+ } from "../../graphql/types/converted";
2
7
  import { ActionInput } from "../../graphql/inputs/action.input";
3
- import { Message } from "../../graphql/types/converted";
8
+ import { Anthropic } from "@anthropic-ai/sdk";
4
9
 
5
10
  export function limitMessagesToTokenCount(
6
11
  messages: any[],
@@ -143,7 +148,7 @@ export function convertMessageToAnthropicMessage(
143
148
  content: [
144
149
  {
145
150
  type: "tool_result",
146
- content: message.result || "Action completed successfully",
151
+ content: message.result,
147
152
  tool_use_id: message.actionExecutionId,
148
153
  },
149
154
  ],