@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
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { Message, AIMessage, ToolMessage } from "@langchain/langgraph-sdk";
|
|
2
|
+
import { RenderMessage } from "./LangGraphClient.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @zh StreamingMessageType 类用于判断消息的类型。
|
|
6
|
+
* @en The StreamingMessageType class is used to determine the type of a message.
|
|
7
|
+
*/
|
|
8
|
+
export class StreamingMessageType {
|
|
9
|
+
static isTool(m: Message): m is ToolMessage {
|
|
10
|
+
return m.type === "tool";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static isToolAssistant(m: Message): m is AIMessage {
|
|
14
|
+
/** @ts-ignore */
|
|
15
|
+
return m.type === "ai" && (m.tool_calls?.length || m.tool_call_chunks?.length);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @zh MessageProcessor 类用于统一处理 Message 相关的逻辑,避免重复处理。
|
|
21
|
+
* @en The MessageProcessor class is used to uniformly handle Message-related logic and avoid duplicate processing.
|
|
22
|
+
*/
|
|
23
|
+
export class MessageProcessor {
|
|
24
|
+
private subAgentsKey: string;
|
|
25
|
+
/** 流式消息缓存 */
|
|
26
|
+
private streamingMessage: RenderMessage[] = [];
|
|
27
|
+
/** 图发过来的更新信息 */
|
|
28
|
+
private graphMessages: RenderMessage[] = [];
|
|
29
|
+
|
|
30
|
+
constructor(subAgentsKey: string = "task_store") {
|
|
31
|
+
this.subAgentsKey = subAgentsKey;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @zh 获取流式消息
|
|
36
|
+
* @en Get streaming messages
|
|
37
|
+
*/
|
|
38
|
+
getStreamingMessages(): RenderMessage[] {
|
|
39
|
+
return [...this.streamingMessage];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @zh 设置流式消息
|
|
44
|
+
* @en Set streaming messages
|
|
45
|
+
*/
|
|
46
|
+
setStreamingMessages(messages: RenderMessage[]): void {
|
|
47
|
+
this.streamingMessage = messages;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @zh 清空流式消息
|
|
52
|
+
* @en Clear streaming messages
|
|
53
|
+
*/
|
|
54
|
+
clearStreamingMessages(): void {
|
|
55
|
+
this.streamingMessage = [];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @zh 获取图消息
|
|
60
|
+
* @en Get graph messages
|
|
61
|
+
*/
|
|
62
|
+
getGraphMessages(): RenderMessage[] {
|
|
63
|
+
return [...this.graphMessages];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @zh 设置图消息
|
|
68
|
+
* @en Set graph messages
|
|
69
|
+
*/
|
|
70
|
+
setGraphMessages(messages: RenderMessage[]): void {
|
|
71
|
+
this.graphMessages = messages;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @zh 更新流式消息
|
|
76
|
+
* @en Update streaming message
|
|
77
|
+
*/
|
|
78
|
+
updateStreamingMessage(message: RenderMessage): void {
|
|
79
|
+
const lastMessage = this.streamingMessage[this.streamingMessage.length - 1];
|
|
80
|
+
if (!lastMessage?.id || message.id !== lastMessage.id) {
|
|
81
|
+
this.streamingMessage.push(message);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
this.streamingMessage[this.streamingMessage.length - 1] = message;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @zh 将 graphMessages 和 streamingMessage 合并,并返回新的消息数组
|
|
89
|
+
* @en Combine graphMessages and streamingMessage and return a new message array
|
|
90
|
+
*/
|
|
91
|
+
combineGraphMessagesWithStreamingMessages(): RenderMessage[] {
|
|
92
|
+
const idMap = new Map<string, RenderMessage>(this.streamingMessage.map((i) => [i.id!, i]));
|
|
93
|
+
return [
|
|
94
|
+
...this.graphMessages.map((i) => {
|
|
95
|
+
if (idMap.has(i.id!)) {
|
|
96
|
+
const newValue = idMap.get(i.id!)!;
|
|
97
|
+
idMap.delete(i.id!);
|
|
98
|
+
return newValue;
|
|
99
|
+
}
|
|
100
|
+
return i;
|
|
101
|
+
}),
|
|
102
|
+
...idMap.values(),
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @zh 子图的数据需要通过 merge 的方式重新进行合并更新
|
|
108
|
+
* @en Subgraph data needs to be merged and updated through merge method
|
|
109
|
+
*/
|
|
110
|
+
mergeSubGraphMessagesToStreamingMessages(messages: Message[]): void {
|
|
111
|
+
const map = new Map(messages.filter((i) => i.id).map((i) => [i.id!, i]));
|
|
112
|
+
this.streamingMessage.forEach((i) => {
|
|
113
|
+
if (map.has(i.id!)) {
|
|
114
|
+
const newValue = map.get(i.id!)!;
|
|
115
|
+
Object.assign(i, newValue);
|
|
116
|
+
map.delete(i.id!);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// 剩余的 message 一定不在 streamMessage 中
|
|
120
|
+
map.forEach((i) => {
|
|
121
|
+
if (i.type === "tool" && i.tool_call_id) {
|
|
122
|
+
this.streamingMessage.push(i as RenderMessage);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @zh 克隆消息对象
|
|
129
|
+
* @en Clone message object
|
|
130
|
+
*/
|
|
131
|
+
cloneMessage(message: Message): Message {
|
|
132
|
+
return JSON.parse(JSON.stringify(message));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @zh 为消息附加额外的信息,如耗时、唯一 ID 等。
|
|
137
|
+
* @en Attaches additional information to messages, such as spend time, unique ID, etc.
|
|
138
|
+
*/
|
|
139
|
+
attachInfoForMessage(messages: RenderMessage[]): RenderMessage[] {
|
|
140
|
+
let lastMessage: RenderMessage | null = null;
|
|
141
|
+
const result = [...messages]; // 创建副本避免修改原数组
|
|
142
|
+
|
|
143
|
+
for (const message of result) {
|
|
144
|
+
const createTime = message.response_metadata?.create_time || "";
|
|
145
|
+
// 工具必须要使用 tool_call_id 来保证一致性
|
|
146
|
+
message.unique_id = message.tool_call_id! || message.id!;
|
|
147
|
+
|
|
148
|
+
message.spend_time = new Date(createTime).getTime() - new Date(lastMessage?.response_metadata?.create_time || createTime).getTime();
|
|
149
|
+
if (!message.usage_metadata && (message as AIMessage).response_metadata?.usage) {
|
|
150
|
+
const usage = (message as AIMessage).response_metadata!.usage as {
|
|
151
|
+
prompt_tokens: number;
|
|
152
|
+
completion_tokens: number;
|
|
153
|
+
total_tokens: number;
|
|
154
|
+
};
|
|
155
|
+
message.usage_metadata = {
|
|
156
|
+
...usage,
|
|
157
|
+
input_tokens: usage.prompt_tokens,
|
|
158
|
+
output_tokens: usage.completion_tokens,
|
|
159
|
+
total_tokens: usage.total_tokens,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
lastMessage = message;
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @zh 组合工具消息,将 AI 的工具调用和工具的执行结果关联起来。
|
|
169
|
+
* @en Composes tool messages, associating AI tool calls with tool execution results.
|
|
170
|
+
*/
|
|
171
|
+
composeToolMessages(messages: RenderMessage[]): RenderMessage[] {
|
|
172
|
+
const result: RenderMessage[] = [];
|
|
173
|
+
const assistantToolMessages = new Map<string, { args: string }>();
|
|
174
|
+
const toolParentMessage = new Map<string, RenderMessage>();
|
|
175
|
+
|
|
176
|
+
for (const message of messages) {
|
|
177
|
+
if (StreamingMessageType.isToolAssistant(message)) {
|
|
178
|
+
/** @ts-ignore 只有 tool_call_chunks 的 args 才是文本 */
|
|
179
|
+
(message.tool_calls || message.tool_call_chunks)?.forEach((element) => {
|
|
180
|
+
assistantToolMessages.set(element.id!, element);
|
|
181
|
+
toolParentMessage.set(element.id!, message);
|
|
182
|
+
});
|
|
183
|
+
if (!message.content) continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (StreamingMessageType.isTool(message) && !message.tool_input) {
|
|
187
|
+
const assistantToolMessage = assistantToolMessages.get(message.tool_call_id!);
|
|
188
|
+
const parentMessage = toolParentMessage.get(message.tool_call_id!);
|
|
189
|
+
if (assistantToolMessage) {
|
|
190
|
+
message.tool_input = typeof assistantToolMessage.args !== "string" ? JSON.stringify(assistantToolMessage.args) : assistantToolMessage.args;
|
|
191
|
+
if (message.additional_kwargs) {
|
|
192
|
+
message.additional_kwargs.done = true;
|
|
193
|
+
message.done = true;
|
|
194
|
+
} else {
|
|
195
|
+
message.done = true;
|
|
196
|
+
message.additional_kwargs = {
|
|
197
|
+
done: true,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (parentMessage) {
|
|
202
|
+
message.usage_metadata = parentMessage.usage_metadata;
|
|
203
|
+
message.node_name = parentMessage.name;
|
|
204
|
+
// 修补特殊情况下,tool name 丢失的问题
|
|
205
|
+
if (!message.name) {
|
|
206
|
+
message.name = (parentMessage as AIMessage).tool_calls!.find((i) => i.id === message.tool_call_id)?.name;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
result.push(message);
|
|
211
|
+
}
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* @zh 转换 subAgent 消息为工具的子消息
|
|
217
|
+
* @en Convert subAgent messages to tool sub-messages
|
|
218
|
+
*/
|
|
219
|
+
convertSubAgentMessages(messages: RenderMessage[], graphState: any): RenderMessage[] {
|
|
220
|
+
const origin_task_store = graphState[this.subAgentsKey];
|
|
221
|
+
if (!origin_task_store) return messages;
|
|
222
|
+
|
|
223
|
+
const task_store = JSON.parse(JSON.stringify(origin_task_store));
|
|
224
|
+
|
|
225
|
+
/** 获取 subAgent 消息的 id,用于流式过程中对数据进行标记 */
|
|
226
|
+
messages
|
|
227
|
+
.filter((i) => {
|
|
228
|
+
return i.node_name?.startsWith("subagent_");
|
|
229
|
+
})
|
|
230
|
+
.forEach((i) => {
|
|
231
|
+
const tool_call_id = i.node_name!.replace("subagent_", "");
|
|
232
|
+
const store = task_store[tool_call_id];
|
|
233
|
+
if (store) {
|
|
234
|
+
// 根据 id 进行去重
|
|
235
|
+
const exists = (store.messages as RenderMessage[]).some((msg) => msg.id === i.id);
|
|
236
|
+
if (!exists) {
|
|
237
|
+
(store.messages as RenderMessage[]).push(i);
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
task_store[tool_call_id] = {
|
|
241
|
+
messages: [i],
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const ignoreIds = new Set<string>();
|
|
247
|
+
Object.values(task_store).forEach((task: any) => {
|
|
248
|
+
task.messages.forEach((message: RenderMessage) => {
|
|
249
|
+
ignoreIds.add(message.id!);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const result: RenderMessage[] = [];
|
|
254
|
+
for (const message of messages) {
|
|
255
|
+
if (message.type === "tool" && message.tool_call_id) {
|
|
256
|
+
const task = task_store[message.tool_call_id];
|
|
257
|
+
if (task) {
|
|
258
|
+
// 递归处理子消息,但避免重复处理
|
|
259
|
+
message.sub_agent_messages = this.processMessages(task.messages);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (message.id && ignoreIds.has(message.id)) continue;
|
|
263
|
+
result.push(message);
|
|
264
|
+
}
|
|
265
|
+
return result;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* @zh 生成用于 UI 中的流式渲染的消息
|
|
270
|
+
* @en Generate messages used for streaming rendering in the UI
|
|
271
|
+
*/
|
|
272
|
+
renderMessages(graphState: any, getGraphNodeNow: () => { name: string }): RenderMessage[] {
|
|
273
|
+
const previousMessage = new Map<string, Message>();
|
|
274
|
+
const closedToolCallIds = new Set<string>();
|
|
275
|
+
const result: Message[] = [];
|
|
276
|
+
const inputMessages = this.combineGraphMessagesWithStreamingMessages();
|
|
277
|
+
|
|
278
|
+
// 从后往前遍历,这样可以保证最新的消息在前面
|
|
279
|
+
for (let i = inputMessages.length - 1; i >= 0; i--) {
|
|
280
|
+
const message = this.cloneMessage(inputMessages[i]);
|
|
281
|
+
|
|
282
|
+
if (!message.id) {
|
|
283
|
+
result.unshift(message);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (message.type === "ai") {
|
|
287
|
+
/** @ts-ignore */
|
|
288
|
+
if (!message.name) message.name = getGraphNodeNow().name;
|
|
289
|
+
}
|
|
290
|
+
if (StreamingMessageType.isToolAssistant(message)) {
|
|
291
|
+
const m = message;
|
|
292
|
+
// 记录这个 id 的消息,并添加到结果中
|
|
293
|
+
previousMessage.set(message.id, m);
|
|
294
|
+
|
|
295
|
+
/** @ts-ignore */
|
|
296
|
+
const tool_calls: NonNullable<AIMessage["tool_calls"]> = (m as AIMessage).tool_calls?.length ? (m as AIMessage).tool_calls : (m as RenderMessage).tool_call_chunks;
|
|
297
|
+
const new_tool_calls = tool_calls
|
|
298
|
+
.filter((i) => {
|
|
299
|
+
return !closedToolCallIds.has(i.id!);
|
|
300
|
+
})!
|
|
301
|
+
.map((tool, index) => {
|
|
302
|
+
return {
|
|
303
|
+
type: "tool",
|
|
304
|
+
additional_kwargs: {},
|
|
305
|
+
/** @ts-ignore */
|
|
306
|
+
tool_input: m.additional_kwargs?.tool_calls?.[index]?.function?.arguments,
|
|
307
|
+
id: tool.id,
|
|
308
|
+
name: tool.name,
|
|
309
|
+
response_metadata: {},
|
|
310
|
+
tool_call_id: tool.id!,
|
|
311
|
+
content: "",
|
|
312
|
+
} as ToolMessage;
|
|
313
|
+
});
|
|
314
|
+
for (const tool of new_tool_calls) {
|
|
315
|
+
if (!previousMessage.has(tool.id!)) {
|
|
316
|
+
result.unshift(tool);
|
|
317
|
+
previousMessage.set(tool.id!, tool);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
result.unshift(m);
|
|
321
|
+
} else {
|
|
322
|
+
if (message.type === "tool" && message.tool_call_id) {
|
|
323
|
+
closedToolCallIds.add(message.tool_call_id);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
previousMessage.set(message.id, message);
|
|
327
|
+
result.unshift(message);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return this.processMessages(result as RenderMessage[], graphState);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* @zh 统一的消息处理入口,按顺序执行所有处理步骤
|
|
336
|
+
* @en Unified message processing entry point, executing all processing steps in order
|
|
337
|
+
*/
|
|
338
|
+
processMessages(messages: RenderMessage[], graphState?: any): RenderMessage[] {
|
|
339
|
+
// 1. 组合工具消息
|
|
340
|
+
const composedMessages = this.composeToolMessages(messages);
|
|
341
|
+
|
|
342
|
+
// 2. 附加信息
|
|
343
|
+
const messagesWithInfo = this.attachInfoForMessage(composedMessages);
|
|
344
|
+
|
|
345
|
+
// 3. 转换子代理消息(如果提供了 graphState)
|
|
346
|
+
if (graphState) {
|
|
347
|
+
return this.convertSubAgentMessages(messagesWithInfo, graphState);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return messagesWithInfo;
|
|
351
|
+
}
|
|
352
|
+
}
|
package/src/TestKit.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RenderMessage } from "./LangGraphClient.js";
|
|
2
|
-
import { Message } from "@langchain/langgraph-sdk";
|
|
2
|
+
import type { Message } from "@langchain/langgraph-sdk";
|
|
3
3
|
import { CallToolResult, UnionTool } from "./tool/createTool.js";
|
|
4
4
|
import { ToolRenderData } from "./tool/ToolUI.js";
|
|
5
5
|
import { createChatStore } from "./ui-store/createChatStore.js";
|
package/src/index.ts
CHANGED
package/src/types.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Thread,
|
|
3
|
+
Assistant,
|
|
4
|
+
Run,
|
|
5
|
+
StreamMode,
|
|
6
|
+
Command,
|
|
7
|
+
Metadata,
|
|
8
|
+
AssistantGraph,
|
|
9
|
+
OnConflictBehavior,
|
|
10
|
+
ThreadStatus,
|
|
11
|
+
ValuesStreamEvent,
|
|
12
|
+
UpdatesStreamEvent,
|
|
13
|
+
DebugStreamEvent,
|
|
14
|
+
MessagesStreamEvent,
|
|
15
|
+
MessagesTupleStreamEvent,
|
|
16
|
+
CustomStreamEvent,
|
|
17
|
+
EventsStreamEvent,
|
|
18
|
+
ErrorStreamEvent,
|
|
19
|
+
MetadataStreamEvent,
|
|
20
|
+
FeedbackStreamEvent,
|
|
21
|
+
Config,
|
|
22
|
+
Checkpoint,
|
|
23
|
+
} from "@langchain/langgraph-sdk";
|
|
24
|
+
import { StreamEvent } from "@langchain/core/tracers/log_stream";
|
|
25
|
+
|
|
26
|
+
// 基础类型定义
|
|
27
|
+
export type AssistantSortBy = "assistant_id" | "graph_id" | "name" | "created_at" | "updated_at";
|
|
28
|
+
export type ThreadSortBy = "thread_id" | "status" | "created_at" | "updated_at";
|
|
29
|
+
export type SortOrder = "asc" | "desc";
|
|
30
|
+
export type RunStatus = "pending" | "running" | "error" | "success" | "timeout" | "interrupted";
|
|
31
|
+
export type MultitaskStrategy = "reject" | "interrupt" | "rollback" | "enqueue";
|
|
32
|
+
export type DisconnectMode = "cancel" | "continue";
|
|
33
|
+
export type OnCompletionBehavior = "complete" | "continue";
|
|
34
|
+
export type CancelAction = "interrupt" | "rollback";
|
|
35
|
+
|
|
36
|
+
// 流式异步生成器类型
|
|
37
|
+
export type TypedAsyncGenerator<
|
|
38
|
+
TStreamMode extends StreamMode | StreamMode[] = [],
|
|
39
|
+
TSubgraphs extends boolean = false,
|
|
40
|
+
TStateType = unknown,
|
|
41
|
+
TUpdateType = TStateType,
|
|
42
|
+
TCustomType = unknown,
|
|
43
|
+
> = AsyncGenerator<
|
|
44
|
+
| {
|
|
45
|
+
values: ValuesStreamEvent<TStateType>;
|
|
46
|
+
updates: UpdatesStreamEvent<TUpdateType>;
|
|
47
|
+
custom: CustomStreamEvent<TCustomType>;
|
|
48
|
+
debug: DebugStreamEvent;
|
|
49
|
+
messages: MessagesStreamEvent;
|
|
50
|
+
"messages-tuple": MessagesTupleStreamEvent;
|
|
51
|
+
events: EventsStreamEvent;
|
|
52
|
+
}[TStreamMode extends StreamMode[] ? TStreamMode[number] : TStreamMode]
|
|
53
|
+
| ErrorStreamEvent
|
|
54
|
+
| MetadataStreamEvent
|
|
55
|
+
| FeedbackStreamEvent
|
|
56
|
+
>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 兼容 LangGraph SDK 的接口定义,方便进行无侵入式的扩展
|
|
60
|
+
*/
|
|
61
|
+
export interface ILangGraphClient<TStateType = unknown, TUpdateType = TStateType> {
|
|
62
|
+
assistants: {
|
|
63
|
+
search(query?: { graphId?: string; metadata?: Metadata; limit?: number; offset?: number; sortBy?: AssistantSortBy; sortOrder?: SortOrder }): Promise<Assistant[]>;
|
|
64
|
+
getGraph(assistantId: string, options?: { xray?: boolean | number }): Promise<AssistantGraph>;
|
|
65
|
+
};
|
|
66
|
+
threads: {
|
|
67
|
+
create<ValuesType = TStateType>(payload?: {
|
|
68
|
+
metadata?: Metadata;
|
|
69
|
+
threadId?: string;
|
|
70
|
+
ifExists?: OnConflictBehavior;
|
|
71
|
+
graphId?: string;
|
|
72
|
+
supersteps?: Array<{
|
|
73
|
+
updates: Array<{
|
|
74
|
+
values: unknown;
|
|
75
|
+
command?: Command;
|
|
76
|
+
asNode: string;
|
|
77
|
+
}>;
|
|
78
|
+
}>;
|
|
79
|
+
}): Promise<Thread<ValuesType>>;
|
|
80
|
+
search<ValuesType = TStateType>(query?: {
|
|
81
|
+
metadata?: Metadata;
|
|
82
|
+
limit?: number;
|
|
83
|
+
offset?: number;
|
|
84
|
+
status?: ThreadStatus;
|
|
85
|
+
sortBy?: ThreadSortBy;
|
|
86
|
+
sortOrder?: SortOrder;
|
|
87
|
+
}): Promise<Thread<ValuesType>[]>;
|
|
88
|
+
get<ValuesType = TStateType>(threadId: string): Promise<Thread<ValuesType>>;
|
|
89
|
+
delete(threadId: string): Promise<void>;
|
|
90
|
+
};
|
|
91
|
+
runs: {
|
|
92
|
+
list(
|
|
93
|
+
threadId: string,
|
|
94
|
+
options?: {
|
|
95
|
+
limit?: number;
|
|
96
|
+
offset?: number;
|
|
97
|
+
status?: RunStatus;
|
|
98
|
+
}
|
|
99
|
+
): Promise<Run[]>;
|
|
100
|
+
stream<TStreamMode extends StreamMode | StreamMode[] = StreamMode, TSubgraphs extends boolean = false>(
|
|
101
|
+
threadId: null,
|
|
102
|
+
assistantId: string,
|
|
103
|
+
payload?: {
|
|
104
|
+
input?: Record<string, unknown> | null;
|
|
105
|
+
metadata?: Metadata;
|
|
106
|
+
config?: Config;
|
|
107
|
+
checkpointId?: string;
|
|
108
|
+
checkpoint?: Omit<Checkpoint, "thread_id">;
|
|
109
|
+
checkpointDuring?: boolean;
|
|
110
|
+
interruptBefore?: "*" | string[];
|
|
111
|
+
interruptAfter?: "*" | string[];
|
|
112
|
+
signal?: AbortController["signal"];
|
|
113
|
+
webhook?: string;
|
|
114
|
+
onDisconnect?: DisconnectMode;
|
|
115
|
+
afterSeconds?: number;
|
|
116
|
+
ifNotExists?: "create" | "reject";
|
|
117
|
+
command?: Command;
|
|
118
|
+
onRunCreated?: (params: { run_id: string; thread_id?: string }) => void;
|
|
119
|
+
streamMode?: TStreamMode;
|
|
120
|
+
streamSubgraphs?: TSubgraphs;
|
|
121
|
+
streamResumable?: boolean;
|
|
122
|
+
feedbackKeys?: string[];
|
|
123
|
+
}
|
|
124
|
+
): TypedAsyncGenerator<TStreamMode, TSubgraphs, TStateType, TUpdateType>;
|
|
125
|
+
stream<TStreamMode extends StreamMode | StreamMode[] = StreamMode, TSubgraphs extends boolean = false>(
|
|
126
|
+
threadId: string,
|
|
127
|
+
assistantId: string,
|
|
128
|
+
payload?: {
|
|
129
|
+
input?: Record<string, unknown> | null;
|
|
130
|
+
metadata?: Metadata;
|
|
131
|
+
config?: Config;
|
|
132
|
+
checkpointId?: string;
|
|
133
|
+
checkpoint?: Omit<Checkpoint, "thread_id">;
|
|
134
|
+
checkpointDuring?: boolean;
|
|
135
|
+
interruptBefore?: "*" | string[];
|
|
136
|
+
interruptAfter?: "*" | string[];
|
|
137
|
+
multitaskStrategy?: MultitaskStrategy;
|
|
138
|
+
onCompletion?: OnCompletionBehavior;
|
|
139
|
+
signal?: AbortController["signal"];
|
|
140
|
+
webhook?: string;
|
|
141
|
+
onDisconnect?: DisconnectMode;
|
|
142
|
+
afterSeconds?: number;
|
|
143
|
+
ifNotExists?: "create" | "reject";
|
|
144
|
+
command?: Command;
|
|
145
|
+
onRunCreated?: (params: { run_id: string; thread_id?: string }) => void;
|
|
146
|
+
streamMode?: TStreamMode;
|
|
147
|
+
streamSubgraphs?: TSubgraphs;
|
|
148
|
+
streamResumable?: boolean;
|
|
149
|
+
feedbackKeys?: string[];
|
|
150
|
+
}
|
|
151
|
+
): TypedAsyncGenerator<TStreamMode, TSubgraphs, TStateType, TUpdateType>;
|
|
152
|
+
joinStream(
|
|
153
|
+
threadId: string | undefined | null,
|
|
154
|
+
runId: string,
|
|
155
|
+
options?:
|
|
156
|
+
| {
|
|
157
|
+
signal?: AbortSignal;
|
|
158
|
+
cancelOnDisconnect?: boolean;
|
|
159
|
+
lastEventId?: string;
|
|
160
|
+
streamMode?: StreamMode | StreamMode[];
|
|
161
|
+
}
|
|
162
|
+
| AbortSignal
|
|
163
|
+
): AsyncGenerator<{ id?: string; event: StreamEvent; data: any }>;
|
|
164
|
+
cancel(threadId: string, runId: string, wait?: boolean, action?: CancelAction): Promise<void>;
|
|
165
|
+
};
|
|
166
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { atom, computed } from "nanostores";
|
|
2
2
|
import { LangGraphClient, LangGraphClientConfig, RenderMessage, SendMessageOptions } from "../LangGraphClient.js";
|
|
3
|
-
import { AssistantGraph, Message, Thread } from "@langchain/langgraph-sdk";
|
|
3
|
+
import { AssistantGraph, Client, Message, Thread } from "@langchain/langgraph-sdk";
|
|
4
4
|
import { debounce } from "ts-debounce";
|
|
5
5
|
import { ToolRenderData } from "../tool/ToolUI.js";
|
|
6
6
|
import { UnionTool } from "../tool/createTool.js";
|
|
7
|
+
import { createLangGraphServerClient } from "../client/LanggraphServer.js";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* @zh 格式化日期对象为时间字符串。
|
|
@@ -110,29 +111,48 @@ export const createChatStore = (
|
|
|
110
111
|
* @en Initializes the LangGraph client.
|
|
111
112
|
*/
|
|
112
113
|
async function initClient() {
|
|
113
|
-
const newClient = new LangGraphClient(
|
|
114
|
+
const newClient = new LangGraphClient({
|
|
115
|
+
...config,
|
|
116
|
+
client: config.client ?? (await createLangGraphServerClient()),
|
|
117
|
+
});
|
|
114
118
|
await newClient.initAssistant(currentAgent.get());
|
|
115
119
|
currentAgent.set(newClient.getCurrentAssistant()!.graph_id);
|
|
116
120
|
// 不再需要创建,sendMessage 会自动创建
|
|
117
121
|
// await newClient.createThread();
|
|
118
122
|
inChatError.set(null);
|
|
119
|
-
|
|
123
|
+
// 监听流开始事件
|
|
124
|
+
newClient.on("start", () => {
|
|
125
|
+
loading.set(true);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// 监听 Thread 创建和流完成事件
|
|
129
|
+
newClient.on("thread", () => {
|
|
120
130
|
currentChatId.set(newClient.getCurrentThread()?.thread_id || null);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
loading.set(false);
|
|
132
|
-
inChatError.set(event.data);
|
|
133
|
-
}
|
|
134
|
-
// console.log(newClient.renderMessage);
|
|
131
|
+
// 创建新流程时,默认为 __start__
|
|
132
|
+
currentNodeName.set("__start__");
|
|
133
|
+
// 创建新会话时,需要自动刷新历史面板
|
|
134
|
+
refreshHistoryList();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
newClient.on("done", () => {
|
|
138
|
+
loading.set(false);
|
|
139
|
+
updateUI(newClient);
|
|
140
|
+
});
|
|
135
141
|
|
|
142
|
+
// 监听错误事件
|
|
143
|
+
newClient.on("error", (event) => {
|
|
144
|
+
loading.set(false);
|
|
145
|
+
inChatError.set(event.data);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// 监听消息和值更新事件
|
|
149
|
+
newClient.on("message", () => {
|
|
150
|
+
currentChatId.set(newClient.getCurrentThread()?.thread_id || null);
|
|
151
|
+
updateUI(newClient);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
newClient.on("value", () => {
|
|
155
|
+
currentChatId.set(newClient.getCurrentThread()?.thread_id || null);
|
|
136
156
|
updateUI(newClient);
|
|
137
157
|
});
|
|
138
158
|
context.onInit?.(newClient);
|
|
@@ -196,8 +216,8 @@ export const createChatStore = (
|
|
|
196
216
|
const refreshHistoryList = async () => {
|
|
197
217
|
if (!client.get() || !showHistory.get()) return;
|
|
198
218
|
try {
|
|
199
|
-
const response = await client.get()?.listThreads
|
|
200
|
-
historyList.set(response || []);
|
|
219
|
+
const response = await client.get()?.listThreads();
|
|
220
|
+
historyList.set((response as Thread<{ messages: Message[] }>[]) || []);
|
|
201
221
|
} catch (error) {
|
|
202
222
|
console.error("Failed to fetch threads:", error);
|
|
203
223
|
}
|
|
@@ -307,7 +327,7 @@ export const createChatStore = (
|
|
|
307
327
|
* @en Deletes the specified historical chat session.
|
|
308
328
|
*/
|
|
309
329
|
async deleteHistoryChat(thread: Thread<{ messages: Message[] }>) {
|
|
310
|
-
await client.get()?.
|
|
330
|
+
await client.get()?.deleteThread(thread.thread_id);
|
|
311
331
|
await refreshHistoryList();
|
|
312
332
|
},
|
|
313
333
|
getToolUIRender,
|