@copilotkit/runtime 1.4.0-pre-1-4-0.11 → 1.4.0-pre-1-4-0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +33 -100
  2. package/dist/{chunk-BNQDVBQH.mjs → chunk-56IQ6PGC.mjs} +449 -75
  3. package/dist/chunk-56IQ6PGC.mjs.map +1 -0
  4. package/dist/chunk-DFOKBSIS.mjs +1 -0
  5. package/dist/chunk-DFOKBSIS.mjs.map +1 -0
  6. package/dist/{chunk-VBGS6IWV.mjs → chunk-RDHJQVWH.mjs} +483 -71
  7. package/dist/chunk-RDHJQVWH.mjs.map +1 -0
  8. package/dist/{chunk-V2YEM4Z5.mjs → chunk-SBWLAAB3.mjs} +4 -3
  9. package/dist/chunk-SBWLAAB3.mjs.map +1 -0
  10. package/dist/{chunk-677K33J7.mjs → chunk-SVL5LEKB.mjs} +4 -3
  11. package/dist/chunk-SVL5LEKB.mjs.map +1 -0
  12. package/dist/{chunk-3DNY5YTL.mjs → chunk-YUJSVJWS.mjs} +5 -4
  13. package/dist/chunk-YUJSVJWS.mjs.map +1 -0
  14. package/dist/{copilot-runtime-8d3f40c7.d.ts → copilot-runtime-dbe5fa02.d.ts} +4 -4
  15. package/dist/{groq-adapter-dbfba3eb.d.ts → groq-adapter-192d2413.d.ts} +1 -1
  16. package/dist/index.d.ts +3 -3
  17. package/dist/index.js +486 -169
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.mjs +22 -22
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/{langserve-f00629d2.d.ts → langserve-878c62b9.d.ts} +46 -9
  22. package/dist/lib/index.d.ts +3 -3
  23. package/dist/lib/index.js +465 -163
  24. package/dist/lib/index.js.map +1 -1
  25. package/dist/lib/index.mjs +21 -21
  26. package/dist/lib/integrations/index.d.ts +3 -3
  27. package/dist/lib/integrations/index.js +161 -85
  28. package/dist/lib/integrations/index.js.map +1 -1
  29. package/dist/lib/integrations/index.mjs +5 -5
  30. package/dist/lib/integrations/nest/index.d.ts +2 -2
  31. package/dist/lib/integrations/nest/index.js +158 -82
  32. package/dist/lib/integrations/nest/index.js.map +1 -1
  33. package/dist/lib/integrations/nest/index.mjs +3 -3
  34. package/dist/lib/integrations/node-express/index.d.ts +2 -2
  35. package/dist/lib/integrations/node-express/index.js +158 -82
  36. package/dist/lib/integrations/node-express/index.js.map +1 -1
  37. package/dist/lib/integrations/node-express/index.mjs +3 -3
  38. package/dist/lib/integrations/node-http/index.d.ts +2 -2
  39. package/dist/lib/integrations/node-http/index.js +157 -81
  40. package/dist/lib/integrations/node-http/index.js.map +1 -1
  41. package/dist/lib/integrations/node-http/index.mjs +2 -2
  42. package/dist/service-adapters/index.d.ts +3 -3
  43. package/dist/service-adapters/index.js +243 -73
  44. package/dist/service-adapters/index.js.map +1 -1
  45. package/dist/service-adapters/index.mjs +1 -2
  46. package/package.json +4 -4
  47. package/src/agents/langgraph/event-source.ts +12 -0
  48. package/src/agents/langgraph/events.ts +2 -0
  49. package/src/graphql/resolvers/copilot.resolver.ts +28 -2
  50. package/src/lib/integrations/nest/index.ts +5 -2
  51. package/src/lib/integrations/nextjs/app-router.ts +5 -2
  52. package/src/lib/integrations/nextjs/pages-router.ts +5 -2
  53. package/src/lib/integrations/node-express/index.ts +5 -2
  54. package/src/lib/integrations/node-http/index.ts +5 -2
  55. package/src/lib/runtime/copilot-runtime.ts +48 -42
  56. package/src/lib/runtime/remote-action-constructors.ts +17 -3
  57. package/src/lib/runtime/remote-lg-cloud-action.ts +41 -15
  58. package/src/lib/telemetry-client.ts +43 -0
  59. package/src/service-adapters/anthropic/anthropic-adapter.ts +15 -6
  60. package/src/service-adapters/events.ts +86 -36
  61. package/src/service-adapters/experimental/ollama/ollama-adapter.ts +7 -3
  62. package/src/service-adapters/google/google-genai-adapter.ts +2 -2
  63. package/src/service-adapters/groq/groq-adapter.ts +22 -8
  64. package/src/service-adapters/langchain/langchain-adapter.ts +22 -16
  65. package/src/service-adapters/langchain/utils.ts +47 -31
  66. package/src/service-adapters/openai/openai-adapter.ts +25 -8
  67. package/src/service-adapters/openai/openai-assistant-adapter.ts +21 -8
  68. package/src/service-adapters/unify/unify-adapter.ts +28 -11
  69. package/dist/chunk-3DNY5YTL.mjs.map +0 -1
  70. package/dist/chunk-677K33J7.mjs.map +0 -1
  71. package/dist/chunk-BNQDVBQH.mjs.map +0 -1
  72. package/dist/chunk-FL67XJAX.mjs +0 -288
  73. package/dist/chunk-FL67XJAX.mjs.map +0 -1
  74. package/dist/chunk-MXXPWWBF.mjs +0 -218
  75. package/dist/chunk-MXXPWWBF.mjs.map +0 -1
  76. package/dist/chunk-V2YEM4Z5.mjs.map +0 -1
  77. package/dist/chunk-VBGS6IWV.mjs.map +0 -1
