@langgraph-js/sdk 3.7.0 → 3.7.1
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/README.md +29 -0
- package/dist/LangGraphClient.d.ts +13 -1
- package/dist/LangGraphClient.js +101 -77
- package/dist/MessageProcessor.js +18 -24
- package/dist/SpendTime.js +4 -9
- package/dist/TestKit.js +16 -15
- package/dist/ToolManager.js +4 -7
- package/dist/artifacts/index.js +1 -1
- package/dist/client/LanggraphServer.js +1 -1
- package/dist/client/LowJSServer.d.ts +3 -0
- package/dist/client/LowJSServer.js +80 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +2 -0
- package/dist/client/utils/sse.d.ts +8 -0
- package/dist/client/utils/sse.js +151 -0
- package/dist/client/utils/stream.d.ts +15 -0
- package/dist/client/utils/stream.js +104 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/react/ChatContext.d.ts +3 -0
- package/dist/react/ChatContext.js +8 -3
- package/dist/tool/ToolUI.js +3 -2
- package/dist/tool/createTool.js +3 -6
- package/dist/tool/utils.js +3 -4
- package/dist/ui-store/createChatStore.js +23 -39
- package/dist/vue/ChatContext.d.ts +3 -0
- package/dist/vue/ChatContext.js +3 -2
- package/package.json +3 -1
- package/src/LangGraphClient.ts +73 -45
- package/src/client/LanggraphServer.ts +1 -2
- package/src/client/LowJSServer.ts +80 -0
- package/src/client/index.ts +2 -0
- package/src/client/utils/sse.ts +176 -0
- package/src/client/utils/stream.ts +114 -0
- package/src/index.ts +1 -0
- package/src/react/ChatContext.ts +20 -15
- package/src/vue/ChatContext.ts +5 -0
- package/test/TestKit.test.ts +10 -2
- package/tsconfig.json +1 -1
|
@@ -45,9 +45,8 @@ export const getMessageContent = (content) => {
|
|
|
45
45
|
* @en Gets the text representation of Thread content in history.
|
|
46
46
|
*/
|
|
47
47
|
export const getHistoryContent = (thread) => {
|
|
48
|
-
var _a, _b, _c;
|
|
49
48
|
/** @ts-ignore */
|
|
50
|
-
const content = thread.title || thread.name ||
|
|
49
|
+
const content = thread.title || thread.name || thread?.values?.messages?.[0]?.content;
|
|
51
50
|
if (content && Array.isArray(content)) {
|
|
52
51
|
return content.map((item) => {
|
|
53
52
|
if (item.type === "text") {
|
|
@@ -67,14 +66,13 @@ export const getHistoryContent = (thread) => {
|
|
|
67
66
|
* @en Creates a state manager (store) for the chat interface.
|
|
68
67
|
*/
|
|
69
68
|
export const createChatStore = (initClientName, config, context = {}) => {
|
|
70
|
-
var _a, _b;
|
|
71
69
|
const client = atom(null);
|
|
72
70
|
const renderMessages = atom([]);
|
|
73
71
|
const userInput = atom("");
|
|
74
72
|
const loading = atom(false);
|
|
75
73
|
const collapsedTools = atom([]);
|
|
76
74
|
const inChatError = atom(null);
|
|
77
|
-
const showHistory = atom(
|
|
75
|
+
const showHistory = atom(context.showHistory ?? false);
|
|
78
76
|
const currentAgent = atom(initClientName);
|
|
79
77
|
const currentChatId = atom(null);
|
|
80
78
|
const currentNodeName = atom("__start__");
|
|
@@ -87,17 +85,16 @@ export const createChatStore = (initClientName, config, context = {}) => {
|
|
|
87
85
|
c.tools.bindTools(tools.get());
|
|
88
86
|
};
|
|
89
87
|
// 显示 langgraph 可视化图
|
|
90
|
-
const showGraph = atom(
|
|
88
|
+
const showGraph = atom(context.showGraph ?? false);
|
|
91
89
|
const graphVisualize = atom(null);
|
|
92
90
|
const refreshGraph = async () => {
|
|
93
|
-
var _a;
|
|
94
91
|
if (showGraph.get())
|
|
95
|
-
graphVisualize.set((await
|
|
92
|
+
graphVisualize.set((await client.get()?.graphVisualize()) || null);
|
|
96
93
|
};
|
|
97
94
|
const updateUI = debounce((newClient) => {
|
|
98
95
|
const messages = newClient.renderMessage;
|
|
99
96
|
const lastMessage = messages[messages.length - 1];
|
|
100
|
-
currentNodeName.set(
|
|
97
|
+
currentNodeName.set(lastMessage?.node_name || lastMessage?.name || "__start__");
|
|
101
98
|
renderMessages.set(messages);
|
|
102
99
|
}, 10);
|
|
103
100
|
/**
|
|
@@ -105,12 +102,11 @@ export const createChatStore = (initClientName, config, context = {}) => {
|
|
|
105
102
|
* @en Initializes the LangGraph client.
|
|
106
103
|
*/
|
|
107
104
|
async function initClient() {
|
|
108
|
-
var _a, _b, _c;
|
|
109
105
|
const newClient = new LangGraphClient({
|
|
110
106
|
...config,
|
|
111
|
-
client:
|
|
107
|
+
client: config.client ?? (await createLangGraphServerClient(config)),
|
|
112
108
|
});
|
|
113
|
-
await newClient.initAssistant(currentAgent.get(), { fallbackToAvailableAssistants:
|
|
109
|
+
await newClient.initAssistant(currentAgent.get(), { fallbackToAvailableAssistants: context.fallbackToAvailableAssistants ?? false });
|
|
114
110
|
currentAgent.set(newClient.getCurrentAssistant().graph_id);
|
|
115
111
|
// 不再需要创建,sendMessage 会自动创建
|
|
116
112
|
// await newClient.createThread();
|
|
@@ -121,8 +117,7 @@ export const createChatStore = (initClientName, config, context = {}) => {
|
|
|
121
117
|
});
|
|
122
118
|
// 监听 Thread 创建和流完成事件
|
|
123
119
|
newClient.on("thread", () => {
|
|
124
|
-
|
|
125
|
-
currentChatId.set(((_a = newClient.getCurrentThread()) === null || _a === void 0 ? void 0 : _a.thread_id) || null);
|
|
120
|
+
currentChatId.set(newClient.getCurrentThread()?.thread_id || null);
|
|
126
121
|
// 创建新流程时,默认为 __start__
|
|
127
122
|
currentNodeName.set("__start__");
|
|
128
123
|
// 创建新会话时,需要自动刷新历史面板
|
|
@@ -139,16 +134,14 @@ export const createChatStore = (initClientName, config, context = {}) => {
|
|
|
139
134
|
});
|
|
140
135
|
// 监听消息和值更新事件
|
|
141
136
|
newClient.on("message", () => {
|
|
142
|
-
|
|
143
|
-
currentChatId.set(((_a = newClient.getCurrentThread()) === null || _a === void 0 ? void 0 : _a.thread_id) || null);
|
|
137
|
+
currentChatId.set(newClient.getCurrentThread()?.thread_id || null);
|
|
144
138
|
updateUI(newClient);
|
|
145
139
|
});
|
|
146
140
|
newClient.on("value", () => {
|
|
147
|
-
|
|
148
|
-
currentChatId.set(((_a = newClient.getCurrentThread()) === null || _a === void 0 ? void 0 : _a.thread_id) || null);
|
|
141
|
+
currentChatId.set(newClient.getCurrentThread()?.thread_id || null);
|
|
149
142
|
updateUI(newClient);
|
|
150
143
|
});
|
|
151
|
-
|
|
144
|
+
context.onInit?.(newClient);
|
|
152
145
|
newClient.graphState = {};
|
|
153
146
|
client.set(newClient);
|
|
154
147
|
if (showGraph.get())
|
|
@@ -161,18 +154,17 @@ export const createChatStore = (initClientName, config, context = {}) => {
|
|
|
161
154
|
* @en Sends a message.
|
|
162
155
|
*/
|
|
163
156
|
const sendMessage = async (message, extraData, withoutCheck = false) => {
|
|
164
|
-
|
|
165
|
-
if ((!withoutCheck && !userInput.get().trim() && !(message === null || message === void 0 ? void 0 : message.length)) || loading.get() || !client.get())
|
|
157
|
+
if ((!withoutCheck && !userInput.get().trim() && !message?.length) || loading.get() || !client.get())
|
|
166
158
|
return;
|
|
167
159
|
loading.set(true);
|
|
168
160
|
inChatError.set(null);
|
|
169
161
|
try {
|
|
170
|
-
await
|
|
162
|
+
await client.get()?.sendMessage(message || userInput.get(), extraData);
|
|
171
163
|
}
|
|
172
164
|
catch (e) {
|
|
173
165
|
const isThreadRunning = e.message.includes("422");
|
|
174
166
|
if (isThreadRunning) {
|
|
175
|
-
await
|
|
167
|
+
await client.get()?.resetStream();
|
|
176
168
|
}
|
|
177
169
|
else {
|
|
178
170
|
throw e;
|
|
@@ -188,8 +180,7 @@ export const createChatStore = (initClientName, config, context = {}) => {
|
|
|
188
180
|
* @en Stops the current message generation.
|
|
189
181
|
*/
|
|
190
182
|
const stopGeneration = () => {
|
|
191
|
-
|
|
192
|
-
(_a = client.get()) === null || _a === void 0 ? void 0 : _a.cancelRun();
|
|
183
|
+
client.get()?.cancelRun();
|
|
193
184
|
};
|
|
194
185
|
/**
|
|
195
186
|
* @zh 切换工具消息的折叠状态。
|
|
@@ -215,11 +206,10 @@ export const createChatStore = (initClientName, config, context = {}) => {
|
|
|
215
206
|
* @en Refreshes the history list.
|
|
216
207
|
*/
|
|
217
208
|
const refreshHistoryList = async () => {
|
|
218
|
-
var _a;
|
|
219
209
|
if (!client.get() || !showHistory.get())
|
|
220
210
|
return;
|
|
221
211
|
try {
|
|
222
|
-
const response = await
|
|
212
|
+
const response = await client.get()?.listThreads();
|
|
223
213
|
historyList.set(response || []);
|
|
224
214
|
}
|
|
225
215
|
catch (error) {
|
|
@@ -235,9 +225,8 @@ export const createChatStore = (initClientName, config, context = {}) => {
|
|
|
235
225
|
historyList.set([thread, ...prev]);
|
|
236
226
|
};
|
|
237
227
|
const getToolUIRender = (tool_name) => {
|
|
238
|
-
var _a;
|
|
239
228
|
const toolsDefine = client.get().tools.getAllTools();
|
|
240
|
-
const tool =
|
|
229
|
+
const tool = toolsDefine.find((i) => i.name === tool_name)?.render;
|
|
241
230
|
return tool ? (message) => tool(new ToolRenderData(message, client.get())) : null;
|
|
242
231
|
};
|
|
243
232
|
const artifactHook = useArtifacts(renderMessages, client);
|
|
@@ -266,8 +255,7 @@ export const createChatStore = (initClientName, config, context = {}) => {
|
|
|
266
255
|
refreshTools();
|
|
267
256
|
},
|
|
268
257
|
isFELocking() {
|
|
269
|
-
|
|
270
|
-
return (_a = client.get()) === null || _a === void 0 ? void 0 : _a.isFELocking(renderMessages.get());
|
|
258
|
+
return client.get()?.isFELocking(renderMessages.get());
|
|
271
259
|
},
|
|
272
260
|
getClient() {
|
|
273
261
|
return client.get();
|
|
@@ -284,8 +272,7 @@ export const createChatStore = (initClientName, config, context = {}) => {
|
|
|
284
272
|
* @en Reverts to the specified message.
|
|
285
273
|
*/
|
|
286
274
|
async revertChatTo(messageId, resend = false, sendOptions) {
|
|
287
|
-
|
|
288
|
-
await ((_a = client.get()) === null || _a === void 0 ? void 0 : _a.revertChatTo(messageId, sendOptions || {}));
|
|
275
|
+
await client.get()?.revertChatTo(messageId, sendOptions || {});
|
|
289
276
|
if (resend) {
|
|
290
277
|
return sendMessage([], sendOptions, true);
|
|
291
278
|
}
|
|
@@ -324,8 +311,7 @@ export const createChatStore = (initClientName, config, context = {}) => {
|
|
|
324
311
|
* @en Creates a new chat session.
|
|
325
312
|
*/
|
|
326
313
|
createNewChat() {
|
|
327
|
-
|
|
328
|
-
(_a = client.get()) === null || _a === void 0 ? void 0 : _a.reset();
|
|
314
|
+
client.get()?.reset();
|
|
329
315
|
inChatError.set(null);
|
|
330
316
|
loading.set(false);
|
|
331
317
|
},
|
|
@@ -334,12 +320,11 @@ export const createChatStore = (initClientName, config, context = {}) => {
|
|
|
334
320
|
* @en Switches to the specified historical chat session.
|
|
335
321
|
*/
|
|
336
322
|
async toHistoryChat(thread) {
|
|
337
|
-
var _a, _b, _c;
|
|
338
323
|
inChatError.set(null);
|
|
339
324
|
loading.set(false);
|
|
340
|
-
const nowThread = await
|
|
325
|
+
const nowThread = await client.get()?.resetThread(thread.metadata?.graph_id, thread.thread_id);
|
|
341
326
|
if (nowThread) {
|
|
342
|
-
|
|
327
|
+
client.get()?.resetStream();
|
|
343
328
|
}
|
|
344
329
|
return nowThread;
|
|
345
330
|
},
|
|
@@ -348,8 +333,7 @@ export const createChatStore = (initClientName, config, context = {}) => {
|
|
|
348
333
|
* @en Deletes the specified historical chat session.
|
|
349
334
|
*/
|
|
350
335
|
async deleteHistoryChat(thread) {
|
|
351
|
-
|
|
352
|
-
await ((_a = client.get()) === null || _a === void 0 ? void 0 : _a.deleteThread(thread.thread_id));
|
|
336
|
+
await client.get()?.deleteThread(thread.thread_id);
|
|
353
337
|
await refreshHistoryList();
|
|
354
338
|
},
|
|
355
339
|
getToolUIRender,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type PropType, Ref } from "vue";
|
|
2
2
|
import { createChatStore } from "../ui-store/index.js";
|
|
3
3
|
import { PreinitializedWritableAtom, StoreValue } from "nanostores";
|
|
4
|
+
import { ILangGraphClient } from "@langgraph-js/pure-graph/dist/types.js";
|
|
4
5
|
/**
|
|
5
6
|
* @zh UnionStore 类型用于合并 store 的 data 和 mutations,使其可以直接访问。
|
|
6
7
|
* @en The UnionStore type is used to merge the data and mutations of a store, allowing direct access.
|
|
@@ -33,6 +34,8 @@ export interface ChatProviderProps {
|
|
|
33
34
|
showGraph?: boolean;
|
|
34
35
|
fallbackToAvailableAssistants?: boolean;
|
|
35
36
|
onInitError?: (error: any, currentAgent: string) => void;
|
|
37
|
+
client?: ILangGraphClient;
|
|
38
|
+
legacyMode?: boolean;
|
|
36
39
|
}
|
|
37
40
|
/**
|
|
38
41
|
* @zh Chat Provider Hook,用于在 setup 中直接使用
|
package/dist/vue/ChatContext.js
CHANGED
|
@@ -45,6 +45,8 @@ export const useChatProvider = (props) => {
|
|
|
45
45
|
fetch: F,
|
|
46
46
|
maxRetries: 1,
|
|
47
47
|
},
|
|
48
|
+
client: props.client,
|
|
49
|
+
legacyMode: props.legacyMode,
|
|
48
50
|
}, {
|
|
49
51
|
showHistory: props.showHistory,
|
|
50
52
|
showGraph: props.showGraph,
|
|
@@ -115,8 +117,7 @@ export const ChatProvider = defineComponent({
|
|
|
115
117
|
unionStore,
|
|
116
118
|
});
|
|
117
119
|
return () => {
|
|
118
|
-
|
|
119
|
-
return (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots);
|
|
120
|
+
return slots.default?.();
|
|
120
121
|
};
|
|
121
122
|
},
|
|
122
123
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@langgraph-js/sdk",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.1",
|
|
4
4
|
"description": "The UI SDK for LangGraph - seamlessly integrate your AI agents with frontend interfaces",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@langchain/langgraph-sdk": "^1.0.0",
|
|
48
|
+
"camelcase-keys": "^10.0.1",
|
|
49
|
+
"change-case": "^5.4.4",
|
|
48
50
|
"eventemitter3": "^5.0.1",
|
|
49
51
|
"jsonrepair": "^3.12.0",
|
|
50
52
|
"nanostores": "^1.0.1",
|
package/src/LangGraphClient.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { CallToolResult } from "./tool/createTool.js";
|
|
|
5
5
|
import { type ILangGraphClient } from "@langgraph-js/pure-graph/dist/types.js";
|
|
6
6
|
import { MessageProcessor } from "./MessageProcessor.js";
|
|
7
7
|
import { revertChatTo, RevertChatToOptions } from "./time-travel/index.js";
|
|
8
|
-
|
|
8
|
+
import camelcaseKeys from "camelcase-keys";
|
|
9
9
|
export type RenderMessage = Message & {
|
|
10
10
|
/** 对于 AIMessage 来说是节点名称,对于工具节点来说是工具名称 */
|
|
11
11
|
name?: string;
|
|
@@ -85,6 +85,8 @@ export interface LangGraphClientConfig {
|
|
|
85
85
|
defaultHeaders?: Record<string, string | null | undefined>;
|
|
86
86
|
/** 自定义客户端实现,如果不提供则使用官方 Client */
|
|
87
87
|
client: ILangGraphClient<any>;
|
|
88
|
+
/** 是否使用 legacy 模式,默认 false */
|
|
89
|
+
legacyMode?: boolean;
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
// 定义事件数据类型
|
|
@@ -118,10 +120,11 @@ export class LangGraphClient<TStateType = unknown> extends EventEmitter<LangGrap
|
|
|
118
120
|
stopController: AbortController | null = null;
|
|
119
121
|
/** Message 处理器 */
|
|
120
122
|
private messageProcessor: MessageProcessor;
|
|
121
|
-
|
|
123
|
+
private legacyMode: boolean;
|
|
122
124
|
constructor(config: LangGraphClientConfig) {
|
|
123
125
|
super();
|
|
124
126
|
this.client = config.client;
|
|
127
|
+
this.legacyMode = config.legacyMode ?? false;
|
|
125
128
|
this.messageProcessor = new MessageProcessor();
|
|
126
129
|
}
|
|
127
130
|
|
|
@@ -345,12 +348,26 @@ export class LangGraphClient<TStateType = unknown> extends EventEmitter<LangGrap
|
|
|
345
348
|
content: input,
|
|
346
349
|
} as HumanMessage,
|
|
347
350
|
];
|
|
351
|
+
|
|
352
|
+
const streamRecord: any[] = [];
|
|
353
|
+
this.emit("start", {
|
|
354
|
+
event: "start",
|
|
355
|
+
});
|
|
348
356
|
const createStreamResponse = async () => {
|
|
349
357
|
if (_debug?.streamResponse) {
|
|
350
358
|
return _debug.streamResponse;
|
|
351
359
|
}
|
|
360
|
+
const onCallback = this.legacyMode
|
|
361
|
+
? (chunk: any) => {
|
|
362
|
+
streamRecord.push(chunk);
|
|
363
|
+
this.processStreamChunk(chunk, command);
|
|
364
|
+
}
|
|
365
|
+
: undefined;
|
|
352
366
|
if (joinRunId) {
|
|
353
|
-
return this.runs.joinStream(this.currentThread!.thread_id, joinRunId
|
|
367
|
+
return this.runs.joinStream(this.currentThread!.thread_id, joinRunId, {
|
|
368
|
+
/** @ts-ignore */
|
|
369
|
+
onCallback,
|
|
370
|
+
});
|
|
354
371
|
}
|
|
355
372
|
|
|
356
373
|
return this.runs.stream(this.currentThread!.thread_id, this.currentAssistant!.assistant_id, {
|
|
@@ -364,51 +381,16 @@ export class LangGraphClient<TStateType = unknown> extends EventEmitter<LangGrap
|
|
|
364
381
|
streamMode: ["messages", "values"],
|
|
365
382
|
streamSubgraphs: true,
|
|
366
383
|
command,
|
|
384
|
+
/** @ts-ignore 为兼容不支持 AsyncIterableFunction 的环境*/
|
|
385
|
+
onCallback,
|
|
367
386
|
});
|
|
368
387
|
};
|
|
369
388
|
const streamResponse = await createStreamResponse();
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
for await (const chunk of streamResponse) {
|
|
377
|
-
streamRecord.push(chunk);
|
|
378
|
-
if (chunk.event === "metadata") {
|
|
379
|
-
this.currentRun = chunk.data;
|
|
380
|
-
} else if (chunk.event === "error" || chunk.event === "Error" || chunk.event === "__stream_error__") {
|
|
381
|
-
this.emit("error", chunk);
|
|
382
|
-
} else if (chunk.event === "messages/metadata") {
|
|
383
|
-
Object.assign(this.messagesMetadata, chunk.data);
|
|
384
|
-
continue;
|
|
385
|
-
} else if (chunk.event === "messages/partial" || chunk.event === "messages/complete") {
|
|
386
|
-
for (const message of chunk.data) {
|
|
387
|
-
this.messageProcessor.updateStreamingMessage(message);
|
|
388
|
-
}
|
|
389
|
-
this.emit("message", chunk);
|
|
390
|
-
continue;
|
|
391
|
-
} else if (chunk.event === "values") {
|
|
392
|
-
const data = chunk.data as {
|
|
393
|
-
__interrupt__?: InterruptData;
|
|
394
|
-
messages: Message[];
|
|
395
|
-
};
|
|
396
|
-
|
|
397
|
-
if (data.__interrupt__) {
|
|
398
|
-
this.humanInTheLoop = data.__interrupt__;
|
|
399
|
-
} else if (data.messages) {
|
|
400
|
-
const isResume = !!command?.resume;
|
|
401
|
-
const isLongerThanLocal = data.messages.length >= this.messageProcessor.getGraphMessages().length;
|
|
402
|
-
// resume 情况下,长度低于前端 message 的统统不接受
|
|
403
|
-
if (!isResume || (isResume && isLongerThanLocal)) {
|
|
404
|
-
this.messageProcessor.setGraphMessages(data.messages as RenderMessage[]);
|
|
405
|
-
this.emit("value", chunk);
|
|
406
|
-
}
|
|
407
|
-
this.graphState = chunk.data;
|
|
408
|
-
}
|
|
409
|
-
continue;
|
|
410
|
-
} else if (chunk.event.startsWith("values|")) {
|
|
411
|
-
this.graphPosition = chunk.event.split("|")[1];
|
|
389
|
+
if (!this.legacyMode) {
|
|
390
|
+
// 正常的 JS 环境都可以执行,但是部分环境不支持 AsyncGeneratorFunction(比如 sb 的微信小程序)
|
|
391
|
+
for await (const chunk of streamResponse) {
|
|
392
|
+
streamRecord.push(chunk);
|
|
393
|
+
this.processStreamChunk(chunk, command);
|
|
412
394
|
}
|
|
413
395
|
}
|
|
414
396
|
const data = await this.runFETool();
|
|
@@ -436,6 +418,52 @@ export class LangGraphClient<TStateType = unknown> extends EventEmitter<LangGrap
|
|
|
436
418
|
return position[position.length - 1];
|
|
437
419
|
}
|
|
438
420
|
|
|
421
|
+
/**
|
|
422
|
+
* @zh 处理流式响应的单个 chunk。
|
|
423
|
+
* @en Processes a single chunk from the stream response.
|
|
424
|
+
* @returns 是否需要跳过后续处理 (continue)
|
|
425
|
+
*/
|
|
426
|
+
private processStreamChunk(chunk: any, command?: Command): boolean {
|
|
427
|
+
if (chunk.event === "metadata") {
|
|
428
|
+
this.currentRun = chunk.data;
|
|
429
|
+
} else if (chunk.event === "error" || chunk.event === "Error" || chunk.event === "__stream_error__") {
|
|
430
|
+
this.emit("error", chunk);
|
|
431
|
+
} else if (chunk.event === "messages/metadata") {
|
|
432
|
+
Object.assign(this.messagesMetadata, chunk.data);
|
|
433
|
+
return true;
|
|
434
|
+
} else if (chunk.event === "messages/partial" || chunk.event === "messages/complete") {
|
|
435
|
+
for (const message of chunk.data) {
|
|
436
|
+
this.messageProcessor.updateStreamingMessage(message);
|
|
437
|
+
}
|
|
438
|
+
this.emit("message", chunk);
|
|
439
|
+
return true;
|
|
440
|
+
} else if (chunk.event === "values") {
|
|
441
|
+
const data = chunk.data as {
|
|
442
|
+
__interrupt__?: InterruptData;
|
|
443
|
+
messages: Message[];
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
if (data.__interrupt__) {
|
|
447
|
+
this.humanInTheLoop = camelcaseKeys(data.__interrupt__, {
|
|
448
|
+
deep: true,
|
|
449
|
+
});
|
|
450
|
+
} else if (data.messages) {
|
|
451
|
+
const isResume = !!command?.resume;
|
|
452
|
+
const isLongerThanLocal = data.messages.length >= this.messageProcessor.getGraphMessages().length;
|
|
453
|
+
// resume 情况下,长度低于前端 message 的统统不接受
|
|
454
|
+
if (!isResume || (isResume && isLongerThanLocal)) {
|
|
455
|
+
this.messageProcessor.setGraphMessages(data.messages as RenderMessage[]);
|
|
456
|
+
this.emit("value", chunk);
|
|
457
|
+
}
|
|
458
|
+
this.graphState = chunk.data;
|
|
459
|
+
}
|
|
460
|
+
return true;
|
|
461
|
+
} else if (chunk.event.startsWith("values|")) {
|
|
462
|
+
this.graphPosition = chunk.event.split("|")[1];
|
|
463
|
+
}
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
|
|
439
467
|
private runFETool() {
|
|
440
468
|
const data = this.messageProcessor.getStreamingMessages(); // 需要保证不被清理
|
|
441
469
|
const lastMessage = data[data.length - 1];
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { LangGraphClientConfig } from "../LangGraphClient.js";
|
|
2
2
|
import { type ILangGraphClient } from "@langgraph-js/pure-graph/dist/types.js";
|
|
3
|
-
|
|
3
|
+
import { Client } from "@langchain/langgraph-sdk";
|
|
4
4
|
export const createLangGraphServerClient = async (config: LangGraphClientConfig): Promise<ILangGraphClient> => {
|
|
5
|
-
const { Client } = await import("@langchain/langgraph-sdk");
|
|
6
5
|
return new Client(config) as ILangGraphClient;
|
|
7
6
|
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { BytesLineDecoder, SSEDecoder } from "./utils/sse.js";
|
|
2
|
+
import { LangGraphClientConfig } from "../LangGraphClient.js";
|
|
3
|
+
import { ILangGraphClient } from "@langgraph-js/pure-graph/dist/types.js";
|
|
4
|
+
|
|
5
|
+
const REGEX_RUN_METADATA = /(\/threads\/(?<thread_id>.+))?\/runs\/(?<run_id>.+)/;
|
|
6
|
+
function getRunMetadataFromResponse(response: Response) {
|
|
7
|
+
const contentLocation = response.headers.get("Content-Location");
|
|
8
|
+
if (!contentLocation) return void 0;
|
|
9
|
+
const match = REGEX_RUN_METADATA.exec(contentLocation);
|
|
10
|
+
if (!match?.groups?.run_id) return void 0;
|
|
11
|
+
return {
|
|
12
|
+
run_id: match.groups.run_id,
|
|
13
|
+
thread_id: match.groups.thread_id || void 0,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
import { Client } from "@langchain/langgraph-sdk";
|
|
17
|
+
|
|
18
|
+
export const createLowerJSClient = (config: Omit<LangGraphClientConfig, "client">): ILangGraphClient => {
|
|
19
|
+
const client = new Client(config);
|
|
20
|
+
/** @ts-ignore */
|
|
21
|
+
client.runs.joinStream = async function (this: any, threadId: string | null, runId: string, options: any) {
|
|
22
|
+
const opts = typeof options === "object" && options != null && options instanceof AbortSignal ? { signal: options } : options;
|
|
23
|
+
let [url, init] = this.prepareFetchOptions(threadId != null ? `/threads/${threadId}/runs/${runId}/stream` : `/runs/${runId}/stream`, {
|
|
24
|
+
method: "GET",
|
|
25
|
+
timeoutMs: null,
|
|
26
|
+
signal: opts?.signal,
|
|
27
|
+
headers: opts?.lastEventId ? { "Last-Event-ID": opts.lastEventId } : void 0,
|
|
28
|
+
params: {
|
|
29
|
+
cancel_on_disconnect: opts?.cancelOnDisconnect ? "1" : "0",
|
|
30
|
+
stream_mode: opts?.streamMode,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
if (this.onRequest != null) init = await this.onRequest(url, init);
|
|
34
|
+
const response = await this.asyncCaller.fetch(url, init);
|
|
35
|
+
const stream: ReadableStream = (response.body || new ReadableStream({ start: (ctrl) => ctrl.close() })).pipeThrough(BytesLineDecoder()).pipeThrough(SSEDecoder());
|
|
36
|
+
return stream.pipeTo(new WritableStream({ write: (chunk) => options.onCallback?.(chunk) }));
|
|
37
|
+
}.bind(client.runs);
|
|
38
|
+
/** @ts-ignore */
|
|
39
|
+
client.runs.stream = async function (this: any, threadId: string | null, assistantId: string, payload?: any) {
|
|
40
|
+
const json = {
|
|
41
|
+
input: payload?.input,
|
|
42
|
+
command: payload?.command,
|
|
43
|
+
config: payload?.config,
|
|
44
|
+
context: payload?.context,
|
|
45
|
+
metadata: payload?.metadata,
|
|
46
|
+
stream_mode: payload?.streamMode,
|
|
47
|
+
stream_subgraphs: payload?.streamSubgraphs,
|
|
48
|
+
stream_resumable: payload?.streamResumable,
|
|
49
|
+
feedback_keys: payload?.feedbackKeys,
|
|
50
|
+
assistant_id: assistantId,
|
|
51
|
+
interrupt_before: payload?.interruptBefore,
|
|
52
|
+
interrupt_after: payload?.interruptAfter,
|
|
53
|
+
checkpoint: payload?.checkpoint,
|
|
54
|
+
checkpoint_id: payload?.checkpointId,
|
|
55
|
+
webhook: payload?.webhook,
|
|
56
|
+
multitask_strategy: payload?.multitaskStrategy,
|
|
57
|
+
on_completion: payload?.onCompletion,
|
|
58
|
+
on_disconnect: payload?.onDisconnect,
|
|
59
|
+
after_seconds: payload?.afterSeconds,
|
|
60
|
+
if_not_exists: payload?.ifNotExists,
|
|
61
|
+
checkpoint_during: payload?.checkpointDuring,
|
|
62
|
+
durability: payload?.durability,
|
|
63
|
+
};
|
|
64
|
+
const endpoint = threadId == null ? `/runs/stream` : `/threads/${threadId}/runs/stream`;
|
|
65
|
+
let [url, init] = this.prepareFetchOptions(endpoint, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
json,
|
|
68
|
+
timeoutMs: null,
|
|
69
|
+
signal: payload?.signal,
|
|
70
|
+
});
|
|
71
|
+
if (this.onRequest != null) init = await this.onRequest(url, init);
|
|
72
|
+
const response = await this.asyncCaller.fetch(url, init);
|
|
73
|
+
const runMetadata = getRunMetadataFromResponse(response);
|
|
74
|
+
if (runMetadata) payload?.onRunCreated?.(runMetadata);
|
|
75
|
+
const stream: ReadableStream = (response.body || new ReadableStream({ start: (ctrl) => ctrl.close() })).pipeThrough(BytesLineDecoder()).pipeThrough(SSEDecoder());
|
|
76
|
+
|
|
77
|
+
return stream.pipeTo(new WritableStream({ write: (chunk) => payload.onCallback?.(chunk) }));
|
|
78
|
+
}.bind(client.runs);
|
|
79
|
+
return client as ILangGraphClient;
|
|
80
|
+
};
|