@copilotkit/runtime 1.9.2-next.3 → 1.9.2-next.4
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 +11 -0
- package/dist/{chunk-XDBXF3Q6.mjs → chunk-5FNEYAWH.mjs} +2 -2
- package/dist/{chunk-N24X5I3C.mjs → chunk-6W7TKZNY.mjs} +2 -2
- package/dist/{chunk-MIPAKFI5.mjs → chunk-CJ44W4R2.mjs} +2 -2
- package/dist/{chunk-WFYPJXWX.mjs → chunk-NK2XARWT.mjs} +2 -2
- package/dist/{chunk-GSYE3DGY.mjs → chunk-ZUF3A5TW.mjs} +197 -16
- package/dist/chunk-ZUF3A5TW.mjs.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +196 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5 -5
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +196 -15
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +5 -5
- package/dist/lib/integrations/index.d.ts +2 -2
- package/dist/lib/integrations/index.js +19 -10
- package/dist/lib/integrations/index.js.map +1 -1
- package/dist/lib/integrations/index.mjs +4 -4
- package/dist/lib/integrations/nest/index.d.ts +1 -1
- package/dist/lib/integrations/nest/index.js +19 -10
- package/dist/lib/integrations/nest/index.js.map +1 -1
- package/dist/lib/integrations/nest/index.mjs +2 -2
- package/dist/lib/integrations/node-express/index.d.ts +1 -1
- package/dist/lib/integrations/node-express/index.js +19 -10
- package/dist/lib/integrations/node-express/index.js.map +1 -1
- package/dist/lib/integrations/node-express/index.mjs +2 -2
- package/dist/lib/integrations/node-http/index.d.ts +1 -1
- package/dist/lib/integrations/node-http/index.js +19 -10
- package/dist/lib/integrations/node-http/index.js.map +1 -1
- package/dist/lib/integrations/node-http/index.mjs +1 -1
- package/dist/{shared-e272b15a.d.ts → shared-ba062831.d.ts} +35 -1
- package/package.json +2 -2
- package/src/graphql/resolvers/copilot.resolver.ts +24 -5
- package/src/lib/runtime/__tests__/copilot-runtime-trace.test.ts +169 -0
- package/src/lib/runtime/copilot-runtime.ts +254 -6
- package/dist/chunk-GSYE3DGY.mjs.map +0 -1
- /package/dist/{chunk-XDBXF3Q6.mjs.map → chunk-5FNEYAWH.mjs.map} +0 -0
- /package/dist/{chunk-N24X5I3C.mjs.map → chunk-6W7TKZNY.mjs.map} +0 -0
- /package/dist/{chunk-MIPAKFI5.mjs.map → chunk-CJ44W4R2.mjs.map} +0 -0
- /package/dist/{chunk-WFYPJXWX.mjs.map → chunk-NK2XARWT.mjs.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as graphql from 'graphql';
|
|
2
2
|
import * as pino from 'pino';
|
|
3
3
|
import { YogaInitialContext, createYoga } from 'graphql-yoga';
|
|
4
|
-
import { Parameter, Action } from '@copilotkit/shared';
|
|
4
|
+
import { Parameter, Action, CopilotTraceHandler } from '@copilotkit/shared';
|
|
5
5
|
import { b as CopilotServiceAdapter, A as ActionInput, d as AgentSessionInput, e as AgentStateInput, F as ForwardedParametersInput, E as ExtensionsInput, R as RemoteChainParameters, f as RuntimeEventSource, g as ExtensionsResponse } from './langserve-4a5c9217.js';
|
|
6
6
|
import { M as MessageInput, a as Message } from './index-d4614f9b.js';
|
|
7
7
|
import { CopilotCloudOptions } from './lib/cloud/index.js';
|
|
@@ -338,6 +338,24 @@ interface CopilotRuntimeConstructorParams<T extends Parameter[] | [] = []> {
|
|
|
338
338
|
* ```
|
|
339
339
|
*/
|
|
340
340
|
createMCPClient?: CreateMCPClientFunction;
|
|
341
|
+
/**
|
|
342
|
+
* Optional trace handler for comprehensive debugging and observability.
|
|
343
|
+
*
|
|
344
|
+
* **Requires publicApiKey**: Tracing only works when requests include a valid publicApiKey.
|
|
345
|
+
* This is a premium CopilotKit Cloud feature.
|
|
346
|
+
*
|
|
347
|
+
* @param traceEvent - Structured trace event with rich debugging context
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* ```typescript
|
|
351
|
+
* const runtime = new CopilotRuntime({
|
|
352
|
+
* onTrace: (traceEvent) => {
|
|
353
|
+
* debugDashboard.capture(traceEvent);
|
|
354
|
+
* }
|
|
355
|
+
* });
|
|
356
|
+
* ```
|
|
357
|
+
*/
|
|
358
|
+
onTrace?: CopilotTraceHandler;
|
|
341
359
|
}
|
|
342
360
|
declare class CopilotRuntime<const T extends Parameter[] | [] = []> {
|
|
343
361
|
actions: ActionsConfiguration<T>;
|
|
@@ -349,6 +367,8 @@ declare class CopilotRuntime<const T extends Parameter[] | [] = []> {
|
|
|
349
367
|
private delegateAgentProcessingToServiceAdapter;
|
|
350
368
|
private observability?;
|
|
351
369
|
private availableAgents;
|
|
370
|
+
private onTrace?;
|
|
371
|
+
private hasWarnedAboutTracing;
|
|
352
372
|
private readonly mcpServersConfig?;
|
|
353
373
|
private mcpActionCache;
|
|
354
374
|
private readonly createMCPClientImpl?;
|
|
@@ -361,6 +381,20 @@ declare class CopilotRuntime<const T extends Parameter[] | [] = []> {
|
|
|
361
381
|
private getServerSideActions;
|
|
362
382
|
private detectProvider;
|
|
363
383
|
private convertStreamingErrorToStructured;
|
|
384
|
+
private trace;
|
|
385
|
+
/**
|
|
386
|
+
* Public method to trace GraphQL validation errors
|
|
387
|
+
* This allows the GraphQL resolver to send validation errors through the trace system
|
|
388
|
+
*/
|
|
389
|
+
traceGraphQLError(error: {
|
|
390
|
+
message: string;
|
|
391
|
+
code: string;
|
|
392
|
+
type: string;
|
|
393
|
+
}, context: {
|
|
394
|
+
operation: string;
|
|
395
|
+
cloudConfigPresent: boolean;
|
|
396
|
+
guardrailsEnabled: boolean;
|
|
397
|
+
}): Promise<void>;
|
|
364
398
|
}
|
|
365
399
|
declare function flattenToolCallsNoDuplicates(toolsByPriority: ActionInput[]): ActionInput[];
|
|
366
400
|
declare function copilotKitEndpoint(config: Omit<CopilotKitEndpoint, "type">): CopilotKitEndpoint;
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|
|
11
11
|
},
|
|
12
|
-
"version": "1.9.2-next.
|
|
12
|
+
"version": "1.9.2-next.4",
|
|
13
13
|
"sideEffects": false,
|
|
14
14
|
"main": "./dist/index.js",
|
|
15
15
|
"module": "./dist/index.mjs",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"rxjs": "7.8.1",
|
|
67
67
|
"type-graphql": "2.0.0-rc.1",
|
|
68
68
|
"zod": "^3.23.3",
|
|
69
|
-
"@copilotkit/shared": "1.9.2-next.
|
|
69
|
+
"@copilotkit/shared": "1.9.2-next.4"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
72
|
"@ag-ui/client": ">=0.0.28",
|
|
@@ -174,15 +174,34 @@ export class CopilotResolver {
|
|
|
174
174
|
let copilotCloudPublicApiKey: string | null = null;
|
|
175
175
|
let copilotCloudBaseUrl: string;
|
|
176
176
|
|
|
177
|
+
// Extract publicApiKey from headers for both cloud and non-cloud requests
|
|
178
|
+
// This enables onTrace functionality regardless of cloud configuration
|
|
179
|
+
const publicApiKeyFromHeaders = ctx.request.headers.get("x-copilotcloud-public-api-key");
|
|
180
|
+
if (publicApiKeyFromHeaders) {
|
|
181
|
+
copilotCloudPublicApiKey = publicApiKeyFromHeaders;
|
|
182
|
+
}
|
|
183
|
+
|
|
177
184
|
if (data.cloud) {
|
|
178
185
|
logger = logger.child({ cloud: true });
|
|
179
186
|
logger.debug("Cloud configuration provided, checking for public API key in headers");
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
logger.debug("Public API key found in headers");
|
|
183
|
-
copilotCloudPublicApiKey = key;
|
|
184
|
-
} else {
|
|
187
|
+
|
|
188
|
+
if (!copilotCloudPublicApiKey) {
|
|
185
189
|
logger.error("Public API key not found in headers");
|
|
190
|
+
|
|
191
|
+
// Trace the validation error for debugging visibility
|
|
192
|
+
await copilotRuntime.traceGraphQLError(
|
|
193
|
+
{
|
|
194
|
+
message: "X-CopilotCloud-Public-API-Key header is required",
|
|
195
|
+
code: "MISSING_PUBLIC_API_KEY",
|
|
196
|
+
type: "GraphQLError",
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
operation: "generateCopilotResponse",
|
|
200
|
+
cloudConfigPresent: Boolean(data.cloud),
|
|
201
|
+
guardrailsEnabled: Boolean(data.cloud?.guardrails),
|
|
202
|
+
},
|
|
203
|
+
);
|
|
204
|
+
|
|
186
205
|
throw new GraphQLError("X-CopilotCloud-Public-API-Key header is required");
|
|
187
206
|
}
|
|
188
207
|
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { CopilotTraceEvent, CopilotRequestContext, CopilotTraceHandler } from "@copilotkit/shared";
|
|
2
|
+
|
|
3
|
+
describe("CopilotRuntime onTrace types", () => {
|
|
4
|
+
it("should have correct CopilotTraceEvent type structure", () => {
|
|
5
|
+
const traceEvent: CopilotTraceEvent = {
|
|
6
|
+
type: "error",
|
|
7
|
+
timestamp: Date.now(),
|
|
8
|
+
context: {
|
|
9
|
+
threadId: "test-123",
|
|
10
|
+
source: "runtime",
|
|
11
|
+
request: {
|
|
12
|
+
operation: "test-operation",
|
|
13
|
+
startTime: Date.now(),
|
|
14
|
+
},
|
|
15
|
+
technical: {},
|
|
16
|
+
metadata: {},
|
|
17
|
+
},
|
|
18
|
+
error: new Error("Test error"),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
expect(traceEvent.type).toBe("error");
|
|
22
|
+
expect(traceEvent.timestamp).toBeGreaterThan(0);
|
|
23
|
+
expect(traceEvent.context.threadId).toBe("test-123");
|
|
24
|
+
expect(traceEvent.error).toBeInstanceOf(Error);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should have correct CopilotRequestContext type structure", () => {
|
|
28
|
+
const context: CopilotRequestContext = {
|
|
29
|
+
threadId: "test-thread-456",
|
|
30
|
+
runId: "test-run-789",
|
|
31
|
+
source: "runtime",
|
|
32
|
+
request: {
|
|
33
|
+
operation: "processRuntimeRequest",
|
|
34
|
+
method: "POST",
|
|
35
|
+
url: "http://localhost:3000/api/copilotkit",
|
|
36
|
+
startTime: Date.now(),
|
|
37
|
+
},
|
|
38
|
+
response: {
|
|
39
|
+
status: 200,
|
|
40
|
+
endTime: Date.now(),
|
|
41
|
+
latency: 1200,
|
|
42
|
+
},
|
|
43
|
+
agent: {
|
|
44
|
+
name: "test-agent",
|
|
45
|
+
nodeName: "test-node",
|
|
46
|
+
state: { step: 1 },
|
|
47
|
+
},
|
|
48
|
+
messages: {
|
|
49
|
+
input: [],
|
|
50
|
+
output: [],
|
|
51
|
+
messageCount: 2,
|
|
52
|
+
},
|
|
53
|
+
technical: {
|
|
54
|
+
userAgent: "Mozilla/5.0...",
|
|
55
|
+
host: "localhost:3000",
|
|
56
|
+
environment: "test",
|
|
57
|
+
version: "1.0.0",
|
|
58
|
+
stackTrace: "Error: Test\n at test.js:1:1",
|
|
59
|
+
},
|
|
60
|
+
performance: {
|
|
61
|
+
requestDuration: 1200,
|
|
62
|
+
streamingDuration: 800,
|
|
63
|
+
actionExecutionTime: 400,
|
|
64
|
+
memoryUsage: 45.2,
|
|
65
|
+
},
|
|
66
|
+
metadata: {
|
|
67
|
+
testFlag: true,
|
|
68
|
+
version: "1.0.0",
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
expect(context.threadId).toBe("test-thread-456");
|
|
73
|
+
expect(context.agent?.name).toBe("test-agent");
|
|
74
|
+
expect(context.messages?.messageCount).toBe(2);
|
|
75
|
+
expect(context.technical?.stackTrace).toContain("Error: Test");
|
|
76
|
+
expect(context.metadata?.testFlag).toBe(true);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should support all trace event types", () => {
|
|
80
|
+
const eventTypes: CopilotTraceEvent["type"][] = [
|
|
81
|
+
"error",
|
|
82
|
+
"request",
|
|
83
|
+
"response",
|
|
84
|
+
"agent_state",
|
|
85
|
+
"action",
|
|
86
|
+
"message",
|
|
87
|
+
"performance",
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
eventTypes.forEach((type) => {
|
|
91
|
+
const event: CopilotTraceEvent = {
|
|
92
|
+
type,
|
|
93
|
+
timestamp: Date.now(),
|
|
94
|
+
context: {
|
|
95
|
+
threadId: `test-${type}`,
|
|
96
|
+
source: "runtime",
|
|
97
|
+
request: {
|
|
98
|
+
operation: "test",
|
|
99
|
+
startTime: Date.now(),
|
|
100
|
+
},
|
|
101
|
+
technical: {},
|
|
102
|
+
metadata: {},
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
expect(event.type).toBe(type);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe("publicApiKey gating logic", () => {
|
|
111
|
+
type ShouldTrace = (onTrace?: CopilotTraceHandler, publicApiKey?: string) => boolean;
|
|
112
|
+
|
|
113
|
+
const shouldTrace: ShouldTrace = (onTrace, publicApiKey) => {
|
|
114
|
+
return Boolean(onTrace && publicApiKey);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
it("should return true when both onTrace and publicApiKey are provided", () => {
|
|
118
|
+
const onTrace = jest.fn();
|
|
119
|
+
const result = shouldTrace(onTrace, "valid-api-key");
|
|
120
|
+
expect(result).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should return false when onTrace is missing", () => {
|
|
124
|
+
const result = shouldTrace(undefined, "valid-api-key");
|
|
125
|
+
expect(result).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("should return false when publicApiKey is missing", () => {
|
|
129
|
+
const onTrace = jest.fn();
|
|
130
|
+
const result = shouldTrace(onTrace, undefined);
|
|
131
|
+
expect(result).toBe(false);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("should return false when publicApiKey is empty string", () => {
|
|
135
|
+
const onTrace = jest.fn();
|
|
136
|
+
const result = shouldTrace(onTrace, "");
|
|
137
|
+
expect(result).toBe(false);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should return false when both are missing", () => {
|
|
141
|
+
const result = shouldTrace(undefined, undefined);
|
|
142
|
+
expect(result).toBe(false);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("should extract publicApiKey from headers for both cloud and non-cloud requests", () => {
|
|
146
|
+
// Test the logic we just fixed in the GraphQL resolver
|
|
147
|
+
const mockHeaders = new Map([["x-copilotcloud-public-api-key", "test-key-123"]]);
|
|
148
|
+
|
|
149
|
+
// Simulate header extraction logic
|
|
150
|
+
const extractPublicApiKey = (headers: Map<string, string>, hasCloudConfig: boolean) => {
|
|
151
|
+
const publicApiKeyFromHeaders = headers.get("x-copilotcloud-public-api-key");
|
|
152
|
+
return publicApiKeyFromHeaders || null;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Should work for cloud requests
|
|
156
|
+
const cloudKey = extractPublicApiKey(mockHeaders, true);
|
|
157
|
+
expect(cloudKey).toBe("test-key-123");
|
|
158
|
+
|
|
159
|
+
// Should also work for non-cloud requests (this was the bug)
|
|
160
|
+
const nonCloudKey = extractPublicApiKey(mockHeaders, false);
|
|
161
|
+
expect(nonCloudKey).toBe("test-key-123");
|
|
162
|
+
|
|
163
|
+
// Both should enable tracing when onTrace is present
|
|
164
|
+
const onTrace = jest.fn();
|
|
165
|
+
expect(shouldTrace(onTrace, cloudKey)).toBe(true);
|
|
166
|
+
expect(shouldTrace(onTrace, nonCloudKey)).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
|
@@ -25,6 +25,9 @@ import {
|
|
|
25
25
|
CopilotKitMisuseError,
|
|
26
26
|
CopilotKitErrorCode,
|
|
27
27
|
CopilotKitLowLevelError,
|
|
28
|
+
CopilotTraceHandler,
|
|
29
|
+
CopilotTraceEvent,
|
|
30
|
+
CopilotRequestContext,
|
|
28
31
|
} from "@copilotkit/shared";
|
|
29
32
|
import {
|
|
30
33
|
CopilotServiceAdapter,
|
|
@@ -274,6 +277,25 @@ export interface CopilotRuntimeConstructorParams<T extends Parameter[] | [] = []
|
|
|
274
277
|
* ```
|
|
275
278
|
*/
|
|
276
279
|
createMCPClient?: CreateMCPClientFunction;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Optional trace handler for comprehensive debugging and observability.
|
|
283
|
+
*
|
|
284
|
+
* **Requires publicApiKey**: Tracing only works when requests include a valid publicApiKey.
|
|
285
|
+
* This is a premium CopilotKit Cloud feature.
|
|
286
|
+
*
|
|
287
|
+
* @param traceEvent - Structured trace event with rich debugging context
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```typescript
|
|
291
|
+
* const runtime = new CopilotRuntime({
|
|
292
|
+
* onTrace: (traceEvent) => {
|
|
293
|
+
* debugDashboard.capture(traceEvent);
|
|
294
|
+
* }
|
|
295
|
+
* });
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
onTrace?: CopilotTraceHandler;
|
|
277
299
|
}
|
|
278
300
|
|
|
279
301
|
export class CopilotRuntime<const T extends Parameter[] | [] = []> {
|
|
@@ -286,6 +308,8 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
|
|
|
286
308
|
private delegateAgentProcessingToServiceAdapter: boolean;
|
|
287
309
|
private observability?: CopilotObservabilityConfig;
|
|
288
310
|
private availableAgents: Pick<AgentWithEndpoint, "name" | "id">[];
|
|
311
|
+
private onTrace?: CopilotTraceHandler;
|
|
312
|
+
private hasWarnedAboutTracing = false;
|
|
289
313
|
|
|
290
314
|
// +++ MCP Properties +++
|
|
291
315
|
private readonly mcpServersConfig?: MCPEndpointConfig[];
|
|
@@ -320,6 +344,7 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
|
|
|
320
344
|
params?.delegateAgentProcessingToServiceAdapter || false;
|
|
321
345
|
this.observability = params?.observability_c;
|
|
322
346
|
this.agents = params?.agents ?? {};
|
|
347
|
+
this.onTrace = params?.onTrace;
|
|
323
348
|
// +++ MCP Initialization +++
|
|
324
349
|
this.mcpServersConfig = params?.mcpServers;
|
|
325
350
|
this.createMCPClientImpl = params?.createMCPClient;
|
|
@@ -452,6 +477,32 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
|
|
|
452
477
|
// For storing streamed chunks if progressive logging is enabled
|
|
453
478
|
const streamedChunks: any[] = [];
|
|
454
479
|
|
|
480
|
+
// Trace request start
|
|
481
|
+
await this.trace(
|
|
482
|
+
"request",
|
|
483
|
+
{
|
|
484
|
+
threadId,
|
|
485
|
+
runId,
|
|
486
|
+
source: "runtime",
|
|
487
|
+
request: {
|
|
488
|
+
operation: "processRuntimeRequest",
|
|
489
|
+
method: "POST",
|
|
490
|
+
url: url,
|
|
491
|
+
startTime: requestStartTime,
|
|
492
|
+
},
|
|
493
|
+
agent: agentSession ? { name: agentSession.agentName } : undefined,
|
|
494
|
+
messages: {
|
|
495
|
+
input: rawMessages,
|
|
496
|
+
messageCount: rawMessages.length,
|
|
497
|
+
},
|
|
498
|
+
technical: {
|
|
499
|
+
environment: process.env.NODE_ENV,
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
undefined,
|
|
503
|
+
publicApiKey,
|
|
504
|
+
);
|
|
505
|
+
|
|
455
506
|
try {
|
|
456
507
|
if (
|
|
457
508
|
Object.keys(this.agents).length &&
|
|
@@ -675,13 +726,43 @@ please use an LLM adapter instead.`,
|
|
|
675
726
|
}
|
|
676
727
|
}
|
|
677
728
|
|
|
729
|
+
let structuredError: CopilotKitError;
|
|
730
|
+
|
|
678
731
|
if (error instanceof CopilotKitError) {
|
|
679
|
-
|
|
732
|
+
structuredError = error;
|
|
733
|
+
} else {
|
|
734
|
+
// Convert non-CopilotKitErrors to structured errors
|
|
735
|
+
console.error("Error getting response:", error);
|
|
736
|
+
structuredError = this.convertStreamingErrorToStructured(error);
|
|
680
737
|
}
|
|
681
738
|
|
|
682
|
-
//
|
|
683
|
-
|
|
684
|
-
|
|
739
|
+
// Trace the error
|
|
740
|
+
await this.trace(
|
|
741
|
+
"error",
|
|
742
|
+
{
|
|
743
|
+
threadId,
|
|
744
|
+
runId,
|
|
745
|
+
source: "runtime",
|
|
746
|
+
request: {
|
|
747
|
+
operation: "processRuntimeRequest",
|
|
748
|
+
method: "POST",
|
|
749
|
+
url: url,
|
|
750
|
+
startTime: requestStartTime,
|
|
751
|
+
},
|
|
752
|
+
response: {
|
|
753
|
+
endTime: Date.now(),
|
|
754
|
+
latency: Date.now() - requestStartTime,
|
|
755
|
+
},
|
|
756
|
+
agent: agentSession ? { name: agentSession.agentName } : undefined,
|
|
757
|
+
technical: {
|
|
758
|
+
environment: process.env.NODE_ENV,
|
|
759
|
+
stackTrace: error instanceof Error ? error.stack : undefined,
|
|
760
|
+
},
|
|
761
|
+
},
|
|
762
|
+
structuredError,
|
|
763
|
+
publicApiKey,
|
|
764
|
+
);
|
|
765
|
+
|
|
685
766
|
throw structuredError;
|
|
686
767
|
}
|
|
687
768
|
}
|
|
@@ -884,6 +965,33 @@ please use an LLM adapter instead.`,
|
|
|
884
965
|
// for backwards compatibility, deal with the case when no threadId is provided
|
|
885
966
|
const threadId = threadIdFromRequest ?? agentSession.threadId;
|
|
886
967
|
|
|
968
|
+
// Trace agent request start
|
|
969
|
+
await this.trace(
|
|
970
|
+
"agent_state",
|
|
971
|
+
{
|
|
972
|
+
threadId,
|
|
973
|
+
source: "agent",
|
|
974
|
+
request: {
|
|
975
|
+
operation: "processAgentRequest",
|
|
976
|
+
method: "POST",
|
|
977
|
+
startTime: requestStartTime,
|
|
978
|
+
},
|
|
979
|
+
agent: {
|
|
980
|
+
name: agentName,
|
|
981
|
+
nodeName: nodeName,
|
|
982
|
+
},
|
|
983
|
+
messages: {
|
|
984
|
+
input: rawMessages,
|
|
985
|
+
messageCount: rawMessages.length,
|
|
986
|
+
},
|
|
987
|
+
technical: {
|
|
988
|
+
environment: process.env.NODE_ENV,
|
|
989
|
+
},
|
|
990
|
+
},
|
|
991
|
+
undefined,
|
|
992
|
+
publicApiKey,
|
|
993
|
+
);
|
|
994
|
+
|
|
887
995
|
const serverSideActions = await this.getServerSideActions(request);
|
|
888
996
|
|
|
889
997
|
const messages = convertGqlInputToMessages(rawMessages);
|
|
@@ -1011,7 +1119,7 @@ please use an LLM adapter instead.`,
|
|
|
1011
1119
|
eventSource.stream(async (eventStream$) => {
|
|
1012
1120
|
from(stream).subscribe({
|
|
1013
1121
|
next: (event) => eventStream$.next(event),
|
|
1014
|
-
error: (err) => {
|
|
1122
|
+
error: async (err) => {
|
|
1015
1123
|
console.error("Error in stream", err);
|
|
1016
1124
|
|
|
1017
1125
|
// Log error with observability if enabled
|
|
@@ -1037,6 +1145,35 @@ please use an LLM adapter instead.`,
|
|
|
1037
1145
|
|
|
1038
1146
|
// Convert network termination errors to structured errors
|
|
1039
1147
|
const structuredError = this.convertStreamingErrorToStructured(err);
|
|
1148
|
+
|
|
1149
|
+
// Trace streaming errors
|
|
1150
|
+
await this.trace(
|
|
1151
|
+
"error",
|
|
1152
|
+
{
|
|
1153
|
+
threadId,
|
|
1154
|
+
source: "agent",
|
|
1155
|
+
request: {
|
|
1156
|
+
operation: "processAgentRequest",
|
|
1157
|
+
method: "POST",
|
|
1158
|
+
startTime: requestStartTime,
|
|
1159
|
+
},
|
|
1160
|
+
response: {
|
|
1161
|
+
endTime: Date.now(),
|
|
1162
|
+
latency: Date.now() - requestStartTime,
|
|
1163
|
+
},
|
|
1164
|
+
agent: {
|
|
1165
|
+
name: agentName,
|
|
1166
|
+
nodeName: nodeName,
|
|
1167
|
+
},
|
|
1168
|
+
technical: {
|
|
1169
|
+
environment: process.env.NODE_ENV,
|
|
1170
|
+
stackTrace: err instanceof Error ? err.stack : undefined,
|
|
1171
|
+
},
|
|
1172
|
+
},
|
|
1173
|
+
structuredError,
|
|
1174
|
+
publicApiKey,
|
|
1175
|
+
);
|
|
1176
|
+
|
|
1040
1177
|
eventStream$.error(structuredError);
|
|
1041
1178
|
eventStream$.complete();
|
|
1042
1179
|
},
|
|
@@ -1114,8 +1251,44 @@ please use an LLM adapter instead.`,
|
|
|
1114
1251
|
}
|
|
1115
1252
|
}
|
|
1116
1253
|
|
|
1254
|
+
// Ensure error is structured
|
|
1255
|
+
let structuredError: CopilotKitError;
|
|
1256
|
+
if (error instanceof CopilotKitError) {
|
|
1257
|
+
structuredError = error;
|
|
1258
|
+
} else {
|
|
1259
|
+
structuredError = this.convertStreamingErrorToStructured(error);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// Trace the agent error
|
|
1263
|
+
await this.trace(
|
|
1264
|
+
"error",
|
|
1265
|
+
{
|
|
1266
|
+
threadId,
|
|
1267
|
+
source: "agent",
|
|
1268
|
+
request: {
|
|
1269
|
+
operation: "processAgentRequest",
|
|
1270
|
+
method: "POST",
|
|
1271
|
+
startTime: requestStartTime,
|
|
1272
|
+
},
|
|
1273
|
+
response: {
|
|
1274
|
+
endTime: Date.now(),
|
|
1275
|
+
latency: Date.now() - requestStartTime,
|
|
1276
|
+
},
|
|
1277
|
+
agent: {
|
|
1278
|
+
name: agentName,
|
|
1279
|
+
nodeName: nodeName,
|
|
1280
|
+
},
|
|
1281
|
+
technical: {
|
|
1282
|
+
environment: process.env.NODE_ENV,
|
|
1283
|
+
stackTrace: error instanceof Error ? error.stack : undefined,
|
|
1284
|
+
},
|
|
1285
|
+
},
|
|
1286
|
+
structuredError,
|
|
1287
|
+
publicApiKey,
|
|
1288
|
+
);
|
|
1289
|
+
|
|
1117
1290
|
console.error("Error getting response:", error);
|
|
1118
|
-
throw
|
|
1291
|
+
throw structuredError;
|
|
1119
1292
|
}
|
|
1120
1293
|
}
|
|
1121
1294
|
|
|
@@ -1278,6 +1451,81 @@ please use an LLM adapter instead.`,
|
|
|
1278
1451
|
code: CopilotKitErrorCode.UNKNOWN,
|
|
1279
1452
|
});
|
|
1280
1453
|
}
|
|
1454
|
+
|
|
1455
|
+
private async trace(
|
|
1456
|
+
type: CopilotTraceEvent["type"],
|
|
1457
|
+
context: CopilotRequestContext,
|
|
1458
|
+
error?: any,
|
|
1459
|
+
publicApiKey?: string,
|
|
1460
|
+
): Promise<void> {
|
|
1461
|
+
if (!this.onTrace) return;
|
|
1462
|
+
|
|
1463
|
+
if (!publicApiKey) {
|
|
1464
|
+
if (!this.hasWarnedAboutTracing) {
|
|
1465
|
+
console.warn(
|
|
1466
|
+
"CopilotKit: onTrace handler provided but requires publicApiKey for tracing to work. " +
|
|
1467
|
+
"This is a CopilotKit Cloud feature. See: https://docs.copilotkit.ai/cloud",
|
|
1468
|
+
);
|
|
1469
|
+
this.hasWarnedAboutTracing = true;
|
|
1470
|
+
}
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
try {
|
|
1475
|
+
const traceEvent: CopilotTraceEvent = {
|
|
1476
|
+
type,
|
|
1477
|
+
timestamp: Date.now(),
|
|
1478
|
+
context,
|
|
1479
|
+
...(error && { error }),
|
|
1480
|
+
};
|
|
1481
|
+
|
|
1482
|
+
await this.onTrace(traceEvent);
|
|
1483
|
+
} catch (traceError) {
|
|
1484
|
+
// Don't let trace errors break the main flow
|
|
1485
|
+
console.error("Error in onTrace handler:", traceError);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
/**
|
|
1490
|
+
* Public method to trace GraphQL validation errors
|
|
1491
|
+
* This allows the GraphQL resolver to send validation errors through the trace system
|
|
1492
|
+
*/
|
|
1493
|
+
public async traceGraphQLError(
|
|
1494
|
+
error: { message: string; code: string; type: string },
|
|
1495
|
+
context: {
|
|
1496
|
+
operation: string;
|
|
1497
|
+
cloudConfigPresent: boolean;
|
|
1498
|
+
guardrailsEnabled: boolean;
|
|
1499
|
+
},
|
|
1500
|
+
): Promise<void> {
|
|
1501
|
+
if (!this.onTrace) return;
|
|
1502
|
+
|
|
1503
|
+
try {
|
|
1504
|
+
await this.onTrace({
|
|
1505
|
+
type: "error",
|
|
1506
|
+
timestamp: Date.now(),
|
|
1507
|
+
context: {
|
|
1508
|
+
source: "runtime",
|
|
1509
|
+
request: {
|
|
1510
|
+
operation: context.operation,
|
|
1511
|
+
startTime: Date.now(),
|
|
1512
|
+
},
|
|
1513
|
+
technical: {
|
|
1514
|
+
environment: process.env.NODE_ENV,
|
|
1515
|
+
},
|
|
1516
|
+
metadata: {
|
|
1517
|
+
errorType: "GraphQLValidationError",
|
|
1518
|
+
cloudConfigPresent: context.cloudConfigPresent,
|
|
1519
|
+
guardrailsEnabled: context.guardrailsEnabled,
|
|
1520
|
+
},
|
|
1521
|
+
},
|
|
1522
|
+
error,
|
|
1523
|
+
});
|
|
1524
|
+
} catch (traceError) {
|
|
1525
|
+
// Don't let trace errors break the main flow
|
|
1526
|
+
console.error("Error in onTrace handler:", traceError);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1281
1529
|
}
|
|
1282
1530
|
|
|
1283
1531
|
export function flattenToolCallsNoDuplicates(toolsByPriority: ActionInput[]): ActionInput[] {
|