@langgraph-js/sdk 1.11.0 → 2.0.0
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/LangGraphClient.d.ts +178 -66
- package/dist/LangGraphClient.js +52 -260
- package/dist/MessageProcessor.d.ts +94 -0
- package/dist/MessageProcessor.js +324 -0
- package/dist/TestKit.d.ts +5 -2
- package/dist/client/LanggraphServer.d.ts +2 -0
- package/dist/client/LanggraphServer.js +4 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/types.d.ts +130 -0
- package/dist/types.js +1 -0
- package/dist/ui-store/createChatStore.d.ts +3 -3
- package/dist/ui-store/createChatStore.js +37 -21
- package/package.json +2 -1
- package/src/LangGraphClient.ts +107 -297
- package/src/MessageProcessor.ts +352 -0
- package/src/TestKit.ts +1 -1
- package/src/client/LanggraphServer.ts +6 -0
- package/src/index.ts +2 -0
- package/src/types.ts +166 -0
- package/src/ui-store/createChatStore.ts +40 -20
package/src/LangGraphClient.ts
CHANGED
|
@@ -1,25 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Thread, Message, Assistant, HumanMessage, AIMessage, ToolMessage, Command } from "@langchain/langgraph-sdk";
|
|
2
|
+
import { EventEmitter } from "eventemitter3";
|
|
2
3
|
import { ToolManager } from "./ToolManager.js";
|
|
3
4
|
import { CallToolResult } from "./tool/createTool.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* Defaults to `Infinity`, which means no limit.
|
|
8
|
-
*/
|
|
9
|
-
maxConcurrency?: number;
|
|
10
|
-
/**
|
|
11
|
-
* The maximum number of retries that can be made for a single call,
|
|
12
|
-
* with an exponential backoff between each attempt. Defaults to 6.
|
|
13
|
-
*/
|
|
14
|
-
maxRetries?: number;
|
|
15
|
-
onFailedResponseHook?: any;
|
|
16
|
-
/**
|
|
17
|
-
* Specify a custom fetch implementation.
|
|
18
|
-
*
|
|
19
|
-
* By default we expect the `fetch` is available in the global scope.
|
|
20
|
-
*/
|
|
21
|
-
fetch?: typeof fetch | ((...args: any[]) => any);
|
|
22
|
-
}
|
|
5
|
+
import { ILangGraphClient } from "./types.js";
|
|
6
|
+
import { MessageProcessor } from "./MessageProcessor.js";
|
|
7
|
+
|
|
23
8
|
export type RenderMessage = Message & {
|
|
24
9
|
/** 对于 AIMessage 来说是节点名称,对于工具节点来说是工具名称 */
|
|
25
10
|
name?: string;
|
|
@@ -35,6 +20,7 @@ export type RenderMessage = Message & {
|
|
|
35
20
|
};
|
|
36
21
|
}[];
|
|
37
22
|
};
|
|
23
|
+
sub_agent_messages?: RenderMessage[];
|
|
38
24
|
usage_metadata?: {
|
|
39
25
|
total_tokens: number;
|
|
40
26
|
input_tokens: number;
|
|
@@ -60,51 +46,81 @@ export type SendMessageOptions = {
|
|
|
60
46
|
export interface LangGraphClientConfig {
|
|
61
47
|
apiUrl?: string;
|
|
62
48
|
apiKey?: string;
|
|
63
|
-
callerOptions?:
|
|
49
|
+
callerOptions?: {
|
|
50
|
+
/**
|
|
51
|
+
* The maximum number of concurrent calls that can be made.
|
|
52
|
+
* Defaults to `Infinity`, which means no limit.
|
|
53
|
+
*/
|
|
54
|
+
maxConcurrency?: number;
|
|
55
|
+
/**
|
|
56
|
+
* The maximum number of retries that can be made for a single call,
|
|
57
|
+
* with an exponential backoff between each attempt. Defaults to 6.
|
|
58
|
+
*/
|
|
59
|
+
maxRetries?: number;
|
|
60
|
+
onFailedResponseHook?: any;
|
|
61
|
+
/**
|
|
62
|
+
* Specify a custom fetch implementation.
|
|
63
|
+
*
|
|
64
|
+
* By default we expect the `fetch` is available in the global scope.
|
|
65
|
+
*/
|
|
66
|
+
fetch?: typeof fetch | ((...args: any[]) => any);
|
|
67
|
+
};
|
|
64
68
|
timeoutMs?: number;
|
|
65
69
|
defaultHeaders?: Record<string, string | null | undefined>;
|
|
70
|
+
/** 自定义客户端实现,如果不提供则使用官方 Client */
|
|
71
|
+
client: ILangGraphClient;
|
|
66
72
|
}
|
|
67
73
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
static isToolAssistant(m: Message): m is AIMessage {
|
|
83
|
-
/** @ts-ignore */
|
|
84
|
-
return m.type === "ai" && (m.tool_calls?.length || m.tool_call_chunks?.length);
|
|
85
|
-
}
|
|
74
|
+
// 定义事件数据类型
|
|
75
|
+
export interface LangGraphEvents {
|
|
76
|
+
/** 流开始事件 */
|
|
77
|
+
start: { event: "start" };
|
|
78
|
+
/** 消息部分更新事件 */
|
|
79
|
+
message: { event: "messages/partial"; data: Message[] };
|
|
80
|
+
/** 值更新事件 */
|
|
81
|
+
value: { event: "messages/partial" | "values"; data: { messages?: Message[] } };
|
|
82
|
+
/** 错误事件 */
|
|
83
|
+
error: { event: "error"; data: any };
|
|
84
|
+
/** Thread 创建事件 */
|
|
85
|
+
thread: { event: "thread/create"; data: { thread: Thread } };
|
|
86
|
+
/** 流完成事件 */
|
|
87
|
+
done: { event: "done" };
|
|
86
88
|
}
|
|
87
89
|
|
|
88
|
-
type StreamingUpdateEvent = {
|
|
89
|
-
type: "message" | "value" | "update" | "error" | "thread" | "done" | "start";
|
|
90
|
-
data: any;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
type StreamingUpdateCallback = (event: StreamingUpdateEvent) => void;
|
|
94
|
-
|
|
95
90
|
/**
|
|
96
91
|
* @zh LangGraphClient 类是与 LangGraph 后端交互的主要客户端。
|
|
97
92
|
* @en The LangGraphClient class is the main client for interacting with the LangGraph backend.
|
|
98
93
|
*/
|
|
99
|
-
export class LangGraphClient extends
|
|
94
|
+
export class LangGraphClient<TStateType = unknown, TUpdateType = TStateType> extends EventEmitter<LangGraphEvents> {
|
|
95
|
+
private client: ILangGraphClient<TStateType, TUpdateType>;
|
|
100
96
|
private currentAssistant: Assistant | null = null;
|
|
101
|
-
private currentThread: Thread | null = null;
|
|
102
|
-
private streamingCallbacks: Set<StreamingUpdateCallback> = new Set();
|
|
97
|
+
private currentThread: Thread<TStateType> | null = null;
|
|
103
98
|
tools: ToolManager = new ToolManager();
|
|
104
99
|
stopController: AbortController | null = null;
|
|
100
|
+
/** 用于存储 subAgent 状态数据的键 */
|
|
101
|
+
subAgentsKey = "task_store";
|
|
102
|
+
/** Message 处理器 */
|
|
103
|
+
private messageProcessor: MessageProcessor;
|
|
105
104
|
|
|
106
105
|
constructor(config: LangGraphClientConfig) {
|
|
107
|
-
super(
|
|
106
|
+
super();
|
|
107
|
+
this.client = config.client;
|
|
108
|
+
this.messageProcessor = new MessageProcessor(this.subAgentsKey);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** 代理 assistants 属性到内部 client */
|
|
112
|
+
get assistants() {
|
|
113
|
+
return this.client.assistants;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** 代理 threads 属性到内部 client */
|
|
117
|
+
get threads() {
|
|
118
|
+
return this.client.threads;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** 代理 runs 属性到内部 client */
|
|
122
|
+
get runs() {
|
|
123
|
+
return this.client.runs;
|
|
108
124
|
}
|
|
109
125
|
availableAssistants: Assistant[] = [];
|
|
110
126
|
private listAssistants() {
|
|
@@ -124,7 +140,7 @@ export class LangGraphClient extends Client {
|
|
|
124
140
|
this.availableAssistants = assistants;
|
|
125
141
|
if (assistants.length > 0) {
|
|
126
142
|
if (agentName) {
|
|
127
|
-
this.currentAssistant = assistants.find((assistant) => assistant.graph_id === agentName) || null;
|
|
143
|
+
this.currentAssistant = assistants.find((assistant: any) => assistant.graph_id === agentName) || null;
|
|
128
144
|
if (!this.currentAssistant) {
|
|
129
145
|
throw new Error("Agent not found: " + agentName);
|
|
130
146
|
}
|
|
@@ -161,7 +177,7 @@ export class LangGraphClient extends Client {
|
|
|
161
177
|
}
|
|
162
178
|
|
|
163
179
|
graphVisualize() {
|
|
164
|
-
return this.assistants.getGraph(this.currentAssistant?.assistant_id!, {
|
|
180
|
+
return this.assistants.getGraph((this.currentAssistant as any)?.assistant_id!, {
|
|
165
181
|
xray: true,
|
|
166
182
|
});
|
|
167
183
|
}
|
|
@@ -169,11 +185,14 @@ export class LangGraphClient extends Client {
|
|
|
169
185
|
* @zh 列出所有的 Thread。
|
|
170
186
|
* @en Lists all Threads.
|
|
171
187
|
*/
|
|
172
|
-
async listThreads
|
|
173
|
-
return this.threads.search
|
|
188
|
+
async listThreads() {
|
|
189
|
+
return this.threads.search({
|
|
174
190
|
sortOrder: "desc",
|
|
175
191
|
});
|
|
176
192
|
}
|
|
193
|
+
async deleteThread(threadId: string) {
|
|
194
|
+
return this.threads.delete(threadId);
|
|
195
|
+
}
|
|
177
196
|
|
|
178
197
|
/**
|
|
179
198
|
* @zh 从历史中恢复 Thread 数据。
|
|
@@ -182,202 +201,42 @@ export class LangGraphClient extends Client {
|
|
|
182
201
|
async resetThread(agent: string, threadId: string) {
|
|
183
202
|
await this.initAssistant(agent);
|
|
184
203
|
this.currentThread = await this.threads.get(threadId);
|
|
185
|
-
this.graphState = this.currentThread.values;
|
|
186
|
-
|
|
187
|
-
this.
|
|
188
|
-
|
|
204
|
+
this.graphState = (this.currentThread as any).values;
|
|
205
|
+
const graphMessages = this.graphState?.messages || [];
|
|
206
|
+
this.messageProcessor.setGraphMessages(graphMessages);
|
|
207
|
+
this.emit("value", {
|
|
208
|
+
event: "messages/partial",
|
|
189
209
|
data: {
|
|
190
|
-
|
|
191
|
-
data: {
|
|
192
|
-
messages: this.graphMessages,
|
|
193
|
-
},
|
|
210
|
+
messages: this.messageProcessor.getGraphMessages(),
|
|
194
211
|
},
|
|
195
212
|
});
|
|
196
213
|
return this.currentThread;
|
|
197
214
|
}
|
|
198
215
|
// 从历史中恢复时,应该恢复流式状态
|
|
199
216
|
async resetStream() {
|
|
200
|
-
const runs = await this.runs.list(this.currentThread!.thread_id);
|
|
201
|
-
const runningRun = runs?.find((run) => run.status === "running" || run.status === "pending");
|
|
217
|
+
const runs = await this.runs.list((this.currentThread as any)!.thread_id);
|
|
218
|
+
const runningRun = runs?.find((run: any) => run.status === "running" || run.status === "pending");
|
|
202
219
|
if (runningRun) {
|
|
203
220
|
await this.sendMessage([], { joinRunId: runningRun.run_id });
|
|
204
221
|
}
|
|
205
222
|
}
|
|
206
223
|
|
|
207
|
-
streamingMessage: RenderMessage[] = [];
|
|
208
|
-
/** 图发过来的更新信息 */
|
|
209
|
-
graphMessages: RenderMessage[] = [];
|
|
210
224
|
cloneMessage(message: Message): Message {
|
|
211
|
-
return
|
|
212
|
-
}
|
|
213
|
-
private updateStreamingMessage(message: RenderMessage) {
|
|
214
|
-
const lastMessage = this.streamingMessage[this.streamingMessage.length - 1];
|
|
215
|
-
if (!lastMessage?.id || message.id !== lastMessage.id) {
|
|
216
|
-
this.streamingMessage.push(message);
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
this.streamingMessage[this.streamingMessage.length - 1] = message;
|
|
220
|
-
}
|
|
221
|
-
/** 将 graphMessages 和 streamingMessage 合并,并返回新的消息数组 */
|
|
222
|
-
private combineGraphMessagesWithStreamingMessages() {
|
|
223
|
-
const idMap = new Map<string, RenderMessage>(this.streamingMessage.map((i) => [i.id!, i]));
|
|
224
|
-
return [
|
|
225
|
-
...this.graphMessages.map((i) => {
|
|
226
|
-
if (idMap.has(i.id!)) {
|
|
227
|
-
const newValue = idMap.get(i.id!)!;
|
|
228
|
-
idMap.delete(i.id!);
|
|
229
|
-
return newValue;
|
|
230
|
-
}
|
|
231
|
-
return i;
|
|
232
|
-
}),
|
|
233
|
-
...idMap.values(),
|
|
234
|
-
];
|
|
225
|
+
return this.messageProcessor.cloneMessage(message);
|
|
235
226
|
}
|
|
236
227
|
/**
|
|
237
228
|
* @zh 用于 UI 中的流式渲染中的消息。
|
|
238
229
|
* @en Messages used for streaming rendering in the UI.
|
|
239
230
|
*/
|
|
240
231
|
get renderMessage() {
|
|
241
|
-
|
|
242
|
-
const closedToolCallIds = new Set<string>();
|
|
243
|
-
const result: Message[] = [];
|
|
244
|
-
const inputMessages = this.combineGraphMessagesWithStreamingMessages();
|
|
245
|
-
// console.log(inputMessages);
|
|
246
|
-
// 从后往前遍历,这样可以保证最新的消息在前面
|
|
247
|
-
for (let i = inputMessages.length - 1; i >= 0; i--) {
|
|
248
|
-
const message = this.cloneMessage(inputMessages[i]);
|
|
249
|
-
|
|
250
|
-
if (!message.id) {
|
|
251
|
-
result.unshift(message);
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
if (message.type === "ai") {
|
|
255
|
-
/** @ts-ignore */
|
|
256
|
-
if (!message.name) message.name = this.getGraphNodeNow().name;
|
|
257
|
-
}
|
|
258
|
-
if (StreamingMessageType.isToolAssistant(message)) {
|
|
259
|
-
const m = message;
|
|
260
|
-
// 记录这个 id 的消息,并添加到结果中
|
|
261
|
-
previousMessage.set(message.id, m);
|
|
262
|
-
|
|
263
|
-
/** @ts-ignore */
|
|
264
|
-
const tool_calls: NonNullable<AIMessage["tool_calls"]> = (m as AIMessage).tool_calls?.length ? (m as AIMessage).tool_calls : (m as RenderMessage).tool_call_chunks;
|
|
265
|
-
const new_tool_calls = tool_calls
|
|
266
|
-
.filter((i) => {
|
|
267
|
-
return !closedToolCallIds.has(i.id!);
|
|
268
|
-
})!
|
|
269
|
-
.map((tool, index) => {
|
|
270
|
-
return {
|
|
271
|
-
type: "tool",
|
|
272
|
-
additional_kwargs: {},
|
|
273
|
-
/** @ts-ignore */
|
|
274
|
-
tool_input: m.additional_kwargs?.tool_calls?.[index]?.function?.arguments,
|
|
275
|
-
id: tool.id,
|
|
276
|
-
name: tool.name,
|
|
277
|
-
response_metadata: {},
|
|
278
|
-
tool_call_id: tool.id!,
|
|
279
|
-
content: "",
|
|
280
|
-
} as ToolMessage;
|
|
281
|
-
});
|
|
282
|
-
for (const tool of new_tool_calls) {
|
|
283
|
-
if (!previousMessage.has(tool.id!)) {
|
|
284
|
-
result.unshift(tool);
|
|
285
|
-
previousMessage.set(tool.id!, tool);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
result.unshift(m);
|
|
289
|
-
} else {
|
|
290
|
-
if (message.type === "tool" && message.tool_call_id) {
|
|
291
|
-
closedToolCallIds.add(message.tool_call_id);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
previousMessage.set(message.id, message);
|
|
295
|
-
result.unshift(message);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return this.attachInfoForMessage(this.composeToolMessages(result as RenderMessage[]));
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* @zh 为消息附加额外的信息,如耗时、唯一 ID 等。
|
|
303
|
-
* @en Attaches additional information to messages, such as spend time, unique ID, etc.
|
|
304
|
-
*/
|
|
305
|
-
private attachInfoForMessage(result: RenderMessage[]) {
|
|
306
|
-
let lastMessage: RenderMessage | null = null;
|
|
307
|
-
for (const message of result) {
|
|
308
|
-
const createTime = message.response_metadata?.create_time || "";
|
|
309
|
-
// 工具必须要使用 tool_call_id 来保证一致性
|
|
310
|
-
message.unique_id = message.tool_call_id! || message.id!;
|
|
311
|
-
|
|
312
|
-
message.spend_time = new Date(createTime).getTime() - new Date(lastMessage?.response_metadata?.create_time || createTime).getTime();
|
|
313
|
-
if (!message.usage_metadata && (message as AIMessage).response_metadata?.usage) {
|
|
314
|
-
const usage = (message as AIMessage).response_metadata!.usage as {
|
|
315
|
-
prompt_tokens: number;
|
|
316
|
-
completion_tokens: number;
|
|
317
|
-
total_tokens: number;
|
|
318
|
-
};
|
|
319
|
-
message.usage_metadata = {
|
|
320
|
-
...usage,
|
|
321
|
-
input_tokens: usage.prompt_tokens,
|
|
322
|
-
output_tokens: usage.completion_tokens,
|
|
323
|
-
total_tokens: usage.total_tokens,
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
lastMessage = message;
|
|
327
|
-
}
|
|
328
|
-
return result;
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* @zh 组合工具消息,将 AI 的工具调用和工具的执行结果关联起来。
|
|
332
|
-
* @en Composes tool messages, associating AI tool calls with tool execution results.
|
|
333
|
-
*/
|
|
334
|
-
private composeToolMessages(messages: RenderMessage[]): RenderMessage[] {
|
|
335
|
-
const result: RenderMessage[] = [];
|
|
336
|
-
const assistantToolMessages = new Map<string, { args: string }>();
|
|
337
|
-
const toolParentMessage = new Map<string, RenderMessage>();
|
|
338
|
-
for (const message of messages) {
|
|
339
|
-
if (StreamingMessageType.isToolAssistant(message)) {
|
|
340
|
-
/** @ts-ignore 只有 tool_call_chunks 的 args 才是文本 */
|
|
341
|
-
(message.tool_calls || message.tool_call_chunks)?.forEach((element) => {
|
|
342
|
-
assistantToolMessages.set(element.id!, element);
|
|
343
|
-
toolParentMessage.set(element.id!, message);
|
|
344
|
-
});
|
|
345
|
-
if (!message.content) continue;
|
|
346
|
-
}
|
|
347
|
-
if (StreamingMessageType.isTool(message) && !message.tool_input) {
|
|
348
|
-
const assistantToolMessage = assistantToolMessages.get(message.tool_call_id!);
|
|
349
|
-
const parentMessage = toolParentMessage.get(message.tool_call_id!);
|
|
350
|
-
if (assistantToolMessage) {
|
|
351
|
-
message.tool_input = typeof assistantToolMessage.args !== "string" ? JSON.stringify(assistantToolMessage.args) : assistantToolMessage.args;
|
|
352
|
-
if (message.additional_kwargs) {
|
|
353
|
-
message.additional_kwargs.done = true;
|
|
354
|
-
message.done = true;
|
|
355
|
-
} else {
|
|
356
|
-
message.done = true;
|
|
357
|
-
message.additional_kwargs = {
|
|
358
|
-
done: true,
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
if (parentMessage) {
|
|
363
|
-
message.usage_metadata = parentMessage.usage_metadata;
|
|
364
|
-
message.node_name = parentMessage.name;
|
|
365
|
-
// 修补特殊情况下,tool name 丢失的问题
|
|
366
|
-
if (!message.name) {
|
|
367
|
-
message.name = (parentMessage as AIMessage).tool_calls!.find((i) => i.id === message.tool_call_id)?.name;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
result.push(message);
|
|
372
|
-
}
|
|
373
|
-
return result;
|
|
232
|
+
return this.messageProcessor.renderMessages(this.graphState, () => this.getGraphNodeNow());
|
|
374
233
|
}
|
|
375
234
|
/**
|
|
376
235
|
* @zh 获取 Token 计数器信息。
|
|
377
236
|
* @en Gets the Token counter information.
|
|
378
237
|
*/
|
|
379
238
|
get tokenCounter() {
|
|
380
|
-
return this.
|
|
239
|
+
return this.messageProcessor.getGraphMessages().reduce(
|
|
381
240
|
(acc, message) => {
|
|
382
241
|
if (message.usage_metadata) {
|
|
383
242
|
acc.total_tokens += message.usage_metadata?.total_tokens || 0;
|
|
@@ -404,20 +263,6 @@ export class LangGraphClient extends Client {
|
|
|
404
263
|
);
|
|
405
264
|
}
|
|
406
265
|
|
|
407
|
-
/**
|
|
408
|
-
* @zh 注册流式更新的回调函数。
|
|
409
|
-
* @en Registers a callback function for streaming updates.
|
|
410
|
-
*/
|
|
411
|
-
onStreamingUpdate(callback: StreamingUpdateCallback) {
|
|
412
|
-
this.streamingCallbacks.add(callback);
|
|
413
|
-
return () => {
|
|
414
|
-
this.streamingCallbacks.delete(callback);
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
private emitStreamingUpdate(event: StreamingUpdateEvent) {
|
|
419
|
-
this.streamingCallbacks.forEach((callback) => callback(event));
|
|
420
|
-
}
|
|
421
266
|
/** 前端工具人机交互时,锁住面板 */
|
|
422
267
|
isFELocking(messages: RenderMessage[]) {
|
|
423
268
|
const lastMessage = messages[messages.length - 1];
|
|
@@ -434,8 +279,8 @@ export class LangGraphClient extends Client {
|
|
|
434
279
|
* @en Cancels the current Run.
|
|
435
280
|
*/
|
|
436
281
|
cancelRun() {
|
|
437
|
-
if (this.currentThread?.thread_id && this.currentRun?.run_id) {
|
|
438
|
-
this.runs.cancel(this.currentThread!.thread_id, this.currentRun.run_id);
|
|
282
|
+
if ((this.currentThread as any)?.thread_id && this.currentRun?.run_id) {
|
|
283
|
+
this.runs.cancel((this.currentThread as any)!.thread_id, this.currentRun.run_id);
|
|
439
284
|
}
|
|
440
285
|
}
|
|
441
286
|
/**
|
|
@@ -448,13 +293,10 @@ export class LangGraphClient extends Client {
|
|
|
448
293
|
}
|
|
449
294
|
if (!this.currentThread) {
|
|
450
295
|
await this.createThread();
|
|
451
|
-
this.
|
|
452
|
-
|
|
296
|
+
this.emit("thread", {
|
|
297
|
+
event: "thread/create",
|
|
453
298
|
data: {
|
|
454
|
-
|
|
455
|
-
data: {
|
|
456
|
-
thread: this.currentThread,
|
|
457
|
-
},
|
|
299
|
+
thread: this.currentThread,
|
|
458
300
|
},
|
|
459
301
|
});
|
|
460
302
|
}
|
|
@@ -491,43 +333,31 @@ export class LangGraphClient extends Client {
|
|
|
491
333
|
const streamResponse = await createStreamResponse();
|
|
492
334
|
|
|
493
335
|
const streamRecord: any[] = [];
|
|
494
|
-
this.
|
|
495
|
-
|
|
496
|
-
data: {
|
|
497
|
-
event: "start",
|
|
498
|
-
},
|
|
336
|
+
this.emit("start", {
|
|
337
|
+
event: "start",
|
|
499
338
|
});
|
|
500
339
|
for await (const chunk of streamResponse) {
|
|
501
340
|
streamRecord.push(chunk);
|
|
502
341
|
if (chunk.event === "metadata") {
|
|
503
342
|
this.currentRun = chunk.data;
|
|
504
343
|
} else if (chunk.event === "error") {
|
|
505
|
-
this.
|
|
506
|
-
type: "error",
|
|
507
|
-
data: chunk,
|
|
508
|
-
});
|
|
344
|
+
this.emit("error", chunk);
|
|
509
345
|
} else if (chunk.event === "messages/partial") {
|
|
510
346
|
for (const message of chunk.data) {
|
|
511
|
-
this.updateStreamingMessage(message);
|
|
347
|
+
this.messageProcessor.updateStreamingMessage(message);
|
|
512
348
|
}
|
|
513
|
-
this.
|
|
514
|
-
type: "message",
|
|
515
|
-
data: chunk,
|
|
516
|
-
});
|
|
349
|
+
this.emit("message", chunk);
|
|
517
350
|
continue;
|
|
518
351
|
} else if (chunk.event === "values") {
|
|
519
352
|
const data = chunk.data as { messages: Message[] };
|
|
520
353
|
|
|
521
354
|
if (data.messages) {
|
|
522
355
|
const isResume = !!command?.resume;
|
|
523
|
-
const isLongerThanLocal = data.messages.length >= this.
|
|
356
|
+
const isLongerThanLocal = data.messages.length >= this.messageProcessor.getGraphMessages().length;
|
|
524
357
|
// resume 情况下,长度低于前端 message 的统统不接受
|
|
525
358
|
if (!isResume || (isResume && isLongerThanLocal)) {
|
|
526
|
-
this.
|
|
527
|
-
this.
|
|
528
|
-
type: "value",
|
|
529
|
-
data: chunk,
|
|
530
|
-
});
|
|
359
|
+
this.messageProcessor.setGraphMessages(data.messages as RenderMessage[]);
|
|
360
|
+
this.emit("value", chunk);
|
|
531
361
|
}
|
|
532
362
|
this.graphState = chunk.data;
|
|
533
363
|
}
|
|
@@ -535,20 +365,17 @@ export class LangGraphClient extends Client {
|
|
|
535
365
|
} else if (chunk.event.startsWith("values|")) {
|
|
536
366
|
// 这个 values 必然是子 values
|
|
537
367
|
if (chunk.data?.messages) {
|
|
538
|
-
this.mergeSubGraphMessagesToStreamingMessages(chunk.data.messages);
|
|
368
|
+
this.messageProcessor.mergeSubGraphMessagesToStreamingMessages(chunk.data.messages);
|
|
539
369
|
}
|
|
540
370
|
this.graphPosition = chunk.event.split("|")[1];
|
|
541
371
|
}
|
|
542
372
|
}
|
|
543
373
|
const data = await this.runFETool();
|
|
544
374
|
if (data) streamRecord.push(...data);
|
|
545
|
-
this.
|
|
546
|
-
|
|
547
|
-
data: {
|
|
548
|
-
event: "done",
|
|
549
|
-
},
|
|
375
|
+
this.emit("done", {
|
|
376
|
+
event: "done",
|
|
550
377
|
});
|
|
551
|
-
this.
|
|
378
|
+
this.messageProcessor.clearStreamingMessages();
|
|
552
379
|
return streamRecord;
|
|
553
380
|
}
|
|
554
381
|
/** 当前子图位置,但是依赖 stream,不太适合稳定使用*/
|
|
@@ -566,26 +393,9 @@ export class LangGraphClient extends Client {
|
|
|
566
393
|
const position = this.getGraphPosition();
|
|
567
394
|
return position[position.length - 1];
|
|
568
395
|
}
|
|
569
|
-
/** 子图的数据需要通过 merge 的方式重新进行合并更新 */
|
|
570
|
-
private mergeSubGraphMessagesToStreamingMessages(messages: Message[]) {
|
|
571
|
-
const map = new Map(messages.filter((i) => i.id).map((i) => [i.id!, i]));
|
|
572
|
-
this.streamingMessage.forEach((i) => {
|
|
573
|
-
if (map.has(i.id!)) {
|
|
574
|
-
const newValue = map.get(i.id!)!;
|
|
575
|
-
Object.assign(i, newValue);
|
|
576
|
-
map.delete(i.id!);
|
|
577
|
-
}
|
|
578
|
-
});
|
|
579
|
-
// 剩余的 message 一定不在 streamMessage 中
|
|
580
|
-
map.forEach((i) => {
|
|
581
|
-
if (i.type === "tool" && i.tool_call_id) {
|
|
582
|
-
this.streamingMessage.push(i as RenderMessage);
|
|
583
|
-
}
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
396
|
|
|
587
397
|
private runFETool() {
|
|
588
|
-
const data = this.
|
|
398
|
+
const data = this.messageProcessor.getStreamingMessages(); // 需要保证不被清理
|
|
589
399
|
const lastMessage = data[data.length - 1];
|
|
590
400
|
if (!lastMessage) return;
|
|
591
401
|
// 如果最后一条消息是前端工具消息,则调用工具
|
|
@@ -663,14 +473,14 @@ export class LangGraphClient extends Client {
|
|
|
663
473
|
await this.initAssistant(this.currentAssistant?.graph_id!);
|
|
664
474
|
this.currentThread = null;
|
|
665
475
|
this.graphState = {};
|
|
666
|
-
this.
|
|
667
|
-
this.
|
|
476
|
+
this.messageProcessor.setGraphMessages([]);
|
|
477
|
+
this.messageProcessor.clearStreamingMessages();
|
|
668
478
|
this.currentRun = undefined;
|
|
669
479
|
this.tools.clearWaiting();
|
|
670
|
-
this.
|
|
671
|
-
|
|
480
|
+
this.emit("value", {
|
|
481
|
+
event: "messages/partial",
|
|
672
482
|
data: {
|
|
673
|
-
|
|
483
|
+
messages: [],
|
|
674
484
|
},
|
|
675
485
|
});
|
|
676
486
|
}
|