@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.
- package/CHANGELOG.md +0 -53
- package/dist/{chunk-XWBDEXDA.mjs → chunk-5BIEM2UU.mjs} +3 -4
- package/dist/{chunk-XWBDEXDA.mjs.map → chunk-5BIEM2UU.mjs.map} +1 -1
- package/dist/{chunk-PMIAGZGS.mjs → chunk-GSYE3DGY.mjs} +2414 -2928
- package/dist/chunk-GSYE3DGY.mjs.map +1 -0
- package/dist/{chunk-GS7DO47Q.mjs → chunk-IIXJVVTV.mjs} +78 -155
- package/dist/chunk-IIXJVVTV.mjs.map +1 -0
- package/dist/{chunk-TOBFVWZU.mjs → chunk-MIPAKFI5.mjs} +2 -2
- package/dist/{chunk-VBXBFZEL.mjs → chunk-N24X5I3C.mjs} +2 -2
- package/dist/{chunk-6RUTA76W.mjs → chunk-WFYPJXWX.mjs} +2 -2
- package/dist/{chunk-5OK4GLKL.mjs → chunk-XDBXF3Q6.mjs} +2 -19
- package/dist/chunk-XDBXF3Q6.mjs.map +1 -0
- package/dist/{groq-adapter-172a2ca4.d.ts → groq-adapter-25a2bd35.d.ts} +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.js +2598 -3250
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8 -12
- package/dist/index.mjs.map +1 -1
- package/dist/lib/index.d.ts +4 -5
- package/dist/lib/index.js +2730 -3339
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +8 -9
- package/dist/lib/integrations/index.d.ts +3 -3
- package/dist/lib/integrations/index.js +96 -160
- package/dist/lib/integrations/index.js.map +1 -1
- package/dist/lib/integrations/index.mjs +6 -7
- package/dist/lib/integrations/nest/index.d.ts +2 -2
- package/dist/lib/integrations/nest/index.js +96 -160
- package/dist/lib/integrations/nest/index.js.map +1 -1
- package/dist/lib/integrations/nest/index.mjs +4 -5
- package/dist/lib/integrations/node-express/index.d.ts +2 -2
- package/dist/lib/integrations/node-express/index.js +96 -160
- package/dist/lib/integrations/node-express/index.js.map +1 -1
- package/dist/lib/integrations/node-express/index.mjs +4 -5
- package/dist/lib/integrations/node-http/index.d.ts +2 -2
- package/dist/lib/integrations/node-http/index.js +96 -160
- package/dist/lib/integrations/node-http/index.js.map +1 -1
- package/dist/lib/integrations/node-http/index.mjs +3 -4
- package/dist/service-adapters/index.d.ts +4 -6
- package/dist/service-adapters/index.js +107 -225
- package/dist/service-adapters/index.js.map +1 -1
- package/dist/service-adapters/index.mjs +2 -6
- package/dist/{shared-bd953ebf.d.ts → shared-e272b15a.d.ts} +5 -45
- package/dist/utils/index.d.ts +1 -17
- package/dist/utils/index.js +2 -3
- 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 +38 -36
- package/src/agents/langgraph/events.ts +1 -19
- package/src/graphql/resolvers/copilot.resolver.ts +45 -108
- package/src/graphql/resolvers/state.resolver.ts +3 -3
- package/src/lib/integrations/shared.ts +0 -43
- package/src/lib/runtime/copilot-runtime.ts +83 -412
- package/src/lib/runtime/langgraph/langgraph-agent.ts +0 -12
- package/src/lib/runtime/remote-action-constructors.ts +3 -28
- package/src/lib/runtime/remote-lg-action.ts +40 -130
- package/src/lib/streaming.ts +36 -125
- package/src/service-adapters/anthropic/anthropic-adapter.ts +8 -67
- package/src/service-adapters/anthropic/utils.ts +8 -3
- package/src/service-adapters/events.ts +81 -37
- package/src/service-adapters/groq/groq-adapter.ts +56 -66
- package/src/service-adapters/index.ts +0 -1
- package/src/service-adapters/openai/openai-adapter.ts +3 -18
- package/src/utils/failed-response-status-reasons.ts +1 -23
- package/tests/service-adapters/anthropic/anthropic-adapter.test.ts +387 -172
- package/dist/chunk-5OK4GLKL.mjs.map +0 -1
- package/dist/chunk-AMUJQ6IR.mjs +0 -50
- package/dist/chunk-AMUJQ6IR.mjs.map +0 -1
- package/dist/chunk-GS7DO47Q.mjs.map +0 -1
- package/dist/chunk-PMIAGZGS.mjs.map +0 -1
- package/dist/service-adapters/shared/index.d.ts +0 -9
- package/dist/service-adapters/shared/index.js +0 -72
- package/dist/service-adapters/shared/index.js.map +0 -1
- package/dist/service-adapters/shared/index.mjs +0 -8
- package/dist/service-adapters/shared/index.mjs.map +0 -1
- package/src/lib/error-messages.ts +0 -200
- package/src/lib/runtime/__tests__/copilot-runtime-trace.test.ts +0 -169
- package/src/service-adapters/shared/error-utils.ts +0 -61
- package/src/service-adapters/shared/index.ts +0 -1
- package/dist/{chunk-TOBFVWZU.mjs.map → chunk-MIPAKFI5.mjs.map} +0 -0
- package/dist/{chunk-VBXBFZEL.mjs.map → chunk-N24X5I3C.mjs.map} +0 -0
- package/dist/{chunk-6RUTA76W.mjs.map → chunk-WFYPJXWX.mjs.map} +0 -0
- 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
|
|
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
|
|
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
|
|
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: {} }
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
package/src/lib/streaming.ts
CHANGED
|
@@ -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
|
-
|
|
62
|
-
|
|
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
|
-
//
|
|
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:
|
|
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
|
-
//
|
|
107
|
+
// Default: convert unknown streaming errors
|
|
92
108
|
return new CopilotKitError({
|
|
93
|
-
message:
|
|
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
|
|
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
|
-
//
|
|
139
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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
|
|
151
|
+
content: message.result,
|
|
147
152
|
tool_use_id: message.actionExecutionId,
|
|
148
153
|
},
|
|
149
154
|
],
|