@copilotkit/runtime 1.56.2 → 1.56.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/dist/graphql/resolvers/copilot.resolver.cjs +2 -1
- package/dist/graphql/resolvers/copilot.resolver.cjs.map +1 -1
- package/dist/graphql/resolvers/copilot.resolver.mjs +2 -1
- package/dist/graphql/resolvers/copilot.resolver.mjs.map +1 -1
- package/dist/graphql/resolvers/resolve-message-id.cjs +19 -0
- package/dist/graphql/resolvers/resolve-message-id.cjs.map +1 -0
- package/dist/graphql/resolvers/resolve-message-id.mjs +18 -0
- package/dist/graphql/resolvers/resolve-message-id.mjs.map +1 -0
- package/dist/lib/runtime/copilot-runtime.cjs +4 -2
- package/dist/lib/runtime/copilot-runtime.cjs.map +1 -1
- package/dist/lib/runtime/copilot-runtime.d.cts.map +1 -1
- package/dist/lib/runtime/copilot-runtime.d.mts.map +1 -1
- package/dist/lib/runtime/copilot-runtime.mjs +4 -2
- package/dist/lib/runtime/copilot-runtime.mjs.map +1 -1
- package/dist/package.cjs +2 -2
- package/dist/package.mjs +2 -2
- package/dist/v2/index.d.cts +2 -2
- package/dist/v2/index.d.mts +2 -2
- package/dist/v2/runtime/core/debug-event-bus.cjs +36 -0
- package/dist/v2/runtime/core/debug-event-bus.cjs.map +1 -0
- package/dist/v2/runtime/core/debug-event-bus.d.cts +19 -0
- package/dist/v2/runtime/core/debug-event-bus.d.cts.map +1 -0
- package/dist/v2/runtime/core/debug-event-bus.d.mts +19 -0
- package/dist/v2/runtime/core/debug-event-bus.d.mts.map +1 -0
- package/dist/v2/runtime/core/debug-event-bus.mjs +35 -0
- package/dist/v2/runtime/core/debug-event-bus.mjs.map +1 -0
- package/dist/v2/runtime/core/fetch-handler.cjs +8 -0
- package/dist/v2/runtime/core/fetch-handler.cjs.map +1 -1
- package/dist/v2/runtime/core/fetch-handler.d.cts.map +1 -1
- package/dist/v2/runtime/core/fetch-handler.d.mts.map +1 -1
- package/dist/v2/runtime/core/fetch-handler.mjs +8 -0
- package/dist/v2/runtime/core/fetch-handler.mjs.map +1 -1
- package/dist/v2/runtime/core/fetch-router.cjs +1 -0
- package/dist/v2/runtime/core/fetch-router.cjs.map +1 -1
- package/dist/v2/runtime/core/fetch-router.mjs +1 -0
- package/dist/v2/runtime/core/fetch-router.mjs.map +1 -1
- package/dist/v2/runtime/core/hooks.cjs.map +1 -1
- package/dist/v2/runtime/core/hooks.d.cts +2 -0
- package/dist/v2/runtime/core/hooks.d.cts.map +1 -1
- package/dist/v2/runtime/core/hooks.d.mts +2 -0
- package/dist/v2/runtime/core/hooks.d.mts.map +1 -1
- package/dist/v2/runtime/core/hooks.mjs.map +1 -1
- package/dist/v2/runtime/core/runtime.cjs +5 -0
- package/dist/v2/runtime/core/runtime.cjs.map +1 -1
- package/dist/v2/runtime/core/runtime.d.cts +5 -0
- package/dist/v2/runtime/core/runtime.d.cts.map +1 -1
- package/dist/v2/runtime/core/runtime.d.mts +5 -1
- package/dist/v2/runtime/core/runtime.d.mts.map +1 -1
- package/dist/v2/runtime/core/runtime.mjs +5 -0
- package/dist/v2/runtime/core/runtime.mjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-connect.cjs +3 -2
- package/dist/v2/runtime/handlers/handle-connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-connect.mjs +3 -2
- package/dist/v2/runtime/handlers/handle-connect.mjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-debug-events.cjs +33 -0
- package/dist/v2/runtime/handlers/handle-debug-events.cjs.map +1 -0
- package/dist/v2/runtime/handlers/handle-debug-events.mjs +32 -0
- package/dist/v2/runtime/handlers/handle-debug-events.mjs.map +1 -0
- package/dist/v2/runtime/handlers/handle-run.cjs +1 -0
- package/dist/v2/runtime/handlers/handle-run.cjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-run.mjs +1 -0
- package/dist/v2/runtime/handlers/handle-run.mjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/connect.cjs +24 -4
- package/dist/v2/runtime/handlers/intelligence/connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/connect.mjs +25 -5
- package/dist/v2/runtime/handlers/intelligence/connect.mjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/run.cjs +111 -26
- package/dist/v2/runtime/handlers/intelligence/run.cjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/run.mjs +111 -26
- package/dist/v2/runtime/handlers/intelligence/run.mjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/intelligence-utils.cjs +7 -3
- package/dist/v2/runtime/handlers/shared/intelligence-utils.cjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/intelligence-utils.mjs +7 -3
- package/dist/v2/runtime/handlers/shared/intelligence-utils.mjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/resolve-intelligence-user.cjs +5 -1
- package/dist/v2/runtime/handlers/shared/resolve-intelligence-user.cjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/resolve-intelligence-user.mjs +5 -1
- package/dist/v2/runtime/handlers/shared/resolve-intelligence-user.mjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/sse-response.cjs +21 -1
- package/dist/v2/runtime/handlers/shared/sse-response.cjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/sse-response.mjs +21 -1
- package/dist/v2/runtime/handlers/shared/sse-response.mjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/connect.cjs +3 -1
- package/dist/v2/runtime/handlers/sse/connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/connect.mjs +3 -1
- package/dist/v2/runtime/handlers/sse/connect.mjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/run.cjs +3 -1
- package/dist/v2/runtime/handlers/sse/run.cjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/run.mjs +3 -1
- package/dist/v2/runtime/handlers/sse/run.mjs.map +1 -1
- package/dist/v2/runtime/index.d.cts +1 -1
- package/dist/v2/runtime/index.d.mts +1 -2
- package/dist/v2/runtime/index.d.mts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.cjs +6 -8
- package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.d.cts +16 -21
- package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.d.mts +16 -21
- package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.mjs +6 -8
- package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
- package/dist/v2/runtime/runner/agent-runner.cjs.map +1 -1
- package/dist/v2/runtime/runner/agent-runner.d.cts +0 -1
- package/dist/v2/runtime/runner/agent-runner.d.cts.map +1 -1
- package/dist/v2/runtime/runner/agent-runner.d.mts +0 -1
- package/dist/v2/runtime/runner/agent-runner.d.mts.map +1 -1
- package/dist/v2/runtime/runner/agent-runner.mjs.map +1 -1
- package/dist/v2/runtime/runner/index.d.cts +1 -1
- package/dist/v2/runtime/runner/index.d.mts +1 -1
- package/dist/v2/runtime/runner/intelligence.cjs +47 -10
- package/dist/v2/runtime/runner/intelligence.cjs.map +1 -1
- package/dist/v2/runtime/runner/intelligence.d.cts +8 -1
- package/dist/v2/runtime/runner/intelligence.d.cts.map +1 -1
- package/dist/v2/runtime/runner/intelligence.d.mts +8 -1
- package/dist/v2/runtime/runner/intelligence.d.mts.map +1 -1
- package/dist/v2/runtime/runner/intelligence.mjs +47 -10
- package/dist/v2/runtime/runner/intelligence.mjs.map +1 -1
- package/dist/v2/runtime/telemetry/instance-created.cjs +33 -0
- package/dist/v2/runtime/telemetry/instance-created.cjs.map +1 -0
- package/dist/v2/runtime/telemetry/instance-created.mjs +33 -0
- package/dist/v2/runtime/telemetry/instance-created.mjs.map +1 -0
- package/dist/v2/runtime/telemetry/telemetry-client.cjs +1 -38
- package/dist/v2/runtime/telemetry/telemetry-client.cjs.map +1 -1
- package/dist/v2/runtime/telemetry/telemetry-client.mjs +1 -37
- package/dist/v2/runtime/telemetry/telemetry-client.mjs.map +1 -1
- package/package.json +3 -3
- package/src/agents/langgraph/__tests__/event-source.test.ts +256 -0
- package/src/graphql/resolvers/__tests__/resolve-message-id.test.ts +25 -0
- package/src/graphql/resolvers/copilot.resolver.ts +2 -1
- package/src/graphql/resolvers/resolve-message-id.ts +14 -0
- package/src/lib/runtime/__tests__/handle-service-adapter.test.ts +108 -0
- package/src/lib/runtime/__tests__/retry-utils.test.ts +137 -0
- package/src/lib/runtime/agent-integrations/langgraph/__tests__/dispatch-event-filtering.test.ts +190 -0
- package/src/lib/runtime/copilot-runtime.ts +20 -4
- package/src/lib/runtime/retry-utils.ts +41 -1
- package/src/v2/runtime/__tests__/express-single-telemetry.integration.test.ts +65 -0
- package/src/v2/runtime/__tests__/express-telemetry.integration.test.ts +101 -0
- package/src/v2/runtime/__tests__/fetch-router.test.ts +22 -0
- package/src/v2/runtime/__tests__/handle-connect.test.ts +183 -23
- package/src/v2/runtime/__tests__/handle-run.test.ts +411 -33
- package/src/v2/runtime/__tests__/handle-threads.test.ts +66 -4
- package/src/v2/runtime/__tests__/hono-single-telemetry.integration.test.ts +46 -0
- package/src/v2/runtime/__tests__/hono-telemetry.integration.test.ts +99 -0
- package/src/v2/runtime/__tests__/integration/node-servers.integration.test.ts +19 -0
- package/src/v2/runtime/__tests__/integration/suites/debug-events.suite.ts +253 -0
- package/src/v2/runtime/__tests__/intelligence-run-telemetry.test.ts +194 -0
- package/src/v2/runtime/__tests__/runtime.test.ts +3 -1
- package/src/v2/runtime/__tests__/sse-response-telemetry.test.ts +108 -0
- package/src/v2/runtime/__tests__/telemetry.test.ts +0 -61
- package/src/v2/runtime/core/__tests__/debug-event-bus.test.ts +156 -0
- package/src/v2/runtime/core/debug-event-bus.ts +45 -0
- package/src/v2/runtime/core/fetch-handler.ts +7 -0
- package/src/v2/runtime/core/fetch-router.ts +11 -0
- package/src/v2/runtime/core/hooks.ts +2 -1
- package/src/v2/runtime/core/runtime.ts +12 -0
- package/src/v2/runtime/handlers/__tests__/handle-debug-events.test.ts +176 -0
- package/src/v2/runtime/handlers/handle-connect.ts +2 -1
- package/src/v2/runtime/handlers/handle-debug-events.ts +52 -0
- package/src/v2/runtime/handlers/handle-run.ts +1 -0
- package/src/v2/runtime/handlers/intelligence/connect.ts +48 -11
- package/src/v2/runtime/handlers/intelligence/run.ts +162 -21
- package/src/v2/runtime/handlers/shared/intelligence-utils.ts +21 -1
- package/src/v2/runtime/handlers/shared/resolve-intelligence-user.ts +4 -1
- package/src/v2/runtime/handlers/shared/sse-response.ts +46 -0
- package/src/v2/runtime/handlers/sse/__tests__/sse-connect-agent-id.test.ts +71 -0
- package/src/v2/runtime/handlers/sse/connect.ts +6 -0
- package/src/v2/runtime/handlers/sse/run.ts +4 -0
- package/src/v2/runtime/intelligence-platform/__tests__/client.test.ts +33 -37
- package/src/v2/runtime/intelligence-platform/client.ts +37 -40
- package/src/v2/runtime/runner/__tests__/intelligence-runner.test.ts +66 -8
- package/src/v2/runtime/runner/agent-runner.ts +0 -1
- package/src/v2/runtime/runner/intelligence.ts +74 -15
- package/src/v2/runtime/telemetry/__tests__/instance-created.test.ts +96 -0
- package/src/v2/runtime/telemetry/instance-created.ts +44 -0
- package/src/v2/runtime/telemetry/telemetry-client.ts +1 -57
- package/dist/v2/runtime/intelligence-platform/index.d.mts +0 -2
- package/dist/v2/runtime/telemetry/utils.cjs +0 -15
- package/dist/v2/runtime/telemetry/utils.cjs.map +0 -1
- package/dist/v2/runtime/telemetry/utils.mjs +0 -14
- package/dist/v2/runtime/telemetry/utils.mjs.map +0 -1
- package/src/v2/runtime/telemetry/utils.ts +0 -15
package/src/lib/runtime/agent-integrations/langgraph/__tests__/dispatch-event-filtering.test.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { EventType } from "@ag-ui/client";
|
|
2
2
|
import { LangGraphAgent } from "../agent";
|
|
3
|
+
import { CustomEventNames } from "../consts";
|
|
4
|
+
import { vi } from "vitest";
|
|
3
5
|
|
|
4
6
|
function createAgent() {
|
|
5
7
|
const agent = new LangGraphAgent({
|
|
@@ -33,6 +35,25 @@ function makeTextEvent(
|
|
|
33
35
|
};
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
function makeCustomEvent(name: string, value: any) {
|
|
39
|
+
return {
|
|
40
|
+
type: EventType.CUSTOM,
|
|
41
|
+
name,
|
|
42
|
+
value,
|
|
43
|
+
} as any;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Mock the parent class's langGraphDefaultMergeState for a single test,
|
|
48
|
+
* using vi.spyOn for automatic cleanup.
|
|
49
|
+
*/
|
|
50
|
+
function withMockedParentMerge(agent: LangGraphAgent, returnValue: any) {
|
|
51
|
+
const parentProto = Object.getPrototypeOf(Object.getPrototypeOf(agent));
|
|
52
|
+
return vi
|
|
53
|
+
.spyOn(parentProto, "langGraphDefaultMergeState")
|
|
54
|
+
.mockReturnValue(returnValue);
|
|
55
|
+
}
|
|
56
|
+
|
|
36
57
|
describe("dispatchEvent emit-messages filtering", () => {
|
|
37
58
|
it("suppresses message events when copilotkit:emit-messages is false", () => {
|
|
38
59
|
const { agent, events } = createAgent();
|
|
@@ -153,3 +174,172 @@ describe("dispatchEvent emit-tool-calls filtering", () => {
|
|
|
153
174
|
expect(events).toHaveLength(1);
|
|
154
175
|
});
|
|
155
176
|
});
|
|
177
|
+
|
|
178
|
+
// ---------- CopilotKit custom event dispatch ----------
|
|
179
|
+
|
|
180
|
+
describe("dispatchEvent custom CopilotKit events", () => {
|
|
181
|
+
it("manually_emit_message produces TextMessage event sequence", () => {
|
|
182
|
+
const { agent, events } = createAgent();
|
|
183
|
+
|
|
184
|
+
const result = agent.dispatchEvent(
|
|
185
|
+
makeCustomEvent(CustomEventNames.CopilotKitManuallyEmitMessage, {
|
|
186
|
+
message_id: "msg-manual-1",
|
|
187
|
+
message: "Hello from agent",
|
|
188
|
+
role: "assistant",
|
|
189
|
+
}),
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
expect(result).toBe(true);
|
|
193
|
+
expect(events).toHaveLength(3);
|
|
194
|
+
expect(events[0].type).toBe(EventType.TEXT_MESSAGE_START);
|
|
195
|
+
expect(events[0].messageId).toBe("msg-manual-1");
|
|
196
|
+
expect(events[0].role).toBe("assistant");
|
|
197
|
+
expect(events[1].type).toBe(EventType.TEXT_MESSAGE_CONTENT);
|
|
198
|
+
expect(events[1].delta).toBe("Hello from agent");
|
|
199
|
+
expect(events[2].type).toBe(EventType.TEXT_MESSAGE_END);
|
|
200
|
+
expect(events[2].messageId).toBe("msg-manual-1");
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("manually_emit_tool_call produces ToolCall event sequence", () => {
|
|
204
|
+
const { agent, events } = createAgent();
|
|
205
|
+
|
|
206
|
+
const result = agent.dispatchEvent(
|
|
207
|
+
makeCustomEvent(CustomEventNames.CopilotKitManuallyEmitToolCall, {
|
|
208
|
+
id: "tc-manual-1",
|
|
209
|
+
name: "SearchTool",
|
|
210
|
+
args: { query: "test" },
|
|
211
|
+
}),
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
expect(result).toBe(true);
|
|
215
|
+
expect(events).toHaveLength(3);
|
|
216
|
+
expect(events[0].type).toBe(EventType.TOOL_CALL_START);
|
|
217
|
+
expect(events[0].toolCallId).toBe("tc-manual-1");
|
|
218
|
+
expect(events[0].toolCallName).toBe("SearchTool");
|
|
219
|
+
expect(events[1].type).toBe(EventType.TOOL_CALL_ARGS);
|
|
220
|
+
expect(events[1].toolCallId).toBe("tc-manual-1");
|
|
221
|
+
expect(events[1].delta).toEqual({ query: "test" });
|
|
222
|
+
expect(events[2].type).toBe(EventType.TOOL_CALL_END);
|
|
223
|
+
expect(events[2].toolCallId).toBe("tc-manual-1");
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("manually_emit_state produces StateSnapshot event", () => {
|
|
227
|
+
const { agent, events } = createAgent();
|
|
228
|
+
|
|
229
|
+
// Mock getStateSnapshot since it depends on thread state
|
|
230
|
+
(agent as any).getStateSnapshot = (state: any) => ({
|
|
231
|
+
values: state.values,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const result = agent.dispatchEvent(
|
|
235
|
+
makeCustomEvent(
|
|
236
|
+
CustomEventNames.CopilotKitManuallyEmitIntermediateState,
|
|
237
|
+
{
|
|
238
|
+
progress: 75,
|
|
239
|
+
},
|
|
240
|
+
),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
expect(result).toBe(true);
|
|
244
|
+
expect((agent as any).activeRun.manuallyEmittedState).toEqual({
|
|
245
|
+
progress: 75,
|
|
246
|
+
});
|
|
247
|
+
const snapshotEvents = events.filter(
|
|
248
|
+
(e) => e.type === EventType.STATE_SNAPSHOT,
|
|
249
|
+
);
|
|
250
|
+
expect(snapshotEvents.length).toBeGreaterThanOrEqual(1);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("copilotkit_exit produces Exit custom event", () => {
|
|
254
|
+
const { agent, events } = createAgent();
|
|
255
|
+
|
|
256
|
+
const result = agent.dispatchEvent(
|
|
257
|
+
makeCustomEvent(CustomEventNames.CopilotKitExit, {}),
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
expect(result).toBe(true);
|
|
261
|
+
expect(events).toHaveLength(1);
|
|
262
|
+
expect(events[0].type).toBe(EventType.CUSTOM);
|
|
263
|
+
// "Exit" is the hardcoded downstream name in agent.ts — not a constant,
|
|
264
|
+
// because it's the value the frontend listens for, not an internal enum.
|
|
265
|
+
expect(events[0].name).toBe("Exit");
|
|
266
|
+
expect(events[0].value).toBe(true);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it("events without rawEvent pass through to subscriber", () => {
|
|
270
|
+
const { agent, events } = createAgent();
|
|
271
|
+
|
|
272
|
+
const result = agent.dispatchEvent({
|
|
273
|
+
type: EventType.TEXT_MESSAGE_START,
|
|
274
|
+
messageId: "msg-no-raw",
|
|
275
|
+
role: "assistant",
|
|
276
|
+
} as any);
|
|
277
|
+
|
|
278
|
+
expect(result).toBe(true);
|
|
279
|
+
expect(events).toHaveLength(1);
|
|
280
|
+
expect(events[0].messageId).toBe("msg-no-raw");
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// ---------- langGraphDefaultMergeState ----------
|
|
285
|
+
|
|
286
|
+
describe("langGraphDefaultMergeState", () => {
|
|
287
|
+
afterEach(() => {
|
|
288
|
+
vi.restoreAllMocks();
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("merges copilotkit actions from ag-ui tools", () => {
|
|
292
|
+
const { agent } = createAgent();
|
|
293
|
+
const tools = [{ name: "tool1" }, { name: "tool2" }];
|
|
294
|
+
|
|
295
|
+
withMockedParentMerge(agent, {
|
|
296
|
+
"ag-ui": { tools, context: [] },
|
|
297
|
+
tools: [],
|
|
298
|
+
messages: [],
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const result = agent.langGraphDefaultMergeState({} as any, [], {} as any);
|
|
302
|
+
expect(result.copilotkit).toBeDefined();
|
|
303
|
+
expect(result.copilotkit.actions).toEqual(expect.arrayContaining(tools));
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("merges copilotkit context from ag-ui", () => {
|
|
307
|
+
const { agent } = createAgent();
|
|
308
|
+
const context = [{ description: "user info", value: "test" }];
|
|
309
|
+
|
|
310
|
+
withMockedParentMerge(agent, {
|
|
311
|
+
"ag-ui": { tools: [], context },
|
|
312
|
+
tools: [],
|
|
313
|
+
messages: [],
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
const result = agent.langGraphDefaultMergeState({} as any, [], {} as any);
|
|
317
|
+
expect(result.copilotkit.context).toEqual(context);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("handles missing ag-ui key without crashing", () => {
|
|
321
|
+
const { agent } = createAgent();
|
|
322
|
+
|
|
323
|
+
withMockedParentMerge(agent, { messages: [] });
|
|
324
|
+
|
|
325
|
+
const result = agent.langGraphDefaultMergeState({} as any, [], {} as any);
|
|
326
|
+
expect(result.copilotkit).toBeDefined();
|
|
327
|
+
expect(result.copilotkit.actions).toEqual([]);
|
|
328
|
+
expect(result.copilotkit.context).toEqual([]);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("deduplicates tools from returnedTools and ag-ui tools", () => {
|
|
332
|
+
const { agent } = createAgent();
|
|
333
|
+
const tool = { name: "SharedTool", id: "shared-1" };
|
|
334
|
+
|
|
335
|
+
withMockedParentMerge(agent, {
|
|
336
|
+
"ag-ui": { tools: [tool], context: [] },
|
|
337
|
+
tools: [tool],
|
|
338
|
+
messages: [],
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const result = agent.langGraphDefaultMergeState({} as any, [], {} as any);
|
|
342
|
+
expect(result.copilotkit.actions).toHaveLength(1);
|
|
343
|
+
expect(result.copilotkit.actions[0].name).toBe("SharedTool");
|
|
344
|
+
});
|
|
345
|
+
});
|
|
@@ -463,10 +463,26 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
|
|
|
463
463
|
}
|
|
464
464
|
|
|
465
465
|
if (isAgentsListEmpty) {
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
466
|
+
const languageModel = serviceAdapter.getLanguageModel?.();
|
|
467
|
+
if (languageModel) {
|
|
468
|
+
// Adapter exposes a pre-configured LanguageModel (e.g. OpenAI/Anthropic adapters)
|
|
469
|
+
agentsList.default = new BuiltInAgent({ model: languageModel });
|
|
470
|
+
} else if (serviceAdapter.provider && serviceAdapter.model) {
|
|
471
|
+
// Adapter exposes provider/model strings
|
|
472
|
+
agentsList.default = new BuiltInAgent({
|
|
473
|
+
model: `${serviceAdapter.provider}/${serviceAdapter.model}`,
|
|
474
|
+
});
|
|
475
|
+
} else {
|
|
476
|
+
throw new CopilotKitMisuseError({
|
|
477
|
+
message:
|
|
478
|
+
`Service adapter "${serviceAdapter.name ?? "unknown"}" does not provide model information. ` +
|
|
479
|
+
`When using adapters like LangChainAdapter without an explicit agents list, ` +
|
|
480
|
+
`please provide a default agent in the runtime config. Example:\n` +
|
|
481
|
+
` new CopilotRuntime({\n` +
|
|
482
|
+
` agents: { default: new BuiltInAgent({ model: "openai/gpt-4o" }) }\n` +
|
|
483
|
+
` })`,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
470
486
|
}
|
|
471
487
|
|
|
472
488
|
const actions = this.params?.actions;
|
|
@@ -7,6 +7,8 @@ export const RETRY_CONFIG = {
|
|
|
7
7
|
maxDelayMs: 5000,
|
|
8
8
|
// HTTP status codes that should be retried
|
|
9
9
|
retryableStatusCodes: [502, 503, 504, 408, 429],
|
|
10
|
+
// Maximum Retry-After value (in seconds) we're willing to honor
|
|
11
|
+
maxRetryAfterSeconds: 60,
|
|
10
12
|
// Network error patterns that should be retried
|
|
11
13
|
retryableErrorMessages: [
|
|
12
14
|
"fetch failed",
|
|
@@ -44,6 +46,28 @@ export function sleep(ms: number): Promise<void> {
|
|
|
44
46
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
// Parse the Retry-After header value into milliseconds.
|
|
50
|
+
// Returns undefined if the header is missing or unparseable.
|
|
51
|
+
export function parseRetryAfter(response: Response): number | undefined {
|
|
52
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
53
|
+
if (!retryAfter) return undefined;
|
|
54
|
+
|
|
55
|
+
// Try as seconds (integer)
|
|
56
|
+
const seconds = Number(retryAfter);
|
|
57
|
+
if (!Number.isNaN(seconds) && seconds >= 0) {
|
|
58
|
+
return seconds * 1000;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Try as HTTP-date
|
|
62
|
+
const date = Date.parse(retryAfter);
|
|
63
|
+
if (!Number.isNaN(date)) {
|
|
64
|
+
const delayMs = date - Date.now();
|
|
65
|
+
return delayMs > 0 ? delayMs : 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
47
71
|
// Calculate exponential backoff delay
|
|
48
72
|
export function calculateDelay(attempt: number): number {
|
|
49
73
|
const delay = RETRY_CONFIG.baseDelayMs * Math.pow(2, attempt);
|
|
@@ -67,7 +91,23 @@ export async function fetchWithRetry(
|
|
|
67
91
|
isRetryableError(null, response) &&
|
|
68
92
|
attempt < RETRY_CONFIG.maxRetries
|
|
69
93
|
) {
|
|
70
|
-
|
|
94
|
+
let delay = calculateDelay(attempt);
|
|
95
|
+
|
|
96
|
+
// Honor Retry-After header on 429 responses
|
|
97
|
+
if (response.status === 429) {
|
|
98
|
+
const retryAfterMs = parseRetryAfter(response);
|
|
99
|
+
if (retryAfterMs !== undefined) {
|
|
100
|
+
const maxMs = RETRY_CONFIG.maxRetryAfterSeconds * 1000;
|
|
101
|
+
if (retryAfterMs > maxMs) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Server requested Retry-After of ${Math.ceil(retryAfterMs / 1000)}s ` +
|
|
104
|
+
`which exceeds the maximum of ${RETRY_CONFIG.maxRetryAfterSeconds}s`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
delay = retryAfterMs;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
71
111
|
logger?.warn(
|
|
72
112
|
`Request to ${url} failed with status ${response.status}. ` +
|
|
73
113
|
`Retrying attempt ${attempt + 1}/${RETRY_CONFIG.maxRetries + 1} in ${delay}ms.`,
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test: Express single-route adapter (deprecated convenience
|
|
3
|
+
* wrapper) + telemetry. This adapter delegates to createCopilotExpressHandler
|
|
4
|
+
* with mode: "single-route".
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
7
|
+
import type { AbstractAgent } from "@ag-ui/client";
|
|
8
|
+
import { Observable, of } from "rxjs";
|
|
9
|
+
|
|
10
|
+
import { telemetry } from "../telemetry";
|
|
11
|
+
import { createCopilotEndpointSingleRouteExpress } from "../endpoints/express-single";
|
|
12
|
+
import { CopilotRuntime } from "../core/runtime";
|
|
13
|
+
|
|
14
|
+
function makeAgent(): AbstractAgent {
|
|
15
|
+
const a: unknown = { execute: async () => ({ events: [] }) };
|
|
16
|
+
(a as { clone: () => unknown }).clone = () => makeAgent();
|
|
17
|
+
return a as AbstractAgent;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function makeRuntime() {
|
|
21
|
+
const runner = {
|
|
22
|
+
run: () =>
|
|
23
|
+
new Observable((observer) => {
|
|
24
|
+
observer.next({});
|
|
25
|
+
observer.complete();
|
|
26
|
+
return () => undefined;
|
|
27
|
+
}),
|
|
28
|
+
connect: () => of({}),
|
|
29
|
+
stop: async () => true,
|
|
30
|
+
};
|
|
31
|
+
return new CopilotRuntime({
|
|
32
|
+
agents: { default: makeAgent() },
|
|
33
|
+
runner,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("Express single-route adapter — telemetry firing (integration)", () => {
|
|
38
|
+
let captureSpy: ReturnType<typeof vi.spyOn>;
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
captureSpy = vi.spyOn(telemetry, "capture").mockResolvedValue(undefined);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
captureSpy.mockRestore();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("fires instance_created on handler creation", async () => {
|
|
49
|
+
const runtime = makeRuntime();
|
|
50
|
+
createCopilotEndpointSingleRouteExpress({
|
|
51
|
+
runtime,
|
|
52
|
+
basePath: "/api/copilotkit",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await vi.waitFor(() => {
|
|
56
|
+
expect(captureSpy).toHaveBeenCalledWith(
|
|
57
|
+
"oss.runtime.instance_created",
|
|
58
|
+
expect.objectContaining({
|
|
59
|
+
agentsAmount: 1,
|
|
60
|
+
"cloud.api_key_provided": false,
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test: Express adapter + telemetry.
|
|
3
|
+
*/
|
|
4
|
+
import express from "express";
|
|
5
|
+
import request from "supertest";
|
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
7
|
+
import type { AbstractAgent } from "@ag-ui/client";
|
|
8
|
+
import { Observable, of } from "rxjs";
|
|
9
|
+
|
|
10
|
+
import { telemetry } from "../telemetry";
|
|
11
|
+
import { createCopilotExpressHandler } from "../endpoints/express";
|
|
12
|
+
import { CopilotRuntime } from "../core/runtime";
|
|
13
|
+
|
|
14
|
+
function makeAgent(): AbstractAgent {
|
|
15
|
+
const a: unknown = { execute: async () => ({ events: [] }) };
|
|
16
|
+
(a as { clone: () => unknown }).clone = () => makeAgent();
|
|
17
|
+
return a as AbstractAgent;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function makeRuntime() {
|
|
21
|
+
const runner = {
|
|
22
|
+
run: () =>
|
|
23
|
+
new Observable((observer) => {
|
|
24
|
+
observer.next({});
|
|
25
|
+
observer.complete();
|
|
26
|
+
return () => undefined;
|
|
27
|
+
}),
|
|
28
|
+
connect: () => of({}),
|
|
29
|
+
stop: async () => true,
|
|
30
|
+
};
|
|
31
|
+
return new CopilotRuntime({
|
|
32
|
+
agents: { default: makeAgent() },
|
|
33
|
+
runner,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("Express adapter — telemetry firing (integration)", () => {
|
|
38
|
+
let captureSpy: ReturnType<typeof vi.spyOn>;
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
captureSpy = vi.spyOn(telemetry, "capture").mockResolvedValue(undefined);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
captureSpy.mockRestore();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("fires instance_created on handler creation (multi-route)", async () => {
|
|
49
|
+
const runtime = makeRuntime();
|
|
50
|
+
createCopilotExpressHandler({ runtime, basePath: "/" });
|
|
51
|
+
|
|
52
|
+
await vi.waitFor(() => {
|
|
53
|
+
expect(captureSpy).toHaveBeenCalledWith(
|
|
54
|
+
"oss.runtime.instance_created",
|
|
55
|
+
expect.objectContaining({
|
|
56
|
+
agentsAmount: 1,
|
|
57
|
+
"cloud.api_key_provided": false,
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("fires copilot_request_created when a real HTTP request hits the handler", async () => {
|
|
64
|
+
const runtime = makeRuntime();
|
|
65
|
+
const app = express();
|
|
66
|
+
app.use(createCopilotExpressHandler({ runtime, basePath: "/" }));
|
|
67
|
+
|
|
68
|
+
await request(app)
|
|
69
|
+
.post("/agent/default/run")
|
|
70
|
+
.set("Content-Type", "application/json")
|
|
71
|
+
.send({ messages: [], state: {}, threadId: "t1" });
|
|
72
|
+
|
|
73
|
+
expect(captureSpy).toHaveBeenCalledWith(
|
|
74
|
+
"oss.runtime.copilot_request_created",
|
|
75
|
+
expect.objectContaining({
|
|
76
|
+
requestType: "run",
|
|
77
|
+
"cloud.api_key_provided": false,
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("includes cloud.public_api_key on request when header is present", async () => {
|
|
83
|
+
const runtime = makeRuntime();
|
|
84
|
+
const app = express();
|
|
85
|
+
app.use(createCopilotExpressHandler({ runtime, basePath: "/" }));
|
|
86
|
+
|
|
87
|
+
await request(app)
|
|
88
|
+
.post("/agent/default/run")
|
|
89
|
+
.set("Content-Type", "application/json")
|
|
90
|
+
.set("x-copilotcloud-public-api-key", "ck_pub_test_xyz")
|
|
91
|
+
.send({ messages: [], state: {}, threadId: "t1" });
|
|
92
|
+
|
|
93
|
+
expect(captureSpy).toHaveBeenCalledWith(
|
|
94
|
+
"oss.runtime.copilot_request_created",
|
|
95
|
+
expect.objectContaining({
|
|
96
|
+
"cloud.api_key_provided": true,
|
|
97
|
+
"cloud.public_api_key": "ck_pub_test_xyz",
|
|
98
|
+
}),
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -135,6 +135,11 @@ describe("fetch-router", () => {
|
|
|
135
135
|
const result = matchRoute("/info", "/");
|
|
136
136
|
expect(result).toEqual({ method: "info" });
|
|
137
137
|
});
|
|
138
|
+
|
|
139
|
+
it("matches GET /cpk-debug-events", () => {
|
|
140
|
+
const result = matchRoute("/api/copilotkit/cpk-debug-events", basePath);
|
|
141
|
+
expect(result).toEqual({ method: "cpk-debug-events" });
|
|
142
|
+
});
|
|
138
143
|
});
|
|
139
144
|
|
|
140
145
|
describe("without basePath (suffix matching)", () => {
|
|
@@ -204,5 +209,22 @@ describe("fetch-router", () => {
|
|
|
204
209
|
const result = matchRoute("/api/v2/copilotkit/agent/a1/run");
|
|
205
210
|
expect(result).toEqual({ method: "agent/run", agentId: "a1" });
|
|
206
211
|
});
|
|
212
|
+
|
|
213
|
+
it("matches /cpk-debug-events suffix", () => {
|
|
214
|
+
const result = matchRoute("/api/copilotkit/cpk-debug-events");
|
|
215
|
+
expect(result).toEqual({ method: "cpk-debug-events" });
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("matches bare /cpk-debug-events", () => {
|
|
219
|
+
const result = matchRoute("/cpk-debug-events");
|
|
220
|
+
expect(result).toEqual({ method: "cpk-debug-events" });
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe("cpk-debug-events route with basePath", () => {
|
|
225
|
+
it("matches /cpk-debug-events with /api basePath", () => {
|
|
226
|
+
const result = matchRoute("/api/cpk-debug-events", "/api");
|
|
227
|
+
expect(result).toEqual({ method: "cpk-debug-events" });
|
|
228
|
+
});
|
|
207
229
|
});
|
|
208
230
|
});
|