@copilotkit/runtime 0.0.0-feat-dynamic-copilotcloud-qa-20250117190454
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/.eslintrc.js +7 -0
- package/CHANGELOG.md +913 -0
- package/README.md +46 -0
- package/__snapshots__/schema/schema.graphql +273 -0
- package/dist/chunk-44O2JGUY.mjs +12 -0
- package/dist/chunk-44O2JGUY.mjs.map +1 -0
- package/dist/chunk-BETLEV37.mjs +25 -0
- package/dist/chunk-BETLEV37.mjs.map +1 -0
- package/dist/chunk-CLGKEUOA.mjs +1408 -0
- package/dist/chunk-CLGKEUOA.mjs.map +1 -0
- package/dist/chunk-D2WLFQS6.mjs +43 -0
- package/dist/chunk-D2WLFQS6.mjs.map +1 -0
- package/dist/chunk-DFOKBSIS.mjs +1 -0
- package/dist/chunk-DFOKBSIS.mjs.map +1 -0
- package/dist/chunk-FA5DJ2TZ.mjs +3437 -0
- package/dist/chunk-FA5DJ2TZ.mjs.map +1 -0
- package/dist/chunk-HNUNXFTW.mjs +129 -0
- package/dist/chunk-HNUNXFTW.mjs.map +1 -0
- package/dist/chunk-SFLMY3ES.mjs +80 -0
- package/dist/chunk-SFLMY3ES.mjs.map +1 -0
- package/dist/chunk-U3V2BCGI.mjs +152 -0
- package/dist/chunk-U3V2BCGI.mjs.map +1 -0
- package/dist/chunk-ZCU6UPCY.mjs +25 -0
- package/dist/chunk-ZCU6UPCY.mjs.map +1 -0
- package/dist/copilot-runtime-1a224a0f.d.ts +196 -0
- package/dist/graphql/types/base/index.d.ts +6 -0
- package/dist/graphql/types/base/index.js +63 -0
- package/dist/graphql/types/base/index.js.map +1 -0
- package/dist/graphql/types/base/index.mjs +8 -0
- package/dist/graphql/types/base/index.mjs.map +1 -0
- package/dist/graphql/types/converted/index.d.ts +2 -0
- package/dist/graphql/types/converted/index.js +187 -0
- package/dist/graphql/types/converted/index.js.map +1 -0
- package/dist/graphql/types/converted/index.mjs +17 -0
- package/dist/graphql/types/converted/index.mjs.map +1 -0
- package/dist/groq-adapter-c35c5374.d.ts +281 -0
- package/dist/index-24315d90.d.ts +103 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +5258 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +76 -0
- package/dist/index.mjs.map +1 -0
- package/dist/langserve-a16ef8f4.d.ts +180 -0
- package/dist/lib/cloud/index.d.ts +6 -0
- package/dist/lib/cloud/index.js +18 -0
- package/dist/lib/cloud/index.js.map +1 -0
- package/dist/lib/cloud/index.mjs +1 -0
- package/dist/lib/cloud/index.mjs.map +1 -0
- package/dist/lib/index.d.ts +20 -0
- package/dist/lib/index.js +4906 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/index.mjs +58 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/integrations/index.d.ts +33 -0
- package/dist/lib/integrations/index.js +2229 -0
- package/dist/lib/integrations/index.js.map +1 -0
- package/dist/lib/integrations/index.mjs +34 -0
- package/dist/lib/integrations/index.mjs.map +1 -0
- package/dist/lib/integrations/nest/index.d.ts +14 -0
- package/dist/lib/integrations/nest/index.js +2138 -0
- package/dist/lib/integrations/nest/index.js.map +1 -0
- package/dist/lib/integrations/nest/index.mjs +13 -0
- package/dist/lib/integrations/nest/index.mjs.map +1 -0
- package/dist/lib/integrations/node-express/index.d.ts +14 -0
- package/dist/lib/integrations/node-express/index.js +2138 -0
- package/dist/lib/integrations/node-express/index.js.map +1 -0
- package/dist/lib/integrations/node-express/index.mjs +13 -0
- package/dist/lib/integrations/node-express/index.mjs.map +1 -0
- package/dist/lib/integrations/node-http/index.d.ts +14 -0
- package/dist/lib/integrations/node-http/index.js +2124 -0
- package/dist/lib/integrations/node-http/index.js.map +1 -0
- package/dist/lib/integrations/node-http/index.mjs +12 -0
- package/dist/lib/integrations/node-http/index.mjs.map +1 -0
- package/dist/service-adapters/index.d.ts +84 -0
- package/dist/service-adapters/index.js +1448 -0
- package/dist/service-adapters/index.js.map +1 -0
- package/dist/service-adapters/index.mjs +26 -0
- package/dist/service-adapters/index.mjs.map +1 -0
- package/dist/utils/index.d.ts +49 -0
- package/dist/utils/index.js +174 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +12 -0
- package/dist/utils/index.mjs.map +1 -0
- package/jest.config.js +5 -0
- package/package.json +85 -0
- package/scripts/generate-gql-schema.ts +13 -0
- package/src/agents/langgraph/event-source.ts +287 -0
- package/src/agents/langgraph/events.ts +338 -0
- package/src/graphql/inputs/action.input.ts +16 -0
- package/src/graphql/inputs/agent-session.input.ts +13 -0
- package/src/graphql/inputs/agent-state.input.ts +10 -0
- package/src/graphql/inputs/cloud-guardrails.input.ts +16 -0
- package/src/graphql/inputs/cloud.input.ts +8 -0
- package/src/graphql/inputs/context-property.input.ts +10 -0
- package/src/graphql/inputs/custom-property.input.ts +15 -0
- package/src/graphql/inputs/forwarded-parameters.input.ts +22 -0
- package/src/graphql/inputs/frontend.input.ts +14 -0
- package/src/graphql/inputs/generate-copilot-response.input.ts +47 -0
- package/src/graphql/inputs/message.input.ts +92 -0
- package/src/graphql/resolvers/copilot.resolver.ts +556 -0
- package/src/graphql/types/agents-response.type.ts +22 -0
- package/src/graphql/types/base/index.ts +10 -0
- package/src/graphql/types/converted/index.ts +136 -0
- package/src/graphql/types/copilot-response.type.ts +113 -0
- package/src/graphql/types/enums.ts +37 -0
- package/src/graphql/types/guardrails-result.type.ts +20 -0
- package/src/graphql/types/message-status.type.ts +40 -0
- package/src/graphql/types/response-status.type.ts +66 -0
- package/src/index.ts +4 -0
- package/src/lib/cloud/index.ts +4 -0
- package/src/lib/index.ts +8 -0
- package/src/lib/integrations/index.ts +6 -0
- package/src/lib/integrations/nest/index.ts +17 -0
- package/src/lib/integrations/nextjs/app-router.ts +40 -0
- package/src/lib/integrations/nextjs/pages-router.ts +49 -0
- package/src/lib/integrations/node-express/index.ts +17 -0
- package/src/lib/integrations/node-http/index.ts +34 -0
- package/src/lib/integrations/shared.ts +109 -0
- package/src/lib/logger.ts +28 -0
- package/src/lib/runtime/copilot-runtime.ts +466 -0
- package/src/lib/runtime/remote-action-constructors.ts +304 -0
- package/src/lib/runtime/remote-actions.ts +174 -0
- package/src/lib/runtime/remote-lg-action.ts +657 -0
- package/src/lib/telemetry-client.ts +52 -0
- package/src/service-adapters/anthropic/anthropic-adapter.ts +205 -0
- package/src/service-adapters/anthropic/utils.ts +144 -0
- package/src/service-adapters/conversion.ts +64 -0
- package/src/service-adapters/events.ts +419 -0
- package/src/service-adapters/experimental/empty/empty-adapter.ts +33 -0
- package/src/service-adapters/experimental/ollama/ollama-adapter.ts +79 -0
- package/src/service-adapters/google/google-genai-adapter.ts +39 -0
- package/src/service-adapters/groq/groq-adapter.ts +173 -0
- package/src/service-adapters/index.ts +16 -0
- package/src/service-adapters/langchain/langchain-adapter.ts +99 -0
- package/src/service-adapters/langchain/langserve.ts +87 -0
- package/src/service-adapters/langchain/types.ts +14 -0
- package/src/service-adapters/langchain/utils.ts +306 -0
- package/src/service-adapters/openai/openai-adapter.ts +210 -0
- package/src/service-adapters/openai/openai-assistant-adapter.ts +304 -0
- package/src/service-adapters/openai/utils.ts +161 -0
- package/src/service-adapters/service-adapter.ts +30 -0
- package/src/service-adapters/unify/unify-adapter.ts +145 -0
- package/src/utils/failed-response-status-reasons.ts +48 -0
- package/src/utils/index.ts +1 -0
- package/tsconfig.json +11 -0
- package/tsup.config.ts +16 -0
- package/typedoc.json +4 -0
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
import { Client } from "@langchain/langgraph-sdk";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
+
import { parse as parsePartialJson } from "partial-json";
|
|
4
|
+
import { Logger } from "pino";
|
|
5
|
+
import { ActionInput } from "../../graphql/inputs/action.input";
|
|
6
|
+
import { LangGraphPlatformAgent, LangGraphPlatformEndpoint } from "./remote-actions";
|
|
7
|
+
import { CopilotRequestContextProperties } from "../integrations";
|
|
8
|
+
import { ActionExecutionMessage, Message, MessageType } from "../../graphql/types/converted";
|
|
9
|
+
import { MessageRole } from "../../graphql/types/enums";
|
|
10
|
+
import { CustomEventNames, LangGraphEventTypes } from "../../agents/langgraph/events";
|
|
11
|
+
import telemetry from "../telemetry-client";
|
|
12
|
+
|
|
13
|
+
type State = Record<string, any>;
|
|
14
|
+
|
|
15
|
+
type ExecutionAction = Pick<ActionInput, "name" | "description"> & { parameters: string };
|
|
16
|
+
|
|
17
|
+
interface ExecutionArgs extends Omit<LangGraphPlatformEndpoint, "agents"> {
|
|
18
|
+
agent: LangGraphPlatformAgent;
|
|
19
|
+
threadId: string;
|
|
20
|
+
nodeName: string;
|
|
21
|
+
messages: Message[];
|
|
22
|
+
state: State;
|
|
23
|
+
properties: CopilotRequestContextProperties;
|
|
24
|
+
actions: ExecutionAction[];
|
|
25
|
+
logger: Logger;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// The following types are our own definition to the messages accepted by LangGraph Platform, enhanced with some of our extra data.
|
|
29
|
+
interface ToolCall {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
args: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type BaseLangGraphPlatformMessage = Omit<
|
|
36
|
+
Message,
|
|
37
|
+
| "isResultMessage"
|
|
38
|
+
| "isTextMessage"
|
|
39
|
+
| "isActionExecutionMessage"
|
|
40
|
+
| "isAgentStateMessage"
|
|
41
|
+
| "type"
|
|
42
|
+
| "createdAt"
|
|
43
|
+
> & {
|
|
44
|
+
content: string;
|
|
45
|
+
role: MessageRole;
|
|
46
|
+
additional_kwargs?: Record<string, unknown>;
|
|
47
|
+
type: MessageType;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
interface LangGraphPlatformResultMessage extends BaseLangGraphPlatformMessage {
|
|
51
|
+
tool_call_id: string;
|
|
52
|
+
name: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface LangGraphPlatformActionExecutionMessage extends BaseLangGraphPlatformMessage {
|
|
56
|
+
tool_calls: ToolCall[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type LangGraphPlatformMessage =
|
|
60
|
+
| LangGraphPlatformActionExecutionMessage
|
|
61
|
+
| LangGraphPlatformResultMessage
|
|
62
|
+
| BaseLangGraphPlatformMessage;
|
|
63
|
+
|
|
64
|
+
export async function execute(args: ExecutionArgs): Promise<ReadableStream<Uint8Array>> {
|
|
65
|
+
return new ReadableStream({
|
|
66
|
+
async start(controller) {
|
|
67
|
+
try {
|
|
68
|
+
await streamEvents(controller, args);
|
|
69
|
+
controller.close();
|
|
70
|
+
} catch (err) {}
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function streamEvents(controller: ReadableStreamDefaultController, args: ExecutionArgs) {
|
|
76
|
+
const {
|
|
77
|
+
deploymentUrl,
|
|
78
|
+
langsmithApiKey,
|
|
79
|
+
threadId: agrsInitialThreadId,
|
|
80
|
+
agent,
|
|
81
|
+
nodeName: initialNodeName,
|
|
82
|
+
state: initialState,
|
|
83
|
+
messages,
|
|
84
|
+
actions,
|
|
85
|
+
logger,
|
|
86
|
+
} = args;
|
|
87
|
+
|
|
88
|
+
let nodeName = initialNodeName;
|
|
89
|
+
let state = initialState;
|
|
90
|
+
const { name, assistantId: initialAssistantId } = agent;
|
|
91
|
+
|
|
92
|
+
const client = new Client({ apiUrl: deploymentUrl, apiKey: langsmithApiKey });
|
|
93
|
+
let initialThreadId = agrsInitialThreadId;
|
|
94
|
+
const wasInitiatedWithExistingThread = !!initialThreadId;
|
|
95
|
+
if (initialThreadId && initialThreadId.startsWith("ck-")) {
|
|
96
|
+
initialThreadId = initialThreadId.substring(3);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const threadId = initialThreadId ?? randomUUID();
|
|
100
|
+
if (initialThreadId === threadId) {
|
|
101
|
+
await client.threads.get(threadId);
|
|
102
|
+
} else {
|
|
103
|
+
await client.threads.create({ threadId: threadId });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let agentState = { values: {} };
|
|
107
|
+
if (wasInitiatedWithExistingThread) {
|
|
108
|
+
agentState = await client.threads.getState(threadId);
|
|
109
|
+
}
|
|
110
|
+
const agentStateValues = agentState.values as State;
|
|
111
|
+
state.messages = agentStateValues.messages;
|
|
112
|
+
const mode = wasInitiatedWithExistingThread && nodeName != "__end__" ? "continue" : "start";
|
|
113
|
+
let formattedMessages = [];
|
|
114
|
+
try {
|
|
115
|
+
formattedMessages = copilotkitMessagesToLangChain(messages);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
logger.error(e, `Error event thrown: ${e.message}`);
|
|
118
|
+
}
|
|
119
|
+
state = langGraphDefaultMergeState(state, formattedMessages, actions, name);
|
|
120
|
+
|
|
121
|
+
if (mode === "continue") {
|
|
122
|
+
await client.threads.updateState(threadId, { values: state, asNode: nodeName });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let streamInfo: {
|
|
126
|
+
provider?: string;
|
|
127
|
+
langGraphHost?: string;
|
|
128
|
+
langGraphVersion?: string;
|
|
129
|
+
hashedLgcKey: string;
|
|
130
|
+
} = {
|
|
131
|
+
hashedLgcKey: createHash("sha256").update(langsmithApiKey).digest("hex"),
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const assistants = await client.assistants.search();
|
|
135
|
+
const retrievedAssistant = assistants.find(
|
|
136
|
+
(a) => a.name === name || a.assistant_id === initialAssistantId,
|
|
137
|
+
);
|
|
138
|
+
if (!retrievedAssistant) {
|
|
139
|
+
telemetry.capture("oss.runtime.agent_execution_stream_errored", {
|
|
140
|
+
...streamInfo,
|
|
141
|
+
error: `Found no assistants for given information, while ${assistants.length} assistants exists`,
|
|
142
|
+
});
|
|
143
|
+
console.error(`
|
|
144
|
+
No agent found for the agent name specified in CopilotKit provider
|
|
145
|
+
Please check your available agents or provide an agent ID in the LangGraph Platform endpoint definition.\n
|
|
146
|
+
|
|
147
|
+
These are the available agents: [${assistants.map((a) => `${a.name} (ID: ${a.assistant_id})`).join(", ")}]
|
|
148
|
+
`);
|
|
149
|
+
throw new Error("No agent id found");
|
|
150
|
+
}
|
|
151
|
+
const assistantId = retrievedAssistant.assistant_id;
|
|
152
|
+
|
|
153
|
+
const graphInfo = await client.assistants.getGraph(assistantId);
|
|
154
|
+
const streamInput = mode === "start" ? state : null;
|
|
155
|
+
|
|
156
|
+
let streamingStateExtractor = new StreamingStateExtractor([]);
|
|
157
|
+
let prevNodeName = null;
|
|
158
|
+
let emitIntermediateStateUntilEnd = null;
|
|
159
|
+
let shouldExit = false;
|
|
160
|
+
let externalRunId = null;
|
|
161
|
+
|
|
162
|
+
const streamResponse = client.runs.stream(threadId, assistantId, {
|
|
163
|
+
input: streamInput,
|
|
164
|
+
streamMode: ["events", "values"],
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const emit = (message: string) => controller.enqueue(new TextEncoder().encode(message));
|
|
168
|
+
|
|
169
|
+
let latestStateValues = {};
|
|
170
|
+
let updatedState = state;
|
|
171
|
+
// If a manual emittance happens, it is the ultimate source of truth of state, unless a node has exited.
|
|
172
|
+
// Therefore, this value should either hold null, or the only edition of state that should be used.
|
|
173
|
+
let manuallyEmittedState = null;
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
telemetry.capture("oss.runtime.agent_execution_stream_started", {
|
|
177
|
+
hashedLgcKey: streamInfo.hashedLgcKey,
|
|
178
|
+
});
|
|
179
|
+
for await (const chunk of streamResponse) {
|
|
180
|
+
if (!["events", "values", "error"].includes(chunk.event)) continue;
|
|
181
|
+
|
|
182
|
+
if (chunk.event === "error") {
|
|
183
|
+
throw new Error(`Error event thrown: ${chunk.data.message}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (chunk.event === "values") {
|
|
187
|
+
latestStateValues = chunk.data;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const event = chunk.data;
|
|
192
|
+
const currentNodeName = event.name;
|
|
193
|
+
const eventType = event.event;
|
|
194
|
+
const runId = event.metadata.run_id;
|
|
195
|
+
externalRunId = runId;
|
|
196
|
+
const metadata = event.metadata;
|
|
197
|
+
|
|
198
|
+
if (event.data?.output?.model != null && event.data?.output?.model != "") {
|
|
199
|
+
streamInfo.provider = event.data?.output?.model;
|
|
200
|
+
}
|
|
201
|
+
if (metadata.langgraph_host != null && metadata.langgraph_host != "") {
|
|
202
|
+
streamInfo.langGraphHost = metadata.langgraph_host;
|
|
203
|
+
}
|
|
204
|
+
if (metadata.langgraph_version != null && metadata.langgraph_version != "") {
|
|
205
|
+
streamInfo.langGraphVersion = metadata.langgraph_version;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
shouldExit =
|
|
209
|
+
shouldExit ||
|
|
210
|
+
(eventType === LangGraphEventTypes.OnCustomEvent &&
|
|
211
|
+
event.name === CustomEventNames.CopilotKitExit);
|
|
212
|
+
|
|
213
|
+
const emitIntermediateState = metadata["copilotkit:emit-intermediate-state"];
|
|
214
|
+
const manuallyEmitIntermediateState =
|
|
215
|
+
eventType === LangGraphEventTypes.OnCustomEvent &&
|
|
216
|
+
event.name === CustomEventNames.CopilotKitManuallyEmitIntermediateState;
|
|
217
|
+
|
|
218
|
+
const exitingNode =
|
|
219
|
+
nodeName === currentNodeName && eventType === LangGraphEventTypes.OnChainEnd;
|
|
220
|
+
|
|
221
|
+
// See manuallyEmittedState for explanation
|
|
222
|
+
if (exitingNode) {
|
|
223
|
+
manuallyEmittedState = null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// we only want to update the node name under certain conditions
|
|
227
|
+
// since we don't need any internal node names to be sent to the frontend
|
|
228
|
+
if (graphInfo["nodes"].some((node) => node.id === currentNodeName)) {
|
|
229
|
+
nodeName = currentNodeName;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
updatedState = manuallyEmittedState ?? latestStateValues;
|
|
233
|
+
|
|
234
|
+
if (!nodeName) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (manuallyEmitIntermediateState) {
|
|
239
|
+
// See manuallyEmittedState for explanation
|
|
240
|
+
manuallyEmittedState = event.data;
|
|
241
|
+
emit(
|
|
242
|
+
getStateSyncEvent({
|
|
243
|
+
threadId,
|
|
244
|
+
runId,
|
|
245
|
+
agentName: agent.name,
|
|
246
|
+
nodeName,
|
|
247
|
+
state: manuallyEmittedState,
|
|
248
|
+
running: true,
|
|
249
|
+
active: true,
|
|
250
|
+
}),
|
|
251
|
+
);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (emitIntermediateState && emitIntermediateStateUntilEnd == null) {
|
|
256
|
+
emitIntermediateStateUntilEnd = nodeName;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (emitIntermediateState && eventType === LangGraphEventTypes.OnChatModelStart) {
|
|
260
|
+
// reset the streaming state extractor
|
|
261
|
+
streamingStateExtractor = new StreamingStateExtractor(emitIntermediateState);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (emitIntermediateState && eventType === LangGraphEventTypes.OnChatModelStream) {
|
|
265
|
+
streamingStateExtractor.bufferToolCalls(event);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (emitIntermediateStateUntilEnd !== null) {
|
|
269
|
+
updatedState = {
|
|
270
|
+
...updatedState,
|
|
271
|
+
...streamingStateExtractor.extractState(),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (
|
|
276
|
+
!emitIntermediateState &&
|
|
277
|
+
currentNodeName === emitIntermediateStateUntilEnd &&
|
|
278
|
+
eventType === LangGraphEventTypes.OnChainEnd
|
|
279
|
+
) {
|
|
280
|
+
// stop emitting function call state
|
|
281
|
+
emitIntermediateStateUntilEnd = null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (
|
|
285
|
+
JSON.stringify(updatedState) !== JSON.stringify(state) ||
|
|
286
|
+
prevNodeName != nodeName ||
|
|
287
|
+
exitingNode
|
|
288
|
+
) {
|
|
289
|
+
state = updatedState;
|
|
290
|
+
prevNodeName = nodeName;
|
|
291
|
+
emit(
|
|
292
|
+
getStateSyncEvent({
|
|
293
|
+
threadId,
|
|
294
|
+
runId,
|
|
295
|
+
agentName: agent.name,
|
|
296
|
+
nodeName,
|
|
297
|
+
state,
|
|
298
|
+
running: true,
|
|
299
|
+
active: !exitingNode,
|
|
300
|
+
}),
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
emit(JSON.stringify(event) + "\n");
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
state = await client.threads.getState(threadId);
|
|
308
|
+
const isEndNode = state.next.length === 0;
|
|
309
|
+
nodeName = Object.keys(state.metadata.writes)[0];
|
|
310
|
+
|
|
311
|
+
telemetry.capture("oss.runtime.agent_execution_stream_ended", streamInfo);
|
|
312
|
+
|
|
313
|
+
emit(
|
|
314
|
+
getStateSyncEvent({
|
|
315
|
+
threadId,
|
|
316
|
+
runId: externalRunId,
|
|
317
|
+
agentName: agent.name,
|
|
318
|
+
nodeName: isEndNode ? "__end__" : nodeName,
|
|
319
|
+
state: state.values,
|
|
320
|
+
running: !shouldExit,
|
|
321
|
+
active: false,
|
|
322
|
+
includeMessages: true,
|
|
323
|
+
}),
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
return Promise.resolve();
|
|
327
|
+
} catch (e) {
|
|
328
|
+
logger.error(e);
|
|
329
|
+
telemetry.capture("oss.runtime.agent_execution_stream_errored", {
|
|
330
|
+
...streamInfo,
|
|
331
|
+
error: e.message,
|
|
332
|
+
});
|
|
333
|
+
return Promise.resolve();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function getStateSyncEvent({
|
|
338
|
+
threadId,
|
|
339
|
+
runId,
|
|
340
|
+
agentName,
|
|
341
|
+
nodeName,
|
|
342
|
+
state,
|
|
343
|
+
running,
|
|
344
|
+
active,
|
|
345
|
+
includeMessages = false,
|
|
346
|
+
}: {
|
|
347
|
+
threadId: string;
|
|
348
|
+
runId: string;
|
|
349
|
+
agentName: string;
|
|
350
|
+
nodeName: string;
|
|
351
|
+
state: State;
|
|
352
|
+
running: boolean;
|
|
353
|
+
active: boolean;
|
|
354
|
+
includeMessages?: boolean;
|
|
355
|
+
}): string {
|
|
356
|
+
if (!includeMessages) {
|
|
357
|
+
state = Object.keys(state).reduce((acc, key) => {
|
|
358
|
+
if (key !== "messages") {
|
|
359
|
+
acc[key] = state[key];
|
|
360
|
+
}
|
|
361
|
+
return acc;
|
|
362
|
+
}, {} as State);
|
|
363
|
+
} else {
|
|
364
|
+
state = {
|
|
365
|
+
...state,
|
|
366
|
+
messages: langchainMessagesToCopilotKit(state.messages || []),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return (
|
|
371
|
+
JSON.stringify({
|
|
372
|
+
event: LangGraphEventTypes.OnCopilotKitStateSync,
|
|
373
|
+
thread_id: threadId,
|
|
374
|
+
run_id: runId,
|
|
375
|
+
agent_name: agentName,
|
|
376
|
+
node_name: nodeName,
|
|
377
|
+
active: active,
|
|
378
|
+
state: state,
|
|
379
|
+
running: running,
|
|
380
|
+
role: "assistant",
|
|
381
|
+
}) + "\n"
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
class StreamingStateExtractor {
|
|
386
|
+
private emitIntermediateState: { [key: string]: any }[];
|
|
387
|
+
private toolCallBuffer: { [key: string]: string };
|
|
388
|
+
private currentToolCall: string | null;
|
|
389
|
+
private previouslyParsableState: { [key: string]: any };
|
|
390
|
+
|
|
391
|
+
constructor(emitIntermediateState: { [key: string]: any }[]) {
|
|
392
|
+
this.emitIntermediateState = emitIntermediateState;
|
|
393
|
+
this.toolCallBuffer = {};
|
|
394
|
+
this.currentToolCall = null;
|
|
395
|
+
this.previouslyParsableState = {};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
bufferToolCalls(event: {
|
|
399
|
+
data: { chunk: { tool_call_chunks: { name: string | null; args: string }[] } };
|
|
400
|
+
}) {
|
|
401
|
+
if (event.data.chunk.tool_call_chunks.length > 0) {
|
|
402
|
+
const chunk = event.data.chunk.tool_call_chunks[0];
|
|
403
|
+
|
|
404
|
+
if (chunk.name !== null && chunk.name !== undefined) {
|
|
405
|
+
this.currentToolCall = chunk.name;
|
|
406
|
+
this.toolCallBuffer[this.currentToolCall] = chunk.args;
|
|
407
|
+
} else if (this.currentToolCall !== null && this.currentToolCall !== undefined) {
|
|
408
|
+
this.toolCallBuffer[this.currentToolCall] += chunk.args;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
getEmitStateConfig(currentToolName: string): [string | null, string | null] {
|
|
414
|
+
for (const config of this.emitIntermediateState) {
|
|
415
|
+
const stateKey = config["state_key"];
|
|
416
|
+
const tool = config["tool"];
|
|
417
|
+
const toolArgument = config["tool_argument"];
|
|
418
|
+
|
|
419
|
+
if (currentToolName === tool) {
|
|
420
|
+
return [toolArgument, stateKey];
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return [null, null];
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
extractState(): State {
|
|
427
|
+
const state: State = {};
|
|
428
|
+
|
|
429
|
+
for (const [key, value] of Object.entries(this.toolCallBuffer)) {
|
|
430
|
+
const [argumentName, stateKey] = this.getEmitStateConfig(key);
|
|
431
|
+
|
|
432
|
+
if (stateKey === null) {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
let parsedValue;
|
|
437
|
+
try {
|
|
438
|
+
parsedValue = parsePartialJson(value);
|
|
439
|
+
} catch (error) {
|
|
440
|
+
if (key in this.previouslyParsableState) {
|
|
441
|
+
parsedValue = this.previouslyParsableState[key];
|
|
442
|
+
} else {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
this.previouslyParsableState[key] = parsedValue;
|
|
448
|
+
|
|
449
|
+
if (!argumentName) {
|
|
450
|
+
state[stateKey] = parsedValue;
|
|
451
|
+
} else {
|
|
452
|
+
state[stateKey] = parsedValue[argumentName];
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return state;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Start of Selection
|
|
461
|
+
function langGraphDefaultMergeState(
|
|
462
|
+
state: State,
|
|
463
|
+
messages: LangGraphPlatformMessage[],
|
|
464
|
+
actions: ExecutionAction[],
|
|
465
|
+
agentName: string,
|
|
466
|
+
): State {
|
|
467
|
+
if (messages.length > 0 && "role" in messages[0] && messages[0].role === "system") {
|
|
468
|
+
// remove system message
|
|
469
|
+
messages = messages.slice(1);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// merge with existing messages
|
|
473
|
+
const existingMessages: LangGraphPlatformMessage[] = state.messages || [];
|
|
474
|
+
const existingMessageIds = new Set(existingMessages.map((message) => message.id));
|
|
475
|
+
const newMessages = messages.filter((message) => !existingMessageIds.has(message.id));
|
|
476
|
+
|
|
477
|
+
return {
|
|
478
|
+
...state,
|
|
479
|
+
messages: newMessages,
|
|
480
|
+
copilotkit: {
|
|
481
|
+
actions,
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function langchainMessagesToCopilotKit(messages: any[]): any[] {
|
|
487
|
+
const result: any[] = [];
|
|
488
|
+
const tool_call_names: Record<string, string> = {};
|
|
489
|
+
|
|
490
|
+
// First pass: gather all tool call names from AI messages
|
|
491
|
+
for (const message of messages) {
|
|
492
|
+
if (message.type === "ai") {
|
|
493
|
+
for (const tool_call of message.tool_calls) {
|
|
494
|
+
tool_call_names[tool_call.id] = tool_call.name;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
for (const message of messages) {
|
|
500
|
+
let content: any = message.content;
|
|
501
|
+
if (content instanceof Array) {
|
|
502
|
+
content = content[0];
|
|
503
|
+
}
|
|
504
|
+
if (content instanceof Object) {
|
|
505
|
+
content = content.text;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (message.type === "human") {
|
|
509
|
+
result.push({
|
|
510
|
+
role: "user",
|
|
511
|
+
content: content,
|
|
512
|
+
id: message.id,
|
|
513
|
+
});
|
|
514
|
+
} else if (message.type === "system") {
|
|
515
|
+
result.push({
|
|
516
|
+
role: "system",
|
|
517
|
+
content: content,
|
|
518
|
+
id: message.id,
|
|
519
|
+
});
|
|
520
|
+
} else if (message.type === "ai") {
|
|
521
|
+
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
522
|
+
for (const tool_call of message.tool_calls) {
|
|
523
|
+
result.push({
|
|
524
|
+
id: tool_call.id,
|
|
525
|
+
name: tool_call.name,
|
|
526
|
+
arguments: tool_call.args,
|
|
527
|
+
parentMessageId: message.id,
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
} else {
|
|
531
|
+
result.push({
|
|
532
|
+
role: "assistant",
|
|
533
|
+
content: content,
|
|
534
|
+
id: message.id,
|
|
535
|
+
parentMessageId: message.id,
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
} else if (message.type === "tool") {
|
|
539
|
+
const actionName = tool_call_names[message.tool_call_id] || message.name || "";
|
|
540
|
+
result.push({
|
|
541
|
+
actionExecutionId: message.tool_call_id,
|
|
542
|
+
actionName: actionName,
|
|
543
|
+
result: content,
|
|
544
|
+
id: message.id,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
const resultsDict: Record<string, any> = {};
|
|
549
|
+
for (const msg of result) {
|
|
550
|
+
if (msg.actionExecutionId) {
|
|
551
|
+
resultsDict[msg.actionExecutionId] = msg;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const reorderedResult: Message[] = [];
|
|
556
|
+
|
|
557
|
+
for (const msg of result) {
|
|
558
|
+
// If it's not a tool result, just append it
|
|
559
|
+
if (!("actionExecutionId" in msg)) {
|
|
560
|
+
reorderedResult.push(msg);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// If the message has arguments (i.e., is a tool call invocation),
|
|
564
|
+
// append the corresponding result right after it
|
|
565
|
+
if ("arguments" in msg) {
|
|
566
|
+
const msgId = msg.id;
|
|
567
|
+
if (msgId in resultsDict) {
|
|
568
|
+
reorderedResult.push(resultsDict[msgId]);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return reorderedResult;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function copilotkitMessagesToLangChain(messages: Message[]): LangGraphPlatformMessage[] {
|
|
577
|
+
const result: LangGraphPlatformMessage[] = [];
|
|
578
|
+
const processedActionExecutions = new Set<string>();
|
|
579
|
+
|
|
580
|
+
for (const message of messages) {
|
|
581
|
+
// Handle TextMessage
|
|
582
|
+
if (message.isTextMessage()) {
|
|
583
|
+
if (message.role === "user") {
|
|
584
|
+
// Human message
|
|
585
|
+
result.push({
|
|
586
|
+
...message,
|
|
587
|
+
role: MessageRole.user,
|
|
588
|
+
});
|
|
589
|
+
} else if (message.role === "system") {
|
|
590
|
+
// System message
|
|
591
|
+
result.push({
|
|
592
|
+
...message,
|
|
593
|
+
role: MessageRole.system,
|
|
594
|
+
});
|
|
595
|
+
} else if (message.role === "assistant") {
|
|
596
|
+
// Assistant message
|
|
597
|
+
result.push({
|
|
598
|
+
...message,
|
|
599
|
+
role: MessageRole.assistant,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// Handle ActionExecutionMessage (multiple tool calls per parentMessageId)
|
|
606
|
+
if (message.isActionExecutionMessage()) {
|
|
607
|
+
const messageId = message.parentMessageId ?? message.id;
|
|
608
|
+
|
|
609
|
+
// If we've already processed this action execution group, skip
|
|
610
|
+
if (processedActionExecutions.has(messageId)) {
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
processedActionExecutions.add(messageId);
|
|
615
|
+
|
|
616
|
+
// Gather all tool calls related to this messageId
|
|
617
|
+
const relatedActionExecutions = messages.filter(
|
|
618
|
+
(m) =>
|
|
619
|
+
m.isActionExecutionMessage() &&
|
|
620
|
+
((m.parentMessageId && m.parentMessageId === messageId) || m.id === messageId),
|
|
621
|
+
) as ActionExecutionMessage[];
|
|
622
|
+
|
|
623
|
+
const tool_calls: ToolCall[] = relatedActionExecutions.map((m) => ({
|
|
624
|
+
name: m.name,
|
|
625
|
+
args: m.arguments,
|
|
626
|
+
id: m.id,
|
|
627
|
+
}));
|
|
628
|
+
|
|
629
|
+
result.push({
|
|
630
|
+
id: messageId,
|
|
631
|
+
type: "ActionExecutionMessage",
|
|
632
|
+
content: "",
|
|
633
|
+
tool_calls: tool_calls,
|
|
634
|
+
role: MessageRole.assistant,
|
|
635
|
+
} satisfies LangGraphPlatformActionExecutionMessage);
|
|
636
|
+
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Handle ResultMessage
|
|
641
|
+
if (message.isResultMessage()) {
|
|
642
|
+
result.push({
|
|
643
|
+
type: message.type,
|
|
644
|
+
content: message.result,
|
|
645
|
+
id: message.id,
|
|
646
|
+
tool_call_id: message.actionExecutionId,
|
|
647
|
+
name: message.actionName,
|
|
648
|
+
role: MessageRole.tool,
|
|
649
|
+
} satisfies LangGraphPlatformResultMessage);
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
throw new Error(`Unknown message type ${message.type}`);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return result;
|
|
657
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { TelemetryClient } from "@copilotkit/shared";
|
|
2
|
+
import { EndpointType, LangGraphPlatformEndpoint } 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";
|
|
6
|
+
const packageJson = require("../../package.json");
|
|
7
|
+
|
|
8
|
+
const telemetryClient = new TelemetryClient({
|
|
9
|
+
packageName: packageJson.name,
|
|
10
|
+
packageVersion: packageJson.version,
|
|
11
|
+
});
|
|
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.LangGraphPlatform) {
|
|
29
|
+
// When type is resolved, recreating a const with casting of type
|
|
30
|
+
const ep = endpoint as LangGraphPlatformEndpoint;
|
|
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
|
+
|
|
52
|
+
export default telemetryClient;
|