@langgraph-js/sdk 2.0.1 → 2.2.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.
@@ -148,15 +148,13 @@ export declare class LangGraphClient<TStateType = unknown, TUpdateType = TStateT
148
148
  supersteps?: Array<{
149
149
  updates: Array<{
150
150
  values: unknown;
151
- command? /** 值更新事件 */: Command;
151
+ command?: Command;
152
152
  asNode: string;
153
153
  }>;
154
154
  }>;
155
155
  }): Promise<Thread<TStateType>>;
156
156
  search(query?: {
157
- metadata
158
- /** Thread 创建事件 */
159
- ? /** Thread 创建事件 */: import("@langchain/langgraph-sdk").Metadata;
157
+ metadata?: import("@langchain/langgraph-sdk").Metadata;
160
158
  limit?: number;
161
159
  offset?: number;
162
160
  status?: import("@langchain/langgraph-sdk").ThreadStatus;
@@ -221,8 +219,9 @@ export declare class LangGraphClient<TStateType = unknown, TUpdateType = TStateT
221
219
  * @zh 创建一个新的 Thread。
222
220
  * @en Creates a new Thread.
223
221
  */
224
- createThread({ threadId, }?: {
222
+ createThread({ threadId, graphId, }?: {
225
223
  threadId?: string;
224
+ graphId?: string;
226
225
  }): Promise<Thread<TStateType>>;
227
226
  graphVisualize(): Promise<import("@langchain/langgraph-sdk").AssistantGraph>;
228
227
  /**
@@ -259,6 +258,13 @@ export declare class LangGraphClient<TStateType = unknown, TUpdateType = TStateT
259
258
  * @en Cancels the current Run.
260
259
  */
261
260
  cancelRun(): void;
261
+ /**
262
+ * @zh 回滚到指定的消息。但是不会触发数据的重新更新
263
+ * @en Reverts to the specified message.
264
+ */
265
+ revertChatTo(messageId: string): Promise<{
266
+ messages: Message[];
267
+ }>;
262
268
  /**
263
269
  * @zh 发送消息到 LangGraph 后端。
264
270
  * @en Sends a message to the LangGraph backend.
@@ -1,6 +1,7 @@
1
1
  import { EventEmitter } from "eventemitter3";
2
2
  import { ToolManager } from "./ToolManager.js";
3
3
  import { MessageProcessor } from "./MessageProcessor.js";
4
+ import { revertChatTo } from "./time-travel/index.js";
4
5
  /**
5
6
  * @zh LangGraphClient 类是与 LangGraph 后端交互的主要客户端。
6
7
  * @en The LangGraphClient class is the main client for interacting with the LangGraph backend.
@@ -73,10 +74,11 @@ export class LangGraphClient extends EventEmitter {
73
74
  * @zh 创建一个新的 Thread。
74
75
  * @en Creates a new Thread.
75
76
  */
76
- async createThread({ threadId, } = {}) {
77
+ async createThread({ threadId, graphId, } = {}) {
77
78
  try {
78
79
  this.currentThread = await this.threads.create({
79
80
  threadId,
81
+ graphId,
80
82
  });
81
83
  return this.currentThread;
82
84
  }
@@ -185,6 +187,17 @@ export class LangGraphClient extends EventEmitter {
185
187
  this.runs.cancel(this.currentThread.thread_id, this.currentRun.run_id);
186
188
  }
187
189
  }
190
+ /**
191
+ * @zh 回滚到指定的消息。但是不会触发数据的重新更新
192
+ * @en Reverts to the specified message.
193
+ */
194
+ async revertChatTo(messageId) {
195
+ const { state, checkpoint } = await revertChatTo(this.client, this.currentThread.thread_id, messageId);
196
+ this.graphState = state;
197
+ this.messageProcessor.clearStreamingMessages();
198
+ this.messageProcessor.setGraphMessages(state.messages);
199
+ return state;
200
+ }
188
201
  /**
189
202
  * @zh 发送消息到 LangGraph 后端。
190
203
  * @en Sends a message to the LangGraph backend.
@@ -195,7 +208,7 @@ export class LangGraphClient extends EventEmitter {
195
208
  throw new Error("Thread or Assistant not initialized");
196
209
  }
197
210
  if (!this.currentThread) {
198
- await this.createThread();
211
+ await this.createThread({ graphId: this.currentAssistant.graph_id });
199
212
  this.emit("thread", {
200
213
  event: "thread/create",
201
214
  data: {
@@ -0,0 +1,11 @@
1
+ import { Client, Message } from "@langchain/langgraph-sdk";
2
+ export declare function revertChatTo(client: Client<{
3
+ messages: Message[];
4
+ }, {
5
+ messages: Message[];
6
+ }, unknown>, threadId: string, messageId: string): Promise<{
7
+ state: {
8
+ messages: Message[];
9
+ };
10
+ checkpoint: Pick<import("@langchain/langgraph-sdk").Config, "configurable">;
11
+ }>;
@@ -0,0 +1,30 @@
1
+ export async function revertChatTo(client, threadId, messageId) {
2
+ const thread = await client.threads.get(threadId);
3
+ const messages = thread.values.messages;
4
+ const idx = messages.findIndex((message) => message.id === messageId);
5
+ if (idx === -1) {
6
+ throw new Error(`Message id ${messageId} not found`);
7
+ }
8
+ const removeMessages = messages.slice(idx + 1);
9
+ const state = {
10
+ ...thread.values,
11
+ messages: removeMessages.map((i) => {
12
+ // sb langgraph 官方实现都不能正常工作
13
+ // return new RemoveMessage({ id: i.id! }).toJSON();
14
+ return {
15
+ type: "remove",
16
+ id: i.id,
17
+ };
18
+ }),
19
+ };
20
+ const res = await client.threads.updateState(threadId, {
21
+ values: state,
22
+ });
23
+ // client.runs.wait();
24
+ return {
25
+ state: {
26
+ messages: messages.slice(0, idx + 1),
27
+ },
28
+ checkpoint: res,
29
+ };
30
+ }
package/dist/types.d.ts CHANGED
@@ -20,7 +20,7 @@ export type TypedAsyncGenerator<TStateType = unknown, TUpdateType = TStateType,
20
20
  /**
21
21
  * 兼容 LangGraph SDK 的接口定义,方便进行无侵入式的扩展
22
22
  */
23
- export interface ILangGraphClient<TStateType = unknown, TUpdateType = TStateType> {
23
+ export interface ILangGraphClient<TStateType = {}, TUpdateType = TStateType> {
24
24
  assistants: {
25
25
  search(query?: {
26
26
  graphId?: string;
@@ -53,7 +53,7 @@ export declare const createChatStore: (initClientName: string, config: Partial<L
53
53
  setTools(new_tools: UnionTool<any>[]): void;
54
54
  isFELocking(): boolean | undefined;
55
55
  initClient: () => Promise<LangGraphClient<unknown, unknown>>;
56
- sendMessage: (message?: Message[], extraData?: SendMessageOptions) => Promise<void>;
56
+ sendMessage: (message?: Message[], extraData?: SendMessageOptions, withoutCheck?: boolean) => Promise<void>;
57
57
  stopGeneration: () => void;
58
58
  toggleToolCollapse: (toolId: string) => void;
59
59
  toggleHistoryVisible: () => void;
@@ -61,6 +61,11 @@ export declare const createChatStore: (initClientName: string, config: Partial<L
61
61
  addToHistory: (thread: Thread<{
62
62
  messages: Message[];
63
63
  }>) => void;
64
+ /**
65
+ * @zh 回滚到指定的消息。
66
+ * @en Reverts to the specified message.
67
+ */
68
+ revertChatTo(messageId: string, resend?: boolean, sendOptions?: SendMessageOptions): Promise<void>;
64
69
  /**
65
70
  * @zh 设置用户输入内容。
66
71
  * @en Sets the user input content.
@@ -158,15 +158,25 @@ export const createChatStore = (initClientName, config, context = {}) => {
158
158
  * @zh 发送消息。
159
159
  * @en Sends a message.
160
160
  */
161
- const sendMessage = async (message, extraData) => {
162
- var _a;
163
- if ((!userInput.get().trim() && !(message === null || message === void 0 ? void 0 : message.length)) || loading.get() || !client.get())
161
+ const sendMessage = async (message, extraData, withoutCheck = false) => {
162
+ var _a, _b;
163
+ if ((!withoutCheck && !userInput.get().trim() && !(message === null || message === void 0 ? void 0 : message.length)) || loading.get() || !client.get())
164
164
  return;
165
165
  loading.set(true);
166
166
  inChatError.set(null);
167
- await ((_a = client.get()) === null || _a === void 0 ? void 0 : _a.sendMessage(message || userInput.get(), extraData));
168
- userInput.set("");
169
- loading.set(false);
167
+ try {
168
+ await ((_a = client.get()) === null || _a === void 0 ? void 0 : _a.sendMessage(message || userInput.get(), extraData));
169
+ }
170
+ catch (e) {
171
+ const isThreadRunning = e.message.includes("422");
172
+ if (isThreadRunning) {
173
+ await ((_b = client.get()) === null || _b === void 0 ? void 0 : _b.resetStream());
174
+ }
175
+ }
176
+ finally {
177
+ userInput.set("");
178
+ loading.set(false);
179
+ }
170
180
  };
171
181
  /**
172
182
  * @zh 停止当前的消息生成。
@@ -259,6 +269,20 @@ export const createChatStore = (initClientName, config, context = {}) => {
259
269
  toggleHistoryVisible,
260
270
  refreshHistoryList,
261
271
  addToHistory,
272
+ /**
273
+ * @zh 回滚到指定的消息。
274
+ * @en Reverts to the specified message.
275
+ */
276
+ async revertChatTo(messageId, resend = false, sendOptions) {
277
+ var _a;
278
+ await ((_a = client.get()) === null || _a === void 0 ? void 0 : _a.revertChatTo(messageId));
279
+ if (resend) {
280
+ return sendMessage([], sendOptions, true);
281
+ }
282
+ else {
283
+ updateUI(client.get());
284
+ }
285
+ },
262
286
  /**
263
287
  * @zh 设置用户输入内容。
264
288
  * @en Sets the user input content.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langgraph-js/sdk",
3
- "version": "2.0.1",
3
+ "version": "2.2.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",
@@ -4,6 +4,7 @@ import { ToolManager } from "./ToolManager.js";
4
4
  import { CallToolResult } from "./tool/createTool.js";
5
5
  import { ILangGraphClient } from "./types.js";
6
6
  import { MessageProcessor } from "./MessageProcessor.js";
7
+ import { revertChatTo } from "./time-travel/index.js";
7
8
 
8
9
  export type RenderMessage = Message & {
9
10
  /** 对于 AIMessage 来说是节点名称,对于工具节点来说是工具名称 */
@@ -164,12 +165,15 @@ export class LangGraphClient<TStateType = unknown, TUpdateType = TStateType> ext
164
165
  */
165
166
  async createThread({
166
167
  threadId,
168
+ graphId,
167
169
  }: {
168
170
  threadId?: string;
171
+ graphId?: string;
169
172
  } = {}) {
170
173
  try {
171
174
  this.currentThread = await this.threads.create({
172
175
  threadId,
176
+ graphId,
173
177
  });
174
178
  return this.currentThread;
175
179
  } catch (error) {
@@ -284,6 +288,17 @@ export class LangGraphClient<TStateType = unknown, TUpdateType = TStateType> ext
284
288
  this.runs.cancel((this.currentThread as any)!.thread_id, this.currentRun.run_id);
285
289
  }
286
290
  }
291
+ /**
292
+ * @zh 回滚到指定的消息。但是不会触发数据的重新更新
293
+ * @en Reverts to the specified message.
294
+ */
295
+ async revertChatTo(messageId: string) {
296
+ const { state, checkpoint } = await revertChatTo(this.client as any, this.currentThread!.thread_id, messageId);
297
+ this.graphState = state;
298
+ this.messageProcessor.clearStreamingMessages();
299
+ this.messageProcessor.setGraphMessages(state.messages! as RenderMessage[]);
300
+ return state;
301
+ }
287
302
  /**
288
303
  * @zh 发送消息到 LangGraph 后端。
289
304
  * @en Sends a message to the LangGraph backend.
@@ -293,7 +308,7 @@ export class LangGraphClient<TStateType = unknown, TUpdateType = TStateType> ext
293
308
  throw new Error("Thread or Assistant not initialized");
294
309
  }
295
310
  if (!this.currentThread) {
296
- await this.createThread();
311
+ await this.createThread({ graphId: this.currentAssistant!.graph_id });
297
312
  this.emit("thread", {
298
313
  event: "thread/create",
299
314
  data: {
@@ -0,0 +1,33 @@
1
+ import { RemoveMessage } from "@langchain/core/messages";
2
+ import { Client, Message } from "@langchain/langgraph-sdk";
3
+
4
+ export async function revertChatTo(client: Client<{ messages: Message[] }, { messages: Message[] }, unknown>, threadId: string, messageId: string) {
5
+ const thread = await client.threads.get(threadId);
6
+ const messages = thread.values.messages;
7
+ const idx = messages.findIndex((message) => message.id === messageId);
8
+ if (idx === -1) {
9
+ throw new Error(`Message id ${messageId} not found`);
10
+ }
11
+ const removeMessages = messages.slice(idx + 1);
12
+ const state = {
13
+ ...thread.values,
14
+ messages: removeMessages.map((i) => {
15
+ // sb langgraph 官方实现都不能正常工作
16
+ // return new RemoveMessage({ id: i.id! }).toJSON();
17
+ return {
18
+ type: "remove",
19
+ id: i.id!,
20
+ };
21
+ }),
22
+ };
23
+ const res = await client.threads.updateState(threadId, {
24
+ values: state,
25
+ });
26
+ // client.runs.wait();
27
+ return {
28
+ state: {
29
+ messages: messages.slice(0, idx + 1),
30
+ },
31
+ checkpoint: res,
32
+ };
33
+ }
package/src/types.ts CHANGED
@@ -52,7 +52,7 @@ export type TypedAsyncGenerator<TStateType = unknown, TUpdateType = TStateType,
52
52
  /**
53
53
  * 兼容 LangGraph SDK 的接口定义,方便进行无侵入式的扩展
54
54
  */
55
- export interface ILangGraphClient<TStateType = unknown, TUpdateType = TStateType> {
55
+ export interface ILangGraphClient<TStateType = {}, TUpdateType = TStateType> {
56
56
  assistants: {
57
57
  search(query?: { graphId?: string; metadata?: Metadata; limit?: number; offset?: number; sortBy?: AssistantSortBy; sortOrder?: SortOrder }): Promise<Assistant[]>;
58
58
  getGraph(assistantId: string, options?: { xray?: boolean | number }): Promise<AssistantGraph>;
@@ -167,16 +167,22 @@ export const createChatStore = (
167
167
  * @zh 发送消息。
168
168
  * @en Sends a message.
169
169
  */
170
- const sendMessage = async (message?: Message[], extraData?: SendMessageOptions) => {
171
- if ((!userInput.get().trim() && !message?.length) || loading.get() || !client.get()) return;
170
+ const sendMessage = async (message?: Message[], extraData?: SendMessageOptions, withoutCheck = false) => {
171
+ if ((!withoutCheck && !userInput.get().trim() && !message?.length) || loading.get() || !client.get()) return;
172
172
 
173
173
  loading.set(true);
174
174
  inChatError.set(null);
175
-
176
- await client.get()?.sendMessage(message || userInput.get(), extraData);
177
-
178
- userInput.set("");
179
- loading.set(false);
175
+ try {
176
+ await client.get()?.sendMessage(message || userInput.get(), extraData);
177
+ } catch (e) {
178
+ const isThreadRunning = (e as Error).message.includes("422");
179
+ if (isThreadRunning) {
180
+ await client.get()?.resetStream();
181
+ }
182
+ } finally {
183
+ userInput.set("");
184
+ loading.set(false);
185
+ }
180
186
  };
181
187
 
182
188
  /**
@@ -270,6 +276,18 @@ export const createChatStore = (
270
276
  toggleHistoryVisible,
271
277
  refreshHistoryList,
272
278
  addToHistory,
279
+ /**
280
+ * @zh 回滚到指定的消息。
281
+ * @en Reverts to the specified message.
282
+ */
283
+ async revertChatTo(messageId: string, resend = false, sendOptions?: SendMessageOptions) {
284
+ await client.get()?.revertChatTo(messageId);
285
+ if (resend) {
286
+ return sendMessage([], sendOptions, true);
287
+ } else {
288
+ updateUI(client.get()!);
289
+ }
290
+ },
273
291
  /**
274
292
  * @zh 设置用户输入内容。
275
293
  * @en Sets the user input content.