@@ -8,6 +8,8 @@ export enum LangGraphEventTypes {
8
8
  OnToolStart = "on_tool_start",
9
9
  OnToolEnd = "on_tool_end",
10
10
  OnCopilotKitStateSync = "on_copilotkit_state_sync",
11
+ OnCopilotKitEmitMessage = "on_copilotkit_emit_message",
12
+ OnCopilotKitEmitToolCall = "on_copilotkit_emit_tool_call",
11
13
  OnCustomEvent = "on_custom_event",
12
14
  }
13
15
 
@@ -3,6 +3,7 @@ import {
3
3
  ReplaySubject,
4
4
  Subject,
5
5
  Subscription,
6
+ filter,
6
7
  finalize,
7
8
  firstValueFrom,
8
9
  shareReplay,
@@ -286,7 +287,19 @@ export class CopilotResolver {
286
287
  // skip until this message start event
287
288
  skipWhile((e) => e !== event),
288
289
  // take until the message end event
289
- takeWhile((e) => e.type != RuntimeEventTypes.TextMessageEnd),
290
+ takeWhile(
291
+ (e) =>
292
+ !(
293
+ e.type === RuntimeEventTypes.TextMessageEnd &&
294
+ e.messageId == event.messageId
295
+ ),
296
+ ),
297
+ // filter out any other message events or message ids
298
+ filter(
299
+ (e) =>
300
+ e.type == RuntimeEventTypes.TextMessageContent &&
301
+ e.messageId == event.messageId,
302
+ ),
290
303
  );
291
304
 
292
305
  // signal when we are done streaming
@@ -367,7 +380,20 @@ export class CopilotResolver {
367
380
  logger.debug("Action execution start event received");
368
381
  const actionExecutionArgumentStream = eventStream.pipe(
369
382
  skipWhile((e) => e !== event),
370
- takeWhile((e) => e.type != RuntimeEventTypes.ActionExecutionEnd),
383
+ // take until the action execution end event
384
+ takeWhile(
385
+ (e) =>
386
+ !(
387
+ e.type === RuntimeEventTypes.ActionExecutionEnd &&
388
+ e.actionExecutionId == event.actionExecutionId
389
+ ),
390
+ ),
391
+ // filter out any other action execution events or action execution ids
392
+ filter(
393
+ (e) =>
394
+ e.type == RuntimeEventTypes.ActionExecutionArgs &&
395
+ e.actionExecutionId == event.actionExecutionId,
396
+ ),
371
397
  );
372
398
  const streamingArgumentsStatus = new Subject<typeof MessageStatusUnion>();
373
399
  pushMessage({
@@ -1,6 +1,6 @@
1
1
  import { CreateCopilotRuntimeServerOptions } from "../shared";
2
2
  import { copilotRuntimeNodeHttpEndpoint } from "../node-http";
3
- import telemetry from "../../telemetry-client";
3
+ import telemetry, { getRuntimeInstanceTelemetryInfo } from "../../telemetry-client";
4
4
 
5
5
  export function copilotRuntimeNestEndpoint(options: CreateCopilotRuntimeServerOptions) {
6
6
  telemetry.setGlobalProperties({
@@ -9,6 +9,9 @@ export function copilotRuntimeNestEndpoint(options: CreateCopilotRuntimeServerOp
9
9
  },
10
10
  });
11
11
 
12
- telemetry.capture("oss.runtime.instance_created", {});
12
+ telemetry.capture(
13
+ "oss.runtime.instance_created",
14
+ getRuntimeInstanceTelemetryInfo(options.runtime),
15
+ );
13
16
  return copilotRuntimeNodeHttpEndpoint(options);
14
17
  }
@@ -1,6 +1,6 @@
1
1
  import { createYoga } from "graphql-yoga";
2
2
  import { CreateCopilotRuntimeServerOptions, getCommonConfig } from "../shared";
3
- import telemetry from "../../telemetry-client";
3
+ import telemetry, { getRuntimeInstanceTelemetryInfo } from "../../telemetry-client";
4
4
 
5
5
  export function copilotRuntimeNextJSAppRouterEndpoint(options: CreateCopilotRuntimeServerOptions) {
6
6
  const commonConfig = getCommonConfig(options);
@@ -17,7 +17,10 @@ export function copilotRuntimeNextJSAppRouterEndpoint(options: CreateCopilotRunt
17
17
  });
18
18
  }
19
19
 
20
- telemetry.capture("oss.runtime.instance_created", {});
20
+ telemetry.capture(
21
+ "oss.runtime.instance_created",
22
+ getRuntimeInstanceTelemetryInfo(options.runtime),
23
+ );
21
24
 
22
25
  const logger = commonConfig.logging;
23
26
  logger.debug("Creating NextJS App Router endpoint");
@@ -1,6 +1,6 @@
1
1
  import { YogaServerInstance, createYoga } from "graphql-yoga";
2
2
  import { CreateCopilotRuntimeServerOptions, GraphQLContext, getCommonConfig } from "../shared";
3
- import telemetry from "../../telemetry-client";
3
+ import telemetry, { getRuntimeInstanceTelemetryInfo } from "../../telemetry-client";
4
4
 
5
5
  export const config = {
6
6
  api: {
@@ -32,7 +32,10 @@ export function copilotRuntimeNextJSPagesRouterEndpoint(
32
32
  });
33
33
  }
34
34
 
35
- telemetry.capture("oss.runtime.instance_created", {});
35
+ telemetry.capture(
36
+ "oss.runtime.instance_created",
37
+ getRuntimeInstanceTelemetryInfo(options.runtime),
38
+ );
36
39
 
37
40
  const logger = commonConfig.logging;
38
41
  logger.debug("Creating NextJS Pages Router endpoint");
@@ -1,6 +1,6 @@
1
1
  import { CreateCopilotRuntimeServerOptions } from "../shared";
2
2
  import { copilotRuntimeNodeHttpEndpoint } from "../node-http";
3
- import telemetry from "../../telemetry-client";
3
+ import telemetry, { getRuntimeInstanceTelemetryInfo } from "../../telemetry-client";
4
4
 
5
5
  export function copilotRuntimeNodeExpressEndpoint(options: CreateCopilotRuntimeServerOptions) {
6
6
  telemetry.setGlobalProperties({
@@ -9,6 +9,9 @@ export function copilotRuntimeNodeExpressEndpoint(options: CreateCopilotRuntimeS
9
9
  },
10
10
  });
11
11
 
12
- telemetry.capture("oss.runtime.instance_created", {});
12
+ telemetry.capture(
13
+ "oss.runtime.instance_created",
14
+ getRuntimeInstanceTelemetryInfo(options.runtime),
15
+ );
13
16
  return copilotRuntimeNodeHttpEndpoint(options);
14
17
  }
@@ -1,6 +1,6 @@
1
1
  import { createYoga } from "graphql-yoga";
2
2
  import { CreateCopilotRuntimeServerOptions, getCommonConfig } from "../shared";
3
- import telemetry from "../../telemetry-client";
3
+ import telemetry, { getRuntimeInstanceTelemetryInfo } from "../../telemetry-client";
4
4
 
5
5
  export function copilotRuntimeNodeHttpEndpoint(options: CreateCopilotRuntimeServerOptions) {
6
6
  const commonConfig = getCommonConfig(options);
@@ -17,7 +17,10 @@ export function copilotRuntimeNodeHttpEndpoint(options: CreateCopilotRuntimeServ
17
17
  });
18
18
  }
19
19
 
20
- telemetry.capture("oss.runtime.instance_created", {});
20
+ telemetry.capture(
21
+ "oss.runtime.instance_created",
22
+ getRuntimeInstanceTelemetryInfo(options.runtime),
23
+ );
21
24
 
22
25
  const logger = commonConfig.logging;
23
26
  logger.debug("Creating Node HTTP endpoint");
@@ -12,7 +12,7 @@
12
12
  * ```
13
13
  */
14
14
 
15
- import { Action, actionParametersToJsonSchema, Parameter } from "@copilotkit/shared";
15
+ import { Action, actionParametersToJsonSchema, Parameter, randomId } from "@copilotkit/shared";
16
16
  import { CopilotServiceAdapter, RemoteChain, RemoteChainParameters } from "../../service-adapters";
17
17
  import { MessageInput } from "../../graphql/inputs/message.input";
18
18
  import { ActionInput } from "../../graphql/inputs/action.input";
@@ -142,7 +142,7 @@ export interface CopilotRuntimeConstructorParams<T extends Parameter[] | [] = []
142
142
 
143
143
  export class CopilotRuntime<const T extends Parameter[] | [] = []> {
144
144
  public actions: ActionsConfiguration<T>;
145
- private remoteEndpointDefinitions: EndpointDefinition[];
145
+ public remoteEndpointDefinitions: EndpointDefinition[];
146
146
  private langserve: Promise<Action<any>>[] = [];
147
147
  private onBeforeRequest?: OnBeforeRequestHandler;
148
148
  private onAfterRequest?: OnAfterRequestHandler;
@@ -155,7 +155,7 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
155
155
  this.langserve.push(remoteChain.toAction());
156
156
  }
157
157
 
158
- this.remoteEndpointDefinitions = params?.remoteEndpoints || [];
158
+ this.remoteEndpointDefinitions = params?.remoteEndpoints ?? params?.remoteActions ?? [];
159
159
 
160
160
  this.onBeforeRequest = params?.middleware?.onBeforeRequest;
161
161
  this.onAfterRequest = params?.middleware?.onAfterRequest;
@@ -175,36 +175,36 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
175
175
  url,
176
176
  } = request;
177
177
 
178
- if (agentSession) {
179
- return this.processAgentRequest(request);
180
- }
178
+ const eventSource = new RuntimeEventSource();
181
179
 
182
- const messages = rawMessages.filter((message) => !message.agentStateMessage);
180
+ try {
181
+ if (agentSession) {
182
+ return await this.processAgentRequest(request);
183
+ }
183
184
 
184
- const inputMessages = convertGqlInputToMessages(messages);
185
- const serverSideActions = await this.getServerSideActions(request);
185
+ const messages = rawMessages.filter((message) => !message.agentStateMessage);
186
186
 
187
- const serverSideActionsInput: ActionInput[] = serverSideActions.map((action) => ({
188
- name: action.name,
189
- description: action.description,
190
- jsonSchema: JSON.stringify(actionParametersToJsonSchema(action.parameters)),
191
- }));
187
+ const inputMessages = convertGqlInputToMessages(messages);
188
+ const serverSideActions = await this.getServerSideActions(request);
192
189
 
193
- const actionInputs = flattenToolCallsNoDuplicates([
194
- ...serverSideActionsInput,
195
- ...clientSideActionsInput,
196
- ]);
190
+ const serverSideActionsInput: ActionInput[] = serverSideActions.map((action) => ({
191
+ name: action.name,
192
+ description: action.description,
193
+ jsonSchema: JSON.stringify(actionParametersToJsonSchema(action.parameters)),
194
+ }));
197
195
 
198
- await this.onBeforeRequest?.({
199
- threadId,
200
- runId,
201
- inputMessages,
202
- properties: graphqlContext.properties,
203
- url,
204
- });
196
+ const actionInputs = flattenToolCallsNoDuplicates([
197
+ ...serverSideActionsInput,
198
+ ...clientSideActionsInput,
199
+ ]);
205
200
 
206
- try {
207
- const eventSource = new RuntimeEventSource();
201
+ await this.onBeforeRequest?.({
202
+ threadId,
203
+ runId,
204
+ inputMessages,
205
+ properties: graphqlContext.properties,
206
+ url,
207
+ });
208
208
 
209
209
  const result = await serviceAdapter.process({
210
210
  messages: inputMessages,
@@ -244,7 +244,14 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
244
244
  };
245
245
  } catch (error) {
246
246
  console.error("Error getting response:", error);
247
- throw error;
247
+ eventSource.sendErrorMessageToChat();
248
+ return {
249
+ threadId: threadId || randomId(),
250
+ runId: runId || randomId(),
251
+ eventSource,
252
+ serverSideActions: [],
253
+ actionInputsWithoutAgents: [],
254
+ };
248
255
  }
249
256
  }
250
257
 
@@ -344,7 +351,7 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
344
351
  (endpoint) =>
345
352
  ({
346
353
  ...endpoint,
347
- type: this.resolveEndpointType(endpoint),
354
+ type: resolveEndpointType(endpoint),
348
355
  }) as EndpointDefinition,
349
356
  );
350
357
 
@@ -363,19 +370,6 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
363
370
 
364
371
  return [...configuredActions, ...langserveFunctions, ...remoteActions];
365
372
  }
366
-
367
- private resolveEndpointType(endpoint: EndpointDefinition) {
368
- if (
369
- !endpoint.type &&
370
- "langsmithApiKey" in endpoint &&
371
- "deploymentUrl" in endpoint &&
372
- "agents" in endpoint
373
- ) {
374
- return EndpointType.LangGraphCloud;
375
- }
376
-
377
- return endpoint.type;
378
- }
379
373
  }
380
374
 
381
375
  export function flattenToolCallsNoDuplicates(toolsByPriority: ActionInput[]): ActionInput[] {
@@ -406,3 +400,15 @@ export function langGraphCloudEndpoint(
406
400
  type: EndpointType.LangGraphCloud,
407
401
  };
408
402
  }
403
+
404
+ export function resolveEndpointType(endpoint: EndpointDefinition) {
405
+ if (!endpoint.type) {
406
+ if ("langsmithApiKey" in endpoint && "deploymentUrl" in endpoint && "agents" in endpoint) {
407
+ return EndpointType.LangGraphCloud;
408
+ } else {
409
+ return EndpointType.CopilotKit;
410
+ }
411
+ }
412
+
413
+ return endpoint.type;
414
+ }
@@ -1,3 +1,4 @@
1
+ import { createHash } from "node:crypto";
1
2
  import {
2
3
  CopilotKitEndpoint,
3
4
  LangGraphAgentHandlerParams,
@@ -42,7 +43,12 @@ export function constructLGCRemoteAction({
42
43
  }: LangGraphAgentHandlerParams): Promise<Observable<RuntimeEvent>> => {
43
44
  logger.debug({ actionName: agent.name }, "Executing LangGraph Cloud agent");
44
45
 
45
- telemetry.capture("oss.runtime.remote_action_executed", {});
46
+ telemetry.capture("oss.runtime.remote_action_executed", {
47
+ agentExecution: true,
48
+ type: "langgraph-cloud",
49
+ agentsAmount: endpoint.agents.length,
50
+ hashedLgcKey: createHash("sha256").update(endpoint.langsmithApiKey).digest("hex"),
51
+ });
46
52
 
47
53
  let state = {};
48
54
  if (agentStates) {
@@ -111,7 +117,11 @@ export function constructRemoteActions({
111
117
  logger.debug({ actionName: action.name, args }, "Executing remote action");
112
118
 
113
119
  const headers = createHeaders(onBeforeRequest, graphqlContext);
114
- telemetry.capture("oss.runtime.remote_action_executed", {});
120
+ telemetry.capture("oss.runtime.remote_action_executed", {
121
+ agentExecution: false,
122
+ type: "self-hosted",
123
+ agentsAmount: json["agents"].length,
124
+ });
115
125
 
116
126
  try {
117
127
  const response = await fetch(`${url}/actions/execute`, {
@@ -162,7 +172,11 @@ export function constructRemoteActions({
162
172
  logger.debug({ actionName: agent.name }, "Executing remote agent");
163
173
 
164
174
  const headers = createHeaders(onBeforeRequest, graphqlContext);
165
- telemetry.capture("oss.runtime.remote_action_executed", {});
175
+ telemetry.capture("oss.runtime.remote_action_executed", {
176
+ agentExecution: true,
177
+ type: "self-hosted",
178
+ agentsAmount: json["agents"].length,
179
+ });
166
180
 
167
181
  let state = {};
168
182
  if (agentStates) {
@@ -1,5 +1,5 @@
1
1
  import { Client } from "@langchain/langgraph-sdk";
2
- import { randomUUID } from "node:crypto";
2
+ import { createHash, randomUUID } from "node:crypto";
3
3
  import { parse as parsePartialJson } from "partial-json";
4
4
  import { Logger } from "pino";
5
5
  import { ActionInput } from "../../graphql/inputs/action.input";
@@ -8,6 +8,7 @@ import { CopilotRequestContextProperties } from "../integrations";
8
8
  import { Message, MessageType } from "../../graphql/types/converted";
9
9
  import { MessageRole } from "../../graphql/types/enums";
10
10
  import { CustomEventNames, LangGraphEventTypes } from "../../agents/langgraph/events";
11
+ import telemetry from "../telemetry-client";
11
12
 
12
13
  type State = Record<string, any>;
13
14
 
@@ -88,7 +89,6 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
88
89
  let state = initialState;
89
90
  const { name, assistantId: initialAssistantId } = agent;
90
91
 
91
- // TODO: deploymentUrl is not required in local development
92
92
  const client = new Client({ apiUrl: deploymentUrl, apiKey: langsmithApiKey });
93
93
  let initialThreadId = agrsInitialThreadId;
94
94
  const wasInitiatedWithExistingThread = !!initialThreadId;
@@ -152,8 +152,22 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
152
152
 
153
153
  let latestStateValues = {};
154
154
  let updatedState = state;
155
+ // If a manual emittance happens, it is the ultimate source of truth of state, unless a node has exited.
156
+ // Therefore, this value should either hold null, or the only edition of state that should be used.
157
+ let manuallyEmittedState = null;
158
+ let streamInfo: {
159
+ provider?: string;
160
+ langGraphHost?: string;
161
+ langGraphVersion?: string;
162
+ hashedLgcKey: string;
163
+ } = {
164
+ hashedLgcKey: createHash("sha256").update(langsmithApiKey).digest("hex"),
165
+ };
155
166
 
156
167
  try {
168
+ telemetry.capture("oss.runtime.agent_execution_stream_started", {
169
+ hashedLgcKey: streamInfo.hashedLgcKey,
170
+ });
157
171
  for await (const chunk of streamResponse) {
158
172
  if (!["events", "values", "error"].includes(chunk.event)) continue;
159
173
 
@@ -174,6 +188,16 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
174
188
  externalRunId = runId;
175
189
  const metadata = event.metadata;
176
190
 
191
+ if (event.data?.output?.model != null && event.data?.output?.model != "") {
192
+ streamInfo.provider = event.data?.output?.model;
193
+ }
194
+ if (metadata.langgraph_host != null && metadata.langgraph_host != "") {
195
+ streamInfo.langGraphHost = metadata.langgraph_host;
196
+ }
197
+ if (metadata.langgraph_version != null && metadata.langgraph_version != "") {
198
+ streamInfo.langGraphVersion = metadata.langgraph_version;
199
+ }
200
+
177
201
  shouldExit =
178
202
  shouldExit ||
179
203
  (eventType === LangGraphEventTypes.OnCustomEvent &&
@@ -184,33 +208,36 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
184
208
  eventType === LangGraphEventTypes.OnCustomEvent &&
185
209
  event.name === CustomEventNames.CopilotKitManuallyEmitIntermediateState;
186
210
 
211
+ const exitingNode =
212
+ nodeName === currentNodeName && eventType === LangGraphEventTypes.OnChainEnd;
213
+
214
+ // See manuallyEmittedState for explanation
215
+ if (exitingNode) {
216
+ manuallyEmittedState = null;
217
+ }
218
+
187
219
  // we only want to update the node name under certain conditions
188
220
  // since we don't need any internal node names to be sent to the frontend
189
221
  if (graphInfo["nodes"].some((node) => node.id === currentNodeName)) {
190
222
  nodeName = currentNodeName;
191
-
192
- // only update state from values when entering or exiting a known node
193
- if (
194
- eventType === LangGraphEventTypes.OnChainStart ||
195
- eventType === LangGraphEventTypes.OnChainEnd
196
- ) {
197
- updatedState = latestStateValues;
198
- }
199
223
  }
200
224
 
225
+ updatedState = manuallyEmittedState ?? latestStateValues;
226
+
201
227
  if (!nodeName) {
202
228
  continue;
203
229
  }
204
230
 
205
231
  if (manuallyEmitIntermediateState) {
206
- updatedState = event.data;
232
+ // See manuallyEmittedState for explanation
233
+ manuallyEmittedState = event.data;
207
234
  emit(
208
235
  getStateSyncEvent({
209
236
  threadId,
210
237
  runId,
211
238
  agentName: agent.name,
212
239
  nodeName,
213
- state: updatedState,
240
+ state: manuallyEmittedState,
214
241
  running: true,
215
242
  active: true,
216
243
  }),
@@ -247,9 +274,6 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
247
274
  emitIntermediateStateUntilEnd = null;
248
275
  }
249
276
 
250
- const exitingNode =
251
- nodeName === currentNodeName && eventType === LangGraphEventTypes.OnChainEnd;
252
-
253
277
  if (
254
278
  JSON.stringify(updatedState) !== JSON.stringify(state) ||
255
279
  prevNodeName != nodeName ||
@@ -277,6 +301,8 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
277
301
  const isEndNode = state.next.length === 0;
278
302
  nodeName = Object.keys(state.metadata.writes)[0];
279
303
 
304
+ telemetry.capture("oss.runtime.agent_execution_stream_ended", streamInfo);
305
+
280
306
  emit(
281
307
  getStateSyncEvent({
282
308
  threadId,
@@ -1,4 +1,8 @@
1
1
  import { TelemetryClient } from "@copilotkit/shared";
2
+ import { EndpointType, LangGraphCloudEndpoint } from "./runtime/remote-actions";
3
+ import { createHash } from "node:crypto";
4
+ import { CopilotRuntime, resolveEndpointType } from "./runtime/copilot-runtime";
5
+ import { RuntimeInstanceCreatedInfo } from "@copilotkit/shared/src/telemetry/events";
2
6
  const packageJson = require("../../package.json");
3
7
 
4
8
  const telemetryClient = new TelemetryClient({
@@ -6,4 +10,43 @@ const telemetryClient = new TelemetryClient({
6
10
  packageVersion: packageJson.version,
7
11
  });
8
12
 
13
+ export function getRuntimeInstanceTelemetryInfo(
14
+ runtime: CopilotRuntime,
15
+ ): RuntimeInstanceCreatedInfo {
16
+ const endpointsInfo = runtime.remoteEndpointDefinitions.reduce(
17
+ (acc, endpoint) => {
18
+ let info = { ...acc };
19
+
20
+ const endpointType = resolveEndpointType(endpoint);
21
+ if (!info.endpointTypes.includes(endpointType)) {
22
+ info = {
23
+ ...info,
24
+ endpointTypes: [...info.endpointTypes, endpointType],
25
+ };
26
+ }
27
+
28
+ if (endpointType === EndpointType.LangGraphCloud) {
29
+ // When type is resolved, recreating a const with casting of type
30
+ const ep = endpoint as LangGraphCloudEndpoint;
31
+ info = {
32
+ ...info,
33
+ agentsAmount: ep.agents.length,
34
+ hashedKey: createHash("sha256").update(ep.langsmithApiKey).digest("hex"),
35
+ };
36
+ }
37
+
38
+ return info;
39
+ },
40
+ { endpointTypes: [], agentsAmount: null, hashedKey: null },
41
+ );
42
+
43
+ return {
44
+ actionsAmount: runtime.actions.length,
45
+ endpointsAmount: runtime.remoteEndpointDefinitions.length,
46
+ endpointTypes: endpointsInfo.endpointTypes,
47
+ agentsAmount: endpointsInfo.agentsAmount,
48
+ hashedLgcKey: endpointsInfo.hashedKey,
49
+ };
50
+ }
51
+
9
52
  export default telemetryClient;
@@ -122,7 +122,10 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
122
122
  mode = "message";
123
123
  } else if (chunk.content_block.type === "tool_use") {
124
124
  currentToolCallId = chunk.content_block.id;
125
- eventStream$.sendActionExecutionStart(currentToolCallId, chunk.content_block.name);
125
+ eventStream$.sendActionExecutionStart({
126
+ actionExecutionId: currentToolCallId,
127
+ actionName: chunk.content_block.name,
128
+ });
126
129
  mode = "function";
127
130
  }
128
131
  } else if (chunk.type === "content_block_delta") {
@@ -130,21 +133,27 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
130
133
  const text = filterThinkingTextBuffer.onTextChunk(chunk.delta.text);
131
134
  if (text.length > 0) {
132
135
  if (!didOutputText) {
133
- eventStream$.sendTextMessageStart(currentMessageId);
136
+ eventStream$.sendTextMessageStart({ messageId: currentMessageId });
134
137
  didOutputText = true;
135
138
  }
136
- eventStream$.sendTextMessageContent(text);
139
+ eventStream$.sendTextMessageContent({
140
+ messageId: currentMessageId,
141
+ content: text,
142
+ });
137
143
  }
138
144
  } else if (chunk.delta.type === "input_json_delta") {
139
- eventStream$.sendActionExecutionArgs(chunk.delta.partial_json);
145
+ eventStream$.sendActionExecutionArgs({
146
+ actionExecutionId: currentToolCallId,
147
+ args: chunk.delta.partial_json,
148
+ });
140
149
  }
141
150
  } else if (chunk.type === "content_block_stop") {
142
151
  if (mode === "message") {
143
152
  if (didOutputText) {
144
- eventStream$.sendTextMessageEnd();
153
+ eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
145
154
  }
146
155
  } else if (mode === "function") {
147
- eventStream$.sendActionExecutionEnd();
156
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: currentToolCallId });
148
157
  }
149
158
  }
150
159
  }