@langgraph-js/sdk 3.6.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.
Files changed (40) hide show
  1. package/README.md +29 -0
  2. package/dist/LangGraphClient.d.ts +13 -1
  3. package/dist/LangGraphClient.js +101 -77
  4. package/dist/MessageProcessor.js +24 -33
  5. package/dist/SpendTime.js +4 -9
  6. package/dist/TestKit.js +16 -15
  7. package/dist/ToolManager.js +4 -7
  8. package/dist/artifacts/index.js +1 -1
  9. package/dist/client/LanggraphServer.js +1 -1
  10. package/dist/client/LowJSServer.d.ts +3 -0
  11. package/dist/client/LowJSServer.js +80 -0
  12. package/dist/client/index.d.ts +2 -0
  13. package/dist/client/index.js +2 -0
  14. package/dist/client/utils/sse.d.ts +8 -0
  15. package/dist/client/utils/sse.js +151 -0
  16. package/dist/client/utils/stream.d.ts +15 -0
  17. package/dist/client/utils/stream.js +104 -0
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +1 -0
  20. package/dist/react/ChatContext.d.ts +3 -0
  21. package/dist/react/ChatContext.js +8 -3
  22. package/dist/tool/ToolUI.js +3 -2
  23. package/dist/tool/createTool.js +3 -6
  24. package/dist/tool/utils.js +3 -4
  25. package/dist/ui-store/createChatStore.js +23 -39
  26. package/dist/vue/ChatContext.d.ts +3 -0
  27. package/dist/vue/ChatContext.js +3 -2
  28. package/package.json +3 -1
  29. package/src/LangGraphClient.ts +73 -45
  30. package/src/MessageProcessor.ts +7 -9
  31. package/src/client/LanggraphServer.ts +1 -2
  32. package/src/client/LowJSServer.ts +80 -0
  33. package/src/client/index.ts +2 -0
  34. package/src/client/utils/sse.ts +176 -0
  35. package/src/client/utils/stream.ts +114 -0
  36. package/src/index.ts +1 -0
  37. package/src/react/ChatContext.ts +20 -15
  38. package/src/vue/ChatContext.ts +5 -0
  39. package/test/TestKit.test.ts +10 -2
  40. 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 || ((_c = (_b = (_a = thread === null || thread === void 0 ? void 0 : thread.values) === null || _a === void 0 ? void 0 : _a.messages) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.content);
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((_a = context.showHistory) !== null && _a !== void 0 ? _a : false);
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((_b = context.showGraph) !== null && _b !== void 0 ? _b : false);
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 ((_a = client.get()) === null || _a === void 0 ? void 0 : _a.graphVisualize())) || null);
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((lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.node_name) || (lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.name) || "__start__");
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: (_a = config.client) !== null && _a !== void 0 ? _a : (await createLangGraphServerClient(config)),
107
+ client: config.client ?? (await createLangGraphServerClient(config)),
112
108
  });
113
- await newClient.initAssistant(currentAgent.get(), { fallbackToAvailableAssistants: (_b = context.fallbackToAvailableAssistants) !== null && _b !== void 0 ? _b : false });
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
- var _a;
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
- var _a;
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
- var _a;
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
- (_c = context.onInit) === null || _c === void 0 ? void 0 : _c.call(context, newClient);
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
- var _a, _b;
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 ((_a = client.get()) === null || _a === void 0 ? void 0 : _a.sendMessage(message || userInput.get(), extraData));
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 ((_b = client.get()) === null || _b === void 0 ? void 0 : _b.resetStream());
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
- var _a;
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 ((_a = client.get()) === null || _a === void 0 ? void 0 : _a.listThreads());
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 = (_a = toolsDefine.find((i) => i.name === tool_name)) === null || _a === void 0 ? void 0 : _a.render;
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
- var _a;
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
- var _a;
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
- var _a;
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 ((_a = client.get()) === null || _a === void 0 ? void 0 : _a.resetThread((_b = thread.metadata) === null || _b === void 0 ? void 0 : _b.graph_id, thread.thread_id));
325
+ const nowThread = await client.get()?.resetThread(thread.metadata?.graph_id, thread.thread_id);
341
326
  if (nowThread) {
342
- (_c = client.get()) === null || _c === void 0 ? void 0 : _c.resetStream();
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
- var _a;
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 中直接使用
@@ -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
- var _a;
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.6.0",
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",
@@ -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
- const streamRecord: any[] = [];
372
- this.emit("start", {
373
- event: "start",
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];
@@ -164,15 +164,13 @@ export class MessageProcessor {
164
164
  const parentMessage = toolParentMessage.get(message.tool_call_id!);
165
165
  if (assistantToolMessage) {
166
166
  message.tool_input = typeof assistantToolMessage.args !== "string" ? JSON.stringify(assistantToolMessage.args) : assistantToolMessage.args;
167
- if (message.additional_kwargs) {
168
- message.additional_kwargs.done = true;
169
- message.done = true;
170
- } else {
171
- message.done = true;
172
- message.additional_kwargs = {
173
- done: true,
174
- };
175
- }
167
+ const isDone = !!message.content;
168
+ message.done = isDone;
169
+ message.additional_kwargs = {
170
+ ...(parentMessage?.additional_kwargs || {}),
171
+ ...(message.additional_kwargs || {}),
172
+ done: isDone,
173
+ };
176
174
  }
177
175
  if (parentMessage) {
178
176
  message.usage_metadata = parentMessage.usage_metadata;
@@ -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
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./LanggraphServer.js";
2
+ export * from "./LowJSServer.js";