@copilotkit/runtime 1.9.2-next.7 → 1.9.2-next.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/{chunk-IIXJVVTV.mjs → chunk-4JBKY7XT.mjs} +109 -75
- package/dist/chunk-4JBKY7XT.mjs.map +1 -0
- package/dist/{chunk-LSB3QZHS.mjs → chunk-5YGKE5SN.mjs} +2 -2
- package/dist/{chunk-2PQGAIVB.mjs → chunk-ALZ5H3VD.mjs} +2 -2
- package/dist/chunk-AMUJQ6IR.mjs +50 -0
- package/dist/chunk-AMUJQ6IR.mjs.map +1 -0
- package/dist/{chunk-2UAJCT5X.mjs → chunk-SMDVD4VG.mjs} +2 -2
- package/dist/{chunk-GW6ERFKI.mjs → chunk-UUXRYAB4.mjs} +2 -2
- package/dist/{chunk-5BIEM2UU.mjs → chunk-XWBDEXDA.mjs} +4 -3
- package/dist/{chunk-5BIEM2UU.mjs.map → chunk-XWBDEXDA.mjs.map} +1 -1
- package/dist/{chunk-HB4D2KJC.mjs → chunk-Z5GYTKMD.mjs} +2345 -2066
- package/dist/chunk-Z5GYTKMD.mjs.map +1 -0
- package/dist/{groq-adapter-25a2bd35.d.ts → groq-adapter-172a2ca4.d.ts} +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3713 -3356
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +12 -8
- package/dist/index.mjs.map +1 -1
- package/dist/lib/index.d.ts +3 -3
- package/dist/lib/index.js +2981 -2624
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +9 -8
- package/dist/lib/integrations/index.d.ts +3 -3
- package/dist/lib/integrations/index.js +141 -86
- package/dist/lib/integrations/index.js.map +1 -1
- package/dist/lib/integrations/index.mjs +7 -6
- package/dist/lib/integrations/nest/index.d.ts +2 -2
- package/dist/lib/integrations/nest/index.js +141 -86
- package/dist/lib/integrations/nest/index.js.map +1 -1
- package/dist/lib/integrations/nest/index.mjs +5 -4
- package/dist/lib/integrations/node-express/index.d.ts +2 -2
- package/dist/lib/integrations/node-express/index.js +141 -86
- package/dist/lib/integrations/node-express/index.js.map +1 -1
- package/dist/lib/integrations/node-express/index.mjs +5 -4
- package/dist/lib/integrations/node-http/index.d.ts +2 -2
- package/dist/lib/integrations/node-http/index.js +141 -86
- package/dist/lib/integrations/node-http/index.js.map +1 -1
- package/dist/lib/integrations/node-http/index.mjs +4 -3
- package/dist/service-adapters/index.d.ts +5 -4
- package/dist/service-adapters/index.js +179 -104
- package/dist/service-adapters/index.js.map +1 -1
- package/dist/service-adapters/index.mjs +6 -2
- package/dist/service-adapters/shared/index.d.ts +9 -0
- package/dist/service-adapters/shared/index.js +72 -0
- package/dist/service-adapters/shared/index.js.map +1 -0
- package/dist/service-adapters/shared/index.mjs +8 -0
- package/dist/service-adapters/shared/index.mjs.map +1 -0
- package/dist/{shared-941d59dc.d.ts → shared-bd953ebf.d.ts} +8 -4
- package/dist/utils/index.d.ts +17 -1
- package/dist/utils/index.js +3 -2
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +1 -1
- package/package.json +2 -2
- package/src/agents/langgraph/event-source.ts +36 -38
- package/src/agents/langgraph/events.ts +19 -1
- package/src/graphql/resolvers/copilot.resolver.ts +84 -40
- package/src/lib/error-messages.ts +200 -0
- package/src/lib/integrations/shared.ts +43 -0
- package/src/lib/runtime/copilot-runtime.ts +56 -48
- package/src/lib/runtime/remote-action-constructors.ts +28 -3
- package/src/lib/runtime/remote-lg-action.ts +130 -40
- package/src/lib/streaming.ts +125 -36
- package/src/service-adapters/anthropic/anthropic-adapter.ts +3 -4
- package/src/service-adapters/events.ts +37 -81
- package/src/service-adapters/groq/groq-adapter.ts +66 -56
- package/src/service-adapters/index.ts +1 -0
- package/src/service-adapters/openai/openai-adapter.ts +18 -3
- package/src/service-adapters/shared/error-utils.ts +61 -0
- package/src/service-adapters/shared/index.ts +1 -0
- package/src/utils/failed-response-status-reasons.ts +23 -1
- package/dist/chunk-HB4D2KJC.mjs.map +0 -1
- package/dist/chunk-IIXJVVTV.mjs.map +0 -1
- package/dist/{chunk-LSB3QZHS.mjs.map → chunk-5YGKE5SN.mjs.map} +0 -0
- package/dist/{chunk-2PQGAIVB.mjs.map → chunk-ALZ5H3VD.mjs.map} +0 -0
- package/dist/{chunk-2UAJCT5X.mjs.map → chunk-SMDVD4VG.mjs.map} +0 -0
- package/dist/{chunk-GW6ERFKI.mjs.map → chunk-UUXRYAB4.mjs.map} +0 -0
- package/dist/{langserve-4a5c9217.d.ts → langserve-fc5cac89.d.ts} +7 -7
|
@@ -2,7 +2,9 @@ import {
|
|
|
2
2
|
Client as LangGraphClient,
|
|
3
3
|
EventsStreamEvent,
|
|
4
4
|
GraphSchema,
|
|
5
|
+
Interrupt,
|
|
5
6
|
StreamMode,
|
|
7
|
+
ThreadState,
|
|
6
8
|
} from "@langchain/langgraph-sdk";
|
|
7
9
|
import { createHash } from "node:crypto";
|
|
8
10
|
import { isValidUUID, randomUUID } from "@copilotkit/shared";
|
|
@@ -17,9 +19,28 @@ import { CustomEventNames, LangGraphEventTypes } from "../../agents/langgraph/ev
|
|
|
17
19
|
import telemetry from "../telemetry-client";
|
|
18
20
|
import { MetaEventInput } from "../../graphql/inputs/meta-event.input";
|
|
19
21
|
import { MetaEventName } from "../../graphql/types/meta-events.type";
|
|
20
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
parseJson,
|
|
24
|
+
CopilotKitMisuseError,
|
|
25
|
+
CopilotKitLowLevelError,
|
|
26
|
+
CopilotKitError,
|
|
27
|
+
} from "@copilotkit/shared";
|
|
21
28
|
import { RemoveMessage } from "@langchain/core/messages";
|
|
22
29
|
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
|
+
}
|
|
23
44
|
|
|
24
45
|
type State = Record<string, any>;
|
|
25
46
|
|
|
@@ -84,8 +105,6 @@ type SchemaKeys = {
|
|
|
84
105
|
config: string[] | null;
|
|
85
106
|
} | null;
|
|
86
107
|
|
|
87
|
-
let activeInterruptEvent = false;
|
|
88
|
-
|
|
89
108
|
export async function execute(args: ExecutionArgs): Promise<ReadableStream<Uint8Array>> {
|
|
90
109
|
return new ReadableStream({
|
|
91
110
|
async start(controller) {
|
|
@@ -128,6 +147,15 @@ export async function execute(args: ExecutionArgs): Promise<ReadableStream<Uint8
|
|
|
128
147
|
See more: https://docs.copilotkit.ai/troubleshooting/common-issues`,
|
|
129
148
|
});
|
|
130
149
|
} 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
|
+
|
|
131
159
|
throw new CopilotKitMisuseError({
|
|
132
160
|
message: `
|
|
133
161
|
The LangGraph client threw unhandled error ${lastError}.
|
|
@@ -188,7 +216,7 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
|
|
|
188
216
|
await client.threads.create({ threadId });
|
|
189
217
|
}
|
|
190
218
|
|
|
191
|
-
let agentState = { values: {} };
|
|
219
|
+
let agentState = { values: {} } as ThreadState;
|
|
192
220
|
if (wasInitiatedWithExistingThread) {
|
|
193
221
|
agentState = await client.threads.getState(threadId);
|
|
194
222
|
}
|
|
@@ -218,16 +246,14 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
|
|
|
218
246
|
const lgInterruptMetaEvent = metaEvents?.find(
|
|
219
247
|
(ev) => ev.name === MetaEventName.LangGraphInterruptEvent,
|
|
220
248
|
);
|
|
221
|
-
|
|
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
|
-
}
|
|
249
|
+
|
|
225
250
|
if (lgInterruptMetaEvent?.response) {
|
|
226
251
|
let response = lgInterruptMetaEvent.response;
|
|
227
252
|
payload.command = { resume: parseJson(response, response) };
|
|
228
253
|
}
|
|
229
254
|
|
|
230
|
-
|
|
255
|
+
const interrupts = (agentState.tasks?.[0]?.interrupts ?? []) as Interrupt[];
|
|
256
|
+
if (mode === "continue" && !interrupts.length) {
|
|
231
257
|
await client.threads.updateState(threadId, { values: state, asNode: nodeName });
|
|
232
258
|
}
|
|
233
259
|
|
|
@@ -311,17 +337,30 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
|
|
|
311
337
|
let shouldExit = false;
|
|
312
338
|
let externalRunId = null;
|
|
313
339
|
|
|
314
|
-
const streamResponse = client.runs.stream(threadId, assistantId, payload);
|
|
315
|
-
|
|
316
340
|
const emit = (message: string) => controller.enqueue(new TextEncoder().encode(message));
|
|
317
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
|
+
const streamResponse = client.runs.stream(threadId, assistantId, payload);
|
|
357
|
+
|
|
318
358
|
let latestStateValues = {};
|
|
319
359
|
let updatedState = state;
|
|
320
360
|
// If a manual emittance happens, it is the ultimate source of truth of state, unless a node has exited.
|
|
321
361
|
// Therefore, this value should either hold null, or the only edition of state that should be used.
|
|
322
362
|
let manuallyEmittedState = null;
|
|
323
363
|
|
|
324
|
-
activeInterruptEvent = false;
|
|
325
364
|
try {
|
|
326
365
|
telemetry.capture("oss.runtime.agent_execution_stream_started", {
|
|
327
366
|
hashedLgcKey: streamInfo.hashedLgcKey,
|
|
@@ -330,7 +369,43 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
|
|
|
330
369
|
if (!["events", "values", "error", "updates"].includes(streamResponseChunk.event)) continue;
|
|
331
370
|
|
|
332
371
|
if (streamResponseChunk.event === "error") {
|
|
333
|
-
|
|
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
|
+
});
|
|
334
409
|
}
|
|
335
410
|
|
|
336
411
|
// Force event type, as data is not properly defined on the LG side.
|
|
@@ -345,33 +420,8 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
|
|
|
345
420
|
|
|
346
421
|
const interruptEvents = chunk.data.__interrupt__;
|
|
347
422
|
if (interruptEvents?.length) {
|
|
348
|
-
activeInterruptEvent = true;
|
|
349
423
|
const interruptValue = interruptEvents?.[0].value;
|
|
350
|
-
|
|
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
|
-
}
|
|
424
|
+
emitInterrupt(interruptValue, emit);
|
|
375
425
|
continue;
|
|
376
426
|
}
|
|
377
427
|
if (streamResponseChunk.event === "updates") continue;
|
|
@@ -521,11 +571,29 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
|
|
|
521
571
|
|
|
522
572
|
return Promise.resolve();
|
|
523
573
|
} catch (e) {
|
|
524
|
-
|
|
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
|
+
|
|
525
583
|
telemetry.capture("oss.runtime.agent_execution_stream_errored", {
|
|
526
584
|
...streamInfo,
|
|
527
585
|
error: e.message,
|
|
528
586
|
});
|
|
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
|
+
|
|
529
597
|
return Promise.resolve();
|
|
530
598
|
}
|
|
531
599
|
}
|
|
@@ -909,3 +977,25 @@ function getSchemaKeys(graphSchema: GraphSchema): SchemaKeys {
|
|
|
909
977
|
function filterObjectBySchemaKeys(obj: Record<string, any>, schemaKeys: string[]) {
|
|
910
978
|
return Object.fromEntries(Object.entries(obj).filter(([key]) => schemaKeys.includes(key)));
|
|
911
979
|
}
|
|
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
|
+
}
|
package/src/lib/streaming.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { ReplaySubject } from "rxjs";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
CopilotKitLowLevelError,
|
|
4
|
+
CopilotKitError,
|
|
5
|
+
CopilotKitErrorCode,
|
|
6
|
+
ensureStructuredError,
|
|
7
|
+
} from "@copilotkit/shared";
|
|
8
|
+
import { errorConfig, getFallbackMessage } from "./error-messages";
|
|
3
9
|
|
|
4
10
|
export async function writeJsonLineResponseToEventStream<T>(
|
|
5
11
|
response: ReadableStream<Uint8Array>,
|
|
@@ -52,10 +58,8 @@ export async function writeJsonLineResponseToEventStream<T>(
|
|
|
52
58
|
}
|
|
53
59
|
}
|
|
54
60
|
} catch (error) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// Convert network termination errors to structured errors
|
|
58
|
-
const structuredError = convertStreamingErrorToStructured(error);
|
|
61
|
+
// Preserve already structured CopilotKit errors, only convert unstructured errors
|
|
62
|
+
const structuredError = ensureStructuredError(error, convertStreamingErrorToStructured);
|
|
59
63
|
eventStream$.error(structuredError);
|
|
60
64
|
return;
|
|
61
65
|
}
|
|
@@ -63,50 +67,135 @@ export async function writeJsonLineResponseToEventStream<T>(
|
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
function convertStreamingErrorToStructured(error: any): CopilotKitError {
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
error?.message?.includes("terminated") ||
|
|
69
|
-
error?.cause?.code === "UND_ERR_SOCKET" ||
|
|
70
|
-
error?.message?.includes("other side closed") ||
|
|
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
|
-
}
|
|
70
|
+
// Determine a more helpful error message based on context
|
|
71
|
+
let helpfulMessage = generateHelpfulErrorMessage(error);
|
|
79
72
|
|
|
80
|
-
//
|
|
73
|
+
// For network-related errors, use CopilotKitLowLevelError to preserve the original error
|
|
81
74
|
if (
|
|
82
75
|
error?.message?.includes("fetch failed") ||
|
|
83
76
|
error?.message?.includes("ECONNREFUSED") ||
|
|
84
77
|
error?.message?.includes("ENOTFOUND") ||
|
|
85
|
-
error?.message?.includes("ETIMEDOUT")
|
|
78
|
+
error?.message?.includes("ETIMEDOUT") ||
|
|
79
|
+
error?.message?.includes("terminated") ||
|
|
80
|
+
error?.cause?.code === "UND_ERR_SOCKET" ||
|
|
81
|
+
error?.message?.includes("other side closed") ||
|
|
82
|
+
error?.code === "UND_ERR_SOCKET"
|
|
86
83
|
) {
|
|
87
84
|
return new CopilotKitLowLevelError({
|
|
88
85
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
89
86
|
url: "streaming connection",
|
|
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,
|
|
87
|
+
message: helpfulMessage,
|
|
104
88
|
});
|
|
105
89
|
}
|
|
106
90
|
|
|
107
|
-
//
|
|
91
|
+
// For all other errors, preserve the raw error in a basic CopilotKitError
|
|
108
92
|
return new CopilotKitError({
|
|
109
|
-
message:
|
|
93
|
+
message: helpfulMessage,
|
|
110
94
|
code: CopilotKitErrorCode.UNKNOWN,
|
|
111
95
|
});
|
|
112
96
|
}
|
|
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,6 +29,7 @@ import {
|
|
|
29
29
|
} from "./utils";
|
|
30
30
|
|
|
31
31
|
import { randomId, randomUUID } from "@copilotkit/shared";
|
|
32
|
+
import { convertServiceAdapterError } from "../shared";
|
|
32
33
|
|
|
33
34
|
const DEFAULT_MODEL = "claude-3-5-sonnet-latest";
|
|
34
35
|
|
|
@@ -220,15 +221,13 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
|
|
|
220
221
|
}
|
|
221
222
|
}
|
|
222
223
|
} catch (error) {
|
|
223
|
-
|
|
224
|
-
throw error;
|
|
224
|
+
throw convertServiceAdapterError(error, "Anthropic");
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
eventStream$.complete();
|
|
228
228
|
});
|
|
229
229
|
} catch (error) {
|
|
230
|
-
|
|
231
|
-
throw error;
|
|
230
|
+
throw convertServiceAdapterError(error, "Anthropic");
|
|
232
231
|
}
|
|
233
232
|
|
|
234
233
|
return {
|
|
@@ -1,37 +1,32 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Action,
|
|
3
|
-
randomId,
|
|
4
3
|
CopilotKitError,
|
|
5
4
|
CopilotKitErrorCode,
|
|
6
5
|
CopilotKitLowLevelError,
|
|
6
|
+
ensureStructuredError,
|
|
7
|
+
randomId,
|
|
8
|
+
Severity,
|
|
7
9
|
} from "@copilotkit/shared";
|
|
10
|
+
import { plainToInstance } from "class-transformer";
|
|
8
11
|
import {
|
|
9
|
-
|
|
12
|
+
catchError,
|
|
10
13
|
concat,
|
|
11
|
-
scan,
|
|
12
14
|
concatMap,
|
|
13
|
-
|
|
14
|
-
Subject,
|
|
15
|
+
EMPTY,
|
|
15
16
|
firstValueFrom,
|
|
16
17
|
from,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
of,
|
|
19
|
+
ReplaySubject,
|
|
20
|
+
scan,
|
|
21
|
+
Subject,
|
|
20
22
|
} from "rxjs";
|
|
21
|
-
import {
|
|
23
|
+
import { ActionInput } from "../graphql/inputs/action.input";
|
|
24
|
+
import { ActionExecutionMessage, ResultMessage, TextMessage } from "../graphql/types/converted";
|
|
22
25
|
import { GuardrailsResult } from "../graphql/types/guardrails-result.type";
|
|
23
|
-
import telemetry from "../lib/telemetry-client";
|
|
24
26
|
import { isRemoteAgentAction } from "../lib/runtime/remote-actions";
|
|
25
|
-
import {
|
|
26
|
-
import
|
|
27
|
-
|
|
28
|
-
ResultMessage,
|
|
29
|
-
TextMessage,
|
|
30
|
-
Message,
|
|
31
|
-
} from "../graphql/types/converted";
|
|
32
|
-
import { plainToInstance } from "class-transformer";
|
|
33
|
-
import { MessageRole } from "../graphql/types/enums";
|
|
34
|
-
import { parseJson, tryMap } from "@copilotkit/shared";
|
|
27
|
+
import { generateHelpfulErrorMessage } from "../lib/streaming";
|
|
28
|
+
import telemetry from "../lib/telemetry-client";
|
|
29
|
+
import { streamLangChainResponse } from "./langchain/utils";
|
|
35
30
|
|
|
36
31
|
export enum RuntimeEventTypes {
|
|
37
32
|
TextMessageStart = "TextMessageStart",
|
|
@@ -273,10 +268,8 @@ export class RuntimeEventSource {
|
|
|
273
268
|
threadId: string;
|
|
274
269
|
}) {
|
|
275
270
|
this.callback(this.eventStream$).catch((error) => {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
// Convert streaming errors to structured errors
|
|
279
|
-
const structuredError = convertStreamingErrorToStructured(error);
|
|
271
|
+
// Convert streaming errors to structured errors, but preserve already structured ones
|
|
272
|
+
const structuredError = ensureStructuredError(error, convertStreamingErrorToStructured);
|
|
280
273
|
this.eventStream$.error(structuredError);
|
|
281
274
|
this.eventStream$.complete();
|
|
282
275
|
});
|
|
@@ -330,17 +323,16 @@ export class RuntimeEventSource {
|
|
|
330
323
|
eventWithState.actionExecutionId,
|
|
331
324
|
actionInputsWithoutAgents,
|
|
332
325
|
threadId,
|
|
333
|
-
).catch((error) => {
|
|
334
|
-
console.error(error);
|
|
335
|
-
});
|
|
326
|
+
).catch((error) => {});
|
|
336
327
|
|
|
337
328
|
telemetry.capture("oss.runtime.server_action_executed", {});
|
|
338
329
|
return concat(of(eventWithState.event!), toolCallEventStream$).pipe(
|
|
339
330
|
catchError((error) => {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
331
|
+
// Convert streaming errors to structured errors and send as action result, but preserve already structured ones
|
|
332
|
+
const structuredError = ensureStructuredError(
|
|
333
|
+
error,
|
|
334
|
+
convertStreamingErrorToStructured,
|
|
335
|
+
);
|
|
344
336
|
toolCallEventStream$.sendActionExecutionResult({
|
|
345
337
|
actionExecutionId: eventWithState.actionExecutionId!,
|
|
346
338
|
actionName: eventWithState.action!.name,
|
|
@@ -436,10 +428,8 @@ async function executeAction(
|
|
|
436
428
|
from(stream).subscribe({
|
|
437
429
|
next: (event) => eventStream$.next(event),
|
|
438
430
|
error: (err) => {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
// Convert streaming errors to structured errors
|
|
442
|
-
const structuredError = convertStreamingErrorToStructured(err);
|
|
431
|
+
// Preserve already structured CopilotKit errors, only convert unstructured errors
|
|
432
|
+
const structuredError = ensureStructuredError(err, convertStreamingErrorToStructured);
|
|
443
433
|
eventStream$.sendActionExecutionResult({
|
|
444
434
|
actionExecutionId,
|
|
445
435
|
actionName: action.name,
|
|
@@ -480,65 +470,31 @@ async function executeAction(
|
|
|
480
470
|
}
|
|
481
471
|
|
|
482
472
|
function convertStreamingErrorToStructured(error: any): CopilotKitError {
|
|
483
|
-
//
|
|
484
|
-
|
|
485
|
-
error?.message?.includes("terminated") ||
|
|
486
|
-
error?.cause?.code === "UND_ERR_SOCKET" ||
|
|
487
|
-
error?.message?.includes("other side closed") ||
|
|
488
|
-
error?.code === "UND_ERR_SOCKET"
|
|
489
|
-
) {
|
|
490
|
-
return new CopilotKitError({
|
|
491
|
-
message:
|
|
492
|
-
"Connection to agent was unexpectedly terminated. This may be due to the agent service being restarted or network issues. Please try again.",
|
|
493
|
-
code: CopilotKitErrorCode.NETWORK_ERROR,
|
|
494
|
-
});
|
|
495
|
-
}
|
|
473
|
+
// Determine a more helpful error message based on context
|
|
474
|
+
let helpfulMessage = generateHelpfulErrorMessage(error, "event streaming connection");
|
|
496
475
|
|
|
497
|
-
//
|
|
476
|
+
// For network-related errors, use CopilotKitLowLevelError to preserve the original error
|
|
498
477
|
if (
|
|
499
478
|
error?.message?.includes("fetch failed") ||
|
|
500
479
|
error?.message?.includes("ECONNREFUSED") ||
|
|
501
480
|
error?.message?.includes("ENOTFOUND") ||
|
|
502
|
-
error?.message?.includes("ETIMEDOUT")
|
|
481
|
+
error?.message?.includes("ETIMEDOUT") ||
|
|
482
|
+
error?.message?.includes("terminated") ||
|
|
483
|
+
error?.cause?.code === "UND_ERR_SOCKET" ||
|
|
484
|
+
error?.message?.includes("other side closed") ||
|
|
485
|
+
error?.code === "UND_ERR_SOCKET"
|
|
503
486
|
) {
|
|
504
487
|
return new CopilotKitLowLevelError({
|
|
505
488
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
506
489
|
url: "event streaming connection",
|
|
507
|
-
message:
|
|
508
|
-
"Network error occurred during event streaming. Please check your connection and try again.",
|
|
509
|
-
});
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// Handle abort/cancellation errors (these are usually normal)
|
|
513
|
-
if (
|
|
514
|
-
error?.message?.includes("aborted") ||
|
|
515
|
-
error?.message?.includes("canceled") ||
|
|
516
|
-
error?.message?.includes("signal is aborted")
|
|
517
|
-
) {
|
|
518
|
-
return new CopilotKitError({
|
|
519
|
-
message: "Request was cancelled",
|
|
520
|
-
code: CopilotKitErrorCode.UNKNOWN,
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// Handle API key errors (authentication/authorization issues)
|
|
525
|
-
const errorMessage = error?.message || String(error);
|
|
526
|
-
if (
|
|
527
|
-
errorMessage.includes("401") ||
|
|
528
|
-
errorMessage.toLowerCase().includes("api key") ||
|
|
529
|
-
errorMessage.toLowerCase().includes("unauthorized") ||
|
|
530
|
-
errorMessage.toLowerCase().includes("authentication") ||
|
|
531
|
-
errorMessage.toLowerCase().includes("incorrect api key")
|
|
532
|
-
) {
|
|
533
|
-
return new CopilotKitError({
|
|
534
|
-
message: `Event streaming error: ${errorMessage}`,
|
|
535
|
-
code: CopilotKitErrorCode.MISSING_PUBLIC_API_KEY_ERROR,
|
|
490
|
+
message: helpfulMessage,
|
|
536
491
|
});
|
|
537
492
|
}
|
|
538
493
|
|
|
539
|
-
//
|
|
494
|
+
// For all other errors, preserve the raw error in a basic CopilotKitError
|
|
540
495
|
return new CopilotKitError({
|
|
541
|
-
message:
|
|
496
|
+
message: helpfulMessage,
|
|
542
497
|
code: CopilotKitErrorCode.UNKNOWN,
|
|
498
|
+
severity: Severity.CRITICAL,
|
|
543
499
|
});
|
|
544
500
|
}
|