@langgraph-js/sdk 1.3.1 → 1.4.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/README.md CHANGED
@@ -1,163 +1,163 @@
1
- # @langgraph-js/sdk
2
-
3
- ![npm version](https://img.shields.io/npm/v/@langgraph-js/sdk)
4
- ![license](https://img.shields.io/npm/l/@langgraph-js/sdk)
5
-
6
- > The missing UI SDK for LangGraph - seamlessly integrate your AI agents with frontend interfaces
7
-
8
- ## Why @langgraph-js/sdk?
9
-
10
- Building AI agent applications is complex, especially when you need to bridge the gap between LangGraph agents and interactive user interfaces. This SDK solves the critical challenges of frontend integration:
11
-
12
- - **Provides a complete UI integration layer** - no more complex custom code to handle tools, streaming, and state management
13
- - **Simplifies human-in-the-loop interactions** - easily incorporate user feedback within agent workflows
14
- - **Handles edge cases automatically** - interruptions, errors, token management and more
15
- - **Offers a rich set of UI components** - ready-to-use elements to display agent interactions
16
-
17
- [DOCS](https://langgraph-js.netlify.app)
18
-
19
- ## Installation
20
-
21
- ```bash
22
- # Using npm
23
- npm install @langgraph-js/sdk
24
-
25
- # Using yarn
26
- yarn add @langgraph-js/sdk
27
-
28
- # Using pnpm
29
- pnpm add @langgraph-js/sdk
30
- ```
31
-
32
- ## Key Features
33
-
34
- ### Generative UI
35
-
36
- - ✅ Custom Tool Messages
37
- - ✅ Token Counter
38
- - ✅ Stop Graph Progress
39
- - ✅ Interrupt Handling
40
- - ✅ Error Handling
41
- - ✅ Spend Time Tracking
42
- - ✅ Time Persistence
43
-
44
- ### Frontend Actions
45
-
46
- - ✅ Definition of Union Tools
47
- - ✅ Frontend Functions As Tools
48
- - ✅ Human-in-the-Loop Interaction
49
- - ✅ Interrupt Mode
50
-
51
- ### Authorization
52
-
53
- - ✅ Cookie-Based Authentication
54
- - ✅ Custom Token Authentication
55
-
56
- ### Persistence
57
-
58
- - ✅ Read History from LangGraph
59
-
60
- ## Advanced Usage
61
-
62
- ### Creating a Chat Store
63
-
64
- You can easily create a reactive store for your LangGraph client:
65
-
66
- ```typescript
67
- import { createChatStore } from "@langgraph-js/sdk";
68
-
69
- export const globalChatStore = createChatStore(
70
- "agent",
71
- {
72
- // Custom LangGraph backend interaction
73
- apiUrl: "http://localhost:8123",
74
- // Custom headers for authentication
75
- defaultHeaders: JSON.parse(localStorage.getItem("code") || "{}"),
76
- callerOptions: {
77
- // Example for including cookies
78
- // fetch(url: string, options: RequestInit) {
79
- // options.credentials = "include";
80
- // return fetch(url, options);
81
- // },
82
- },
83
- },
84
- {
85
- onInit(client) {
86
- client.tools.bindTools([]);
87
- },
88
- }
89
- );
90
- ```
91
-
92
- ### React Integration
93
-
94
- First, install the nanostores React integration:
95
-
96
- ```bash
97
- pnpm i @nanostores/react
98
- ```
99
-
100
- Then create a context provider for your chat:
101
-
102
- ```tsx
103
- import React, { createContext, useContext, useEffect } from "react";
104
- import { globalChatStore } from "../store"; // Import your store
105
- import { UnionStore, useUnionStore } from "@langgraph-js/sdk";
106
- import { useStore } from "@nanostores/react";
107
-
108
- type ChatContextType = UnionStore<typeof globalChatStore>;
109
-
110
- const ChatContext = createContext<ChatContextType | undefined>(undefined);
111
-
112
- export const useChat = () => {
113
- const context = useContext(ChatContext);
114
- if (!context) {
115
- throw new Error("useChat must be used within a ChatProvider");
116
- }
117
- return context;
118
- };
119
-
120
- export const ChatProvider = ({ children }) => {
121
- // Use store to ensure React gets reactive state updates
122
- const store = useUnionStore(globalChatStore, useStore);
123
-
124
- useEffect(() => {
125
- // Initialize client
126
- store.initClient().then(() => {
127
- // Initialize conversation history
128
- store.refreshHistoryList();
129
- });
130
- }, [store.currentAgent]);
131
-
132
- return <ChatContext.Provider value={store}>{children}</ChatContext.Provider>;
133
- };
134
- ```
135
-
136
- Use it in your components:
137
-
138
- ```tsx
139
- export const MyChat = () => {
140
- return (
141
- <ChatProvider>
142
- <ChatComp></ChatComp>
143
- </ChatProvider>
144
- );
145
- };
146
-
147
- function ChatComp() {
148
- const chat = useChat();
149
- // Use chat store methods and state here
150
- }
151
- ```
152
-
153
- ## Documentation
154
-
155
- For complete documentation, visit our [official docs](https://langgraph-js.netlify.app).
156
-
157
- ## Contributing
158
-
159
- Contributions are welcome! Please feel free to submit a Pull Request.
160
-
161
- ## License
162
-
163
- This project is licensed under the Apache-2.0 License.
1
+ # @langgraph-js/sdk
2
+
3
+ ![npm version](https://img.shields.io/npm/v/@langgraph-js/sdk)
4
+ ![license](https://img.shields.io/npm/l/@langgraph-js/sdk)
5
+
6
+ > The missing UI SDK for LangGraph - seamlessly integrate your AI agents with frontend interfaces
7
+
8
+ ## Why @langgraph-js/sdk?
9
+
10
+ Building AI agent applications is complex, especially when you need to bridge the gap between LangGraph agents and interactive user interfaces. This SDK solves the critical challenges of frontend integration:
11
+
12
+ - **Provides a complete UI integration layer** - no more complex custom code to handle tools, streaming, and state management
13
+ - **Simplifies human-in-the-loop interactions** - easily incorporate user feedback within agent workflows
14
+ - **Handles edge cases automatically** - interruptions, errors, token management and more
15
+ - **Offers a rich set of UI components** - ready-to-use elements to display agent interactions
16
+
17
+ [DOCS](https://langgraph-js.netlify.app)
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ # Using npm
23
+ npm install @langgraph-js/sdk
24
+
25
+ # Using yarn
26
+ yarn add @langgraph-js/sdk
27
+
28
+ # Using pnpm
29
+ pnpm add @langgraph-js/sdk
30
+ ```
31
+
32
+ ## Key Features
33
+
34
+ ### Generative UI
35
+
36
+ - ✅ Custom Tool Messages
37
+ - ✅ Token Counter
38
+ - ✅ Stop Graph Progress
39
+ - ✅ Interrupt Handling
40
+ - ✅ Error Handling
41
+ - ✅ Spend Time Tracking
42
+ - ✅ Time Persistence
43
+
44
+ ### Frontend Actions
45
+
46
+ - ✅ Definition of Union Tools
47
+ - ✅ Frontend Functions As Tools
48
+ - ✅ Human-in-the-Loop Interaction
49
+ - ✅ Interrupt Mode
50
+
51
+ ### Authorization
52
+
53
+ - ✅ Cookie-Based Authentication
54
+ - ✅ Custom Token Authentication
55
+
56
+ ### Persistence
57
+
58
+ - ✅ Read History from LangGraph
59
+
60
+ ## Advanced Usage
61
+
62
+ ### Creating a Chat Store
63
+
64
+ You can easily create a reactive store for your LangGraph client:
65
+
66
+ ```typescript
67
+ import { createChatStore } from "@langgraph-js/sdk";
68
+
69
+ export const globalChatStore = createChatStore(
70
+ "agent",
71
+ {
72
+ // Custom LangGraph backend interaction
73
+ apiUrl: "http://localhost:8123",
74
+ // Custom headers for authentication
75
+ defaultHeaders: JSON.parse(localStorage.getItem("code") || "{}"),
76
+ callerOptions: {
77
+ // Example for including cookies
78
+ // fetch(url: string, options: RequestInit) {
79
+ // options.credentials = "include";
80
+ // return fetch(url, options);
81
+ // },
82
+ },
83
+ },
84
+ {
85
+ onInit(client) {
86
+ client.tools.bindTools([]);
87
+ },
88
+ }
89
+ );
90
+ ```
91
+
92
+ ### React Integration
93
+
94
+ First, install the nanostores React integration:
95
+
96
+ ```bash
97
+ pnpm i @nanostores/react
98
+ ```
99
+
100
+ Then create a context provider for your chat:
101
+
102
+ ```tsx
103
+ import React, { createContext, useContext, useEffect } from "react";
104
+ import { globalChatStore } from "../store"; // Import your store
105
+ import { UnionStore, useUnionStore } from "@langgraph-js/sdk";
106
+ import { useStore } from "@nanostores/react";
107
+
108
+ type ChatContextType = UnionStore<typeof globalChatStore>;
109
+
110
+ const ChatContext = createContext<ChatContextType | undefined>(undefined);
111
+
112
+ export const useChat = () => {
113
+ const context = useContext(ChatContext);
114
+ if (!context) {
115
+ throw new Error("useChat must be used within a ChatProvider");
116
+ }
117
+ return context;
118
+ };
119
+
120
+ export const ChatProvider = ({ children }) => {
121
+ // Use store to ensure React gets reactive state updates
122
+ const store = useUnionStore(globalChatStore, useStore);
123
+
124
+ useEffect(() => {
125
+ // Initialize client
126
+ store.initClient().then(() => {
127
+ // Initialize conversation history
128
+ store.refreshHistoryList();
129
+ });
130
+ }, [store.currentAgent]);
131
+
132
+ return <ChatContext.Provider value={store}>{children}</ChatContext.Provider>;
133
+ };
134
+ ```
135
+
136
+ Use it in your components:
137
+
138
+ ```tsx
139
+ export const MyChat = () => {
140
+ return (
141
+ <ChatProvider>
142
+ <ChatComp></ChatComp>
143
+ </ChatProvider>
144
+ );
145
+ };
146
+
147
+ function ChatComp() {
148
+ const chat = useChat();
149
+ // Use chat store methods and state here
150
+ }
151
+ ```
152
+
153
+ ## Documentation
154
+
155
+ For complete documentation, visit our [official docs](https://langgraph-js.netlify.app).
156
+
157
+ ## Contributing
158
+
159
+ Contributions are welcome! Please feel free to submit a Pull Request.
160
+
161
+ ## License
162
+
163
+ This project is licensed under the Apache-2.0 License.
@@ -119,6 +119,7 @@ export declare class LangGraphClient extends Client {
119
119
  /** 图发过来的更新信息 */
120
120
  graphMessages: RenderMessage[];
121
121
  cloneMessage(message: Message): Message;
122
+ private updateStreamingMessage;
122
123
  private replaceMessageWithValuesMessage;
123
124
  /**
124
125
  * @zh 用于 UI 中的流式渲染中的消息。
@@ -164,6 +165,8 @@ export declare class LangGraphClient extends Client {
164
165
  * @en Sends a message to the LangGraph backend.
165
166
  */
166
167
  sendMessage(input: string | Message[], { extraParams, _debug, command }?: SendMessageOptions): Promise<any[]>;
168
+ /** 子图的数据需要通过 merge 的方式重新进行合并更新 */
169
+ private mergeSubGraphMessagesToStreamingMessages;
167
170
  private runFETool;
168
171
  private callFETool;
169
172
  /**
@@ -126,6 +126,14 @@ export class LangGraphClient extends Client {
126
126
  cloneMessage(message) {
127
127
  return JSON.parse(JSON.stringify(message));
128
128
  }
129
+ updateStreamingMessage(message) {
130
+ const lastMessage = this.streamingMessage[this.streamingMessage.length - 1];
131
+ if (!(lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.id) || message.id !== lastMessage.id) {
132
+ this.streamingMessage.push(message);
133
+ return;
134
+ }
135
+ this.streamingMessage[this.streamingMessage.length - 1] = message;
136
+ }
129
137
  replaceMessageWithValuesMessage(message, isTool = false) {
130
138
  const key = (isTool ? "tool_call_id" : "id");
131
139
  const valuesMessage = this.graphMessages.find((i) => i[key] === message[key]);
@@ -145,8 +153,10 @@ export class LangGraphClient extends Client {
145
153
  get renderMessage() {
146
154
  var _a;
147
155
  const previousMessage = new Map();
156
+ const closedToolCallIds = new Set();
148
157
  const result = [];
149
158
  const inputMessages = [...this.graphMessages, ...this.streamingMessage];
159
+ console.log(inputMessages);
150
160
  // 从后往前遍历,这样可以保证最新的消息在前面
151
161
  for (let i = inputMessages.length - 1; i >= 0; i--) {
152
162
  const message = this.cloneMessage(inputMessages[i]);
@@ -164,7 +174,11 @@ export class LangGraphClient extends Client {
164
174
  previousMessage.set(message.id, m);
165
175
  /** @ts-ignore */
166
176
  const tool_calls = ((_a = m.tool_calls) === null || _a === void 0 ? void 0 : _a.length) ? m.tool_calls : m.tool_call_chunks;
167
- const new_tool_calls = tool_calls.map((tool, index) => {
177
+ const new_tool_calls = tool_calls
178
+ .filter((i) => {
179
+ return !closedToolCallIds.has(i.id);
180
+ })
181
+ .map((tool, index) => {
168
182
  var _a, _b, _c, _d;
169
183
  return this.replaceMessageWithValuesMessage({
170
184
  type: "tool",
@@ -186,6 +200,9 @@ export class LangGraphClient extends Client {
186
200
  result.unshift(m);
187
201
  }
188
202
  else {
203
+ if (message.type === "tool" && message.tool_call_id) {
204
+ closedToolCallIds.add(message.tool_call_id);
205
+ }
189
206
  // 记录这个 id 的消息,并添加到结果中
190
207
  const m = this.replaceMessageWithValuesMessage(message);
191
208
  previousMessage.set(message.id, m);
@@ -364,7 +381,7 @@ export class LangGraphClient extends Client {
364
381
  }
365
382
  else if (chunk.event === "messages/partial") {
366
383
  for (const message of chunk.data) {
367
- this.streamingMessage.push(message);
384
+ this.updateStreamingMessage(message);
368
385
  }
369
386
  this.emitStreamingUpdate({
370
387
  type: "message",
@@ -372,7 +389,7 @@ export class LangGraphClient extends Client {
372
389
  });
373
390
  continue;
374
391
  }
375
- else if (chunk.event.startsWith("values")) {
392
+ else if (chunk.event === "values") {
376
393
  const data = chunk.data;
377
394
  if (data.messages) {
378
395
  const isResume = !!(command === null || command === void 0 ? void 0 : command.resume);
@@ -390,6 +407,12 @@ export class LangGraphClient extends Client {
390
407
  this.streamingMessage = [];
391
408
  continue;
392
409
  }
410
+ else if (chunk.event.startsWith("values|")) {
411
+ // 这个 values 必然是子 values
412
+ if (chunk.data.messages) {
413
+ this.mergeSubGraphMessagesToStreamingMessages(chunk.data.messages);
414
+ }
415
+ }
393
416
  }
394
417
  this.streamingMessage = [];
395
418
  const data = await this.runFETool();
@@ -403,6 +426,23 @@ export class LangGraphClient extends Client {
403
426
  });
404
427
  return streamRecord;
405
428
  }
429
+ /** 子图的数据需要通过 merge 的方式重新进行合并更新 */
430
+ mergeSubGraphMessagesToStreamingMessages(messages) {
431
+ const map = new Map(messages.filter((i) => i.id).map((i) => [i.id, i]));
432
+ this.streamingMessage.forEach((i) => {
433
+ if (map.has(i.id)) {
434
+ const newValue = map.get(i.id);
435
+ Object.assign(i, newValue);
436
+ map.delete(i.id);
437
+ }
438
+ });
439
+ // 剩余的 message 一定不在 streamMessage 中
440
+ map.forEach((i) => {
441
+ if (i.type === "tool" && i.tool_call_id) {
442
+ this.streamingMessage.push(i);
443
+ }
444
+ });
445
+ }
406
446
  runFETool() {
407
447
  var _a;
408
448
  const data = this.graphMessages;
@@ -105,6 +105,8 @@ export const createChatStore = (initClientName, config, context = {}) => {
105
105
  // console.log(event.data);
106
106
  // 创建新流程时,默认为 __start__
107
107
  currentNodeName.set("__start__");
108
+ if (event.type === "done")
109
+ loading.set(false);
108
110
  // 创建新会话时,需要自动刷新历史面板
109
111
  return refreshHistoryList();
110
112
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langgraph-js/sdk",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
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",