@langgraph-js/sdk 2.0.0 → 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.
@@ -68,7 +68,7 @@ export interface LangGraphClientConfig {
68
68
  timeoutMs?: number;
69
69
  defaultHeaders?: Record<string, string | null | undefined>;
70
70
  /** 自定义客户端实现,如果不提供则使用官方 Client */
71
- client: ILangGraphClient;
71
+ client: ILangGraphClient<any, any>;
72
72
  }
73
73
  export interface LangGraphEvents {
74
74
  /** 流开始事件 */
@@ -113,6 +113,11 @@ export declare class LangGraphClient<TStateType = unknown, TUpdateType = TStateT
113
113
  private currentAssistant;
114
114
  private currentThread;
115
115
  tools: ToolManager;
116
+ availableAssistants: Assistant[];
117
+ graphState: any;
118
+ currentRun?: {
119
+ run_id: string;
120
+ };
116
121
  stopController: AbortController | null;
117
122
  /** 用于存储 subAgent 状态数据的键 */
118
123
  subAgentsKey: string;
@@ -135,7 +140,7 @@ export declare class LangGraphClient<TStateType = unknown, TUpdateType = TStateT
135
140
  };
136
141
  /** 代理 threads 属性到内部 client */
137
142
  get threads(): {
138
- create<ValuesType = TStateType>(payload?: {
143
+ create(payload?: {
139
144
  metadata?: import("@langchain/langgraph-sdk").Metadata;
140
145
  threadId?: string;
141
146
  ifExists?: import("@langchain/langgraph-sdk").OnConflictBehavior;
@@ -147,16 +152,16 @@ export declare class LangGraphClient<TStateType = unknown, TUpdateType = TStateT
147
152
  asNode: string;
148
153
  }>;
149
154
  }>;
150
- }): Promise<Thread<ValuesType>>;
151
- search<ValuesType = TStateType>(query?: {
155
+ }): Promise<Thread<TStateType>>;
156
+ search(query?: {
152
157
  metadata?: import("@langchain/langgraph-sdk").Metadata;
153
158
  limit?: number;
154
159
  offset?: number;
155
160
  status?: import("@langchain/langgraph-sdk").ThreadStatus;
156
161
  sortBy?: import("./types.js").ThreadSortBy;
157
162
  sortOrder?: import("./types.js").SortOrder;
158
- }): Promise<Thread<ValuesType>[]>;
159
- get<ValuesType = TStateType>(threadId: string): Promise<Thread<ValuesType>>;
163
+ }): Promise<Thread<TStateType>[]>;
164
+ get(threadId: string): Promise<Thread<TStateType>>;
160
165
  delete(threadId: string): Promise<void>;
161
166
  };
162
167
  /** 代理 runs 属性到内部 client */
@@ -166,31 +171,7 @@ export declare class LangGraphClient<TStateType = unknown, TUpdateType = TStateT
166
171
  offset?: number;
167
172
  status?: import("./types.js").RunStatus;
168
173
  }): Promise<import("@langchain/langgraph-sdk").Run[]>;
169
- stream<TStreamMode extends import("@langchain/langgraph-sdk").StreamMode | import("@langchain/langgraph-sdk").StreamMode[] = import("@langchain/langgraph-sdk").StreamMode, TSubgraphs extends boolean = false>(threadId: null, assistantId: string, payload?: {
170
- input?: Record<string, unknown> | null;
171
- metadata?: import("@langchain/langgraph-sdk").Metadata;
172
- config?: import("@langchain/langgraph-sdk").Config;
173
- checkpointId?: string;
174
- checkpoint?: Omit<import("@langchain/langgraph-sdk").Checkpoint, "thread_id">;
175
- checkpointDuring?: boolean;
176
- interruptBefore?: "*" | string[];
177
- interruptAfter?: "*" | string[];
178
- signal?: AbortController["signal"];
179
- webhook?: string;
180
- onDisconnect?: import("./types.js").DisconnectMode;
181
- afterSeconds?: number;
182
- ifNotExists?: "create" | "reject";
183
- command?: Command;
184
- onRunCreated?: (params: {
185
- run_id: string;
186
- thread_id?: string;
187
- }) => void;
188
- streamMode?: TStreamMode | undefined;
189
- streamSubgraphs?: TSubgraphs | undefined;
190
- streamResumable?: boolean;
191
- feedbackKeys?: string[];
192
- } | undefined): import("./types.js").TypedAsyncGenerator<TStreamMode, TSubgraphs, TStateType, TUpdateType, unknown>;
193
- stream<TStreamMode extends import("@langchain/langgraph-sdk").StreamMode | import("@langchain/langgraph-sdk").StreamMode[] = import("@langchain/langgraph-sdk").StreamMode, TSubgraphs extends boolean = false>(threadId: string, assistantId: string, payload?: {
174
+ stream<TSubgraphs extends boolean = false>(threadId: string, assistantId: string, payload?: {
194
175
  input?: Record<string, unknown> | null;
195
176
  metadata?: import("@langchain/langgraph-sdk").Metadata;
196
177
  config?: import("@langchain/langgraph-sdk").Config;
@@ -211,12 +192,12 @@ export declare class LangGraphClient<TStateType = unknown, TUpdateType = TStateT
211
192
  run_id: string;
212
193
  thread_id?: string;
213
194
  }) => void;
214
- streamMode?: TStreamMode | undefined;
195
+ streamMode?: import("@langchain/langgraph-sdk").StreamMode[];
215
196
  streamSubgraphs?: TSubgraphs | undefined;
216
197
  streamResumable?: boolean;
217
198
  feedbackKeys?: string[];
218
- } | undefined): import("./types.js").TypedAsyncGenerator<TStreamMode, TSubgraphs, TStateType, TUpdateType, unknown>;
219
- joinStream(threadId: string | undefined | null, runId: string, options?: {
199
+ } | undefined): import("./types.js").TypedAsyncGenerator<TSubgraphs, TStateType, TUpdateType>;
200
+ joinStream(threadId: string, runId: string, options?: {
220
201
  signal?: AbortSignal;
221
202
  cancelOnDisconnect?: boolean;
222
203
  lastEventId?: string;
@@ -228,7 +209,6 @@ export declare class LangGraphClient<TStateType = unknown, TUpdateType = TStateT
228
209
  }>;
229
210
  cancel(threadId: string, runId: string, wait?: boolean, action?: import("./types.js").CancelAction): Promise<void>;
230
211
  };
231
- availableAssistants: Assistant[];
232
212
  private listAssistants;
233
213
  /**
234
214
  * @zh 初始化 Assistant。
@@ -239,8 +219,9 @@ export declare class LangGraphClient<TStateType = unknown, TUpdateType = TStateT
239
219
  * @zh 创建一个新的 Thread。
240
220
  * @en Creates a new Thread.
241
221
  */
242
- createThread({ threadId, }?: {
222
+ createThread({ threadId, graphId, }?: {
243
223
  threadId?: string;
224
+ graphId?: string;
244
225
  }): Promise<Thread<TStateType>>;
245
226
  graphVisualize(): Promise<import("@langchain/langgraph-sdk").AssistantGraph>;
246
227
  /**
@@ -272,15 +253,18 @@ export declare class LangGraphClient<TStateType = unknown, TUpdateType = TStateT
272
253
  };
273
254
  /** 前端工具人机交互时,锁住面板 */
274
255
  isFELocking(messages: RenderMessage[]): boolean | undefined;
275
- graphState: any;
276
- currentRun?: {
277
- run_id: string;
278
- };
279
256
  /**
280
257
  * @zh 取消当前的 Run。
281
258
  * @en Cancels the current Run.
282
259
  */
283
260
  cancelRun(): void;
261
+ /**
262
+ * @zh 回滚到指定的消息。但是不会触发数据的重新更新
263
+ * @en Reverts to the specified message.
264
+ */
265
+ revertChatTo(messageId: string): Promise<{
266
+ messages: Message[];
267
+ }>;
284
268
  /**
285
269
  * @zh 发送消息到 LangGraph 后端。
286
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.
@@ -11,11 +12,11 @@ export class LangGraphClient extends EventEmitter {
11
12
  this.currentAssistant = null;
12
13
  this.currentThread = null;
13
14
  this.tools = new ToolManager();
15
+ this.availableAssistants = [];
16
+ this.graphState = {};
14
17
  this.stopController = null;
15
18
  /** 用于存储 subAgent 状态数据的键 */
16
19
  this.subAgentsKey = "task_store";
17
- this.availableAssistants = [];
18
- this.graphState = {};
19
20
  /** 当前子图位置,但是依赖 stream,不太适合稳定使用*/
20
21
  this.graphPosition = "";
21
22
  this.extraParams = {};
@@ -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: {
@@ -1,2 +1,3 @@
1
+ import { LangGraphClientConfig } from "../LangGraphClient.js";
1
2
  import { ILangGraphClient } from "../types.js";
2
- export declare const createLangGraphServerClient: () => Promise<ILangGraphClient>;
3
+ export declare const createLangGraphServerClient: (config: LangGraphClientConfig) => Promise<ILangGraphClient>;
@@ -1,4 +1,4 @@
1
- export const createLangGraphServerClient = async () => {
1
+ export const createLangGraphServerClient = async (config) => {
2
2
  const { Client } = await import("@langchain/langgraph-sdk");
3
- return new Client();
3
+ return new Client(config);
4
4
  };
@@ -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
@@ -8,7 +8,7 @@ export type MultitaskStrategy = "reject" | "interrupt" | "rollback" | "enqueue";
8
8
  export type DisconnectMode = "cancel" | "continue";
9
9
  export type OnCompletionBehavior = "complete" | "continue";
10
10
  export type CancelAction = "interrupt" | "rollback";
11
- export type TypedAsyncGenerator<TStreamMode extends StreamMode | StreamMode[] = [], TSubgraphs extends boolean = false, TStateType = unknown, TUpdateType = TStateType, TCustomType = unknown> = AsyncGenerator<{
11
+ export type TypedAsyncGenerator<TStateType = unknown, TUpdateType = TStateType, TCustomType = unknown> = AsyncGenerator<{
12
12
  values: ValuesStreamEvent<TStateType>;
13
13
  updates: UpdatesStreamEvent<TUpdateType>;
14
14
  custom: CustomStreamEvent<TCustomType>;
@@ -16,11 +16,11 @@ export type TypedAsyncGenerator<TStreamMode extends StreamMode | StreamMode[] =
16
16
  messages: MessagesStreamEvent;
17
17
  "messages-tuple": MessagesTupleStreamEvent;
18
18
  events: EventsStreamEvent;
19
- }[TStreamMode extends StreamMode[] ? TStreamMode[number] : TStreamMode] | ErrorStreamEvent | MetadataStreamEvent | FeedbackStreamEvent>;
19
+ }[StreamMode] | ErrorStreamEvent | MetadataStreamEvent | FeedbackStreamEvent>;
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;
@@ -35,7 +35,7 @@ export interface ILangGraphClient<TStateType = unknown, TUpdateType = TStateType
35
35
  }): Promise<AssistantGraph>;
36
36
  };
37
37
  threads: {
38
- create<ValuesType = TStateType>(payload?: {
38
+ create(payload?: {
39
39
  metadata?: Metadata;
40
40
  threadId?: string;
41
41
  ifExists?: OnConflictBehavior;
@@ -47,16 +47,16 @@ export interface ILangGraphClient<TStateType = unknown, TUpdateType = TStateType
47
47
  asNode: string;
48
48
  }>;
49
49
  }>;
50
- }): Promise<Thread<ValuesType>>;
51
- search<ValuesType = TStateType>(query?: {
50
+ }): Promise<Thread<TStateType>>;
51
+ search(query?: {
52
52
  metadata?: Metadata;
53
53
  limit?: number;
54
54
  offset?: number;
55
55
  status?: ThreadStatus;
56
56
  sortBy?: ThreadSortBy;
57
57
  sortOrder?: SortOrder;
58
- }): Promise<Thread<ValuesType>[]>;
59
- get<ValuesType = TStateType>(threadId: string): Promise<Thread<ValuesType>>;
58
+ }): Promise<Thread<TStateType>[]>;
59
+ get(threadId: string): Promise<Thread<TStateType>>;
60
60
  delete(threadId: string): Promise<void>;
61
61
  };
62
62
  runs: {
@@ -65,31 +65,7 @@ export interface ILangGraphClient<TStateType = unknown, TUpdateType = TStateType
65
65
  offset?: number;
66
66
  status?: RunStatus;
67
67
  }): Promise<Run[]>;
68
- stream<TStreamMode extends StreamMode | StreamMode[] = StreamMode, TSubgraphs extends boolean = false>(threadId: null, assistantId: string, payload?: {
69
- input?: Record<string, unknown> | null;
70
- metadata?: Metadata;
71
- config?: Config;
72
- checkpointId?: string;
73
- checkpoint?: Omit<Checkpoint, "thread_id">;
74
- checkpointDuring?: boolean;
75
- interruptBefore?: "*" | string[];
76
- interruptAfter?: "*" | string[];
77
- signal?: AbortController["signal"];
78
- webhook?: string;
79
- onDisconnect?: DisconnectMode;
80
- afterSeconds?: number;
81
- ifNotExists?: "create" | "reject";
82
- command?: Command;
83
- onRunCreated?: (params: {
84
- run_id: string;
85
- thread_id?: string;
86
- }) => void;
87
- streamMode?: TStreamMode;
88
- streamSubgraphs?: TSubgraphs;
89
- streamResumable?: boolean;
90
- feedbackKeys?: string[];
91
- }): TypedAsyncGenerator<TStreamMode, TSubgraphs, TStateType, TUpdateType>;
92
- stream<TStreamMode extends StreamMode | StreamMode[] = StreamMode, TSubgraphs extends boolean = false>(threadId: string, assistantId: string, payload?: {
68
+ stream<TSubgraphs extends boolean = false>(threadId: string, assistantId: string, payload?: {
93
69
  input?: Record<string, unknown> | null;
94
70
  metadata?: Metadata;
95
71
  config?: Config;
@@ -110,12 +86,12 @@ export interface ILangGraphClient<TStateType = unknown, TUpdateType = TStateType
110
86
  run_id: string;
111
87
  thread_id?: string;
112
88
  }) => void;
113
- streamMode?: TStreamMode;
89
+ streamMode?: StreamMode[];
114
90
  streamSubgraphs?: TSubgraphs;
115
91
  streamResumable?: boolean;
116
92
  feedbackKeys?: string[];
117
- }): TypedAsyncGenerator<TStreamMode, TSubgraphs, TStateType, TUpdateType>;
118
- joinStream(threadId: string | undefined | null, runId: string, options?: {
93
+ }): TypedAsyncGenerator<TSubgraphs, TStateType, TUpdateType>;
94
+ joinStream(threadId: string, runId: string, options?: {
119
95
  signal?: AbortSignal;
120
96
  cancelOnDisconnect?: boolean;
121
97
  lastEventId?: string;
@@ -25,7 +25,7 @@ export declare const getHistoryContent: (thread: Thread) => string | any[];
25
25
  * @zh 创建一个用于聊天界面的状态管理器 (store)。
26
26
  * @en Creates a state manager (store) for the chat interface.
27
27
  */
28
- export declare const createChatStore: (initClientName: string, config: LangGraphClientConfig, context?: {
28
+ export declare const createChatStore: (initClientName: string, config: Partial<LangGraphClientConfig>, context?: {
29
29
  showHistory?: boolean;
30
30
  showGraph?: boolean;
31
31
  onInit?: (client: LangGraphClient) => void;
@@ -53,7 +53,7 @@ export declare const createChatStore: (initClientName: string, config: LangGraph
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: LangGraph
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.
@@ -106,7 +106,7 @@ export const createChatStore = (initClientName, config, context = {}) => {
106
106
  var _a, _b;
107
107
  const newClient = new LangGraphClient({
108
108
  ...config,
109
- client: (_a = config.client) !== null && _a !== void 0 ? _a : (await createLangGraphServerClient()),
109
+ client: (_a = config.client) !== null && _a !== void 0 ? _a : (await createLangGraphServerClient(config)),
110
110
  });
111
111
  await newClient.initAssistant(currentAgent.get());
112
112
  currentAgent.set(newClient.getCurrentAssistant().graph_id);
@@ -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.0",
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 来说是节点名称,对于工具节点来说是工具名称 */
@@ -68,7 +69,7 @@ export interface LangGraphClientConfig {
68
69
  timeoutMs?: number;
69
70
  defaultHeaders?: Record<string, string | null | undefined>;
70
71
  /** 自定义客户端实现,如果不提供则使用官方 Client */
71
- client: ILangGraphClient;
72
+ client: ILangGraphClient<any, any>;
72
73
  }
73
74
 
74
75
  // 定义事件数据类型
@@ -96,6 +97,9 @@ export class LangGraphClient<TStateType = unknown, TUpdateType = TStateType> ext
96
97
  private currentAssistant: Assistant | null = null;
97
98
  private currentThread: Thread<TStateType> | null = null;
98
99
  tools: ToolManager = new ToolManager();
100
+ availableAssistants: Assistant[] = [];
101
+ graphState: any = {};
102
+ currentRun?: { run_id: string };
99
103
  stopController: AbortController | null = null;
100
104
  /** 用于存储 subAgent 状态数据的键 */
101
105
  subAgentsKey = "task_store";
@@ -122,7 +126,6 @@ export class LangGraphClient<TStateType = unknown, TUpdateType = TStateType> ext
122
126
  get runs() {
123
127
  return this.client.runs;
124
128
  }
125
- availableAssistants: Assistant[] = [];
126
129
  private listAssistants() {
127
130
  return this.assistants.search({
128
131
  metadata: null,
@@ -162,12 +165,15 @@ export class LangGraphClient<TStateType = unknown, TUpdateType = TStateType> ext
162
165
  */
163
166
  async createThread({
164
167
  threadId,
168
+ graphId,
165
169
  }: {
166
170
  threadId?: string;
171
+ graphId?: string;
167
172
  } = {}) {
168
173
  try {
169
174
  this.currentThread = await this.threads.create({
170
175
  threadId,
176
+ graphId,
171
177
  });
172
178
  return this.currentThread;
173
179
  } catch (error) {
@@ -272,8 +278,7 @@ export class LangGraphClient<TStateType = unknown, TUpdateType = TStateType> ext
272
278
  const tool = this.tools.getTool(lastMessage?.name!);
273
279
  return tool && tool.render && lastMessage?.type === "tool" && !lastMessage?.additional_kwargs?.done;
274
280
  }
275
- graphState: any = {};
276
- currentRun?: { run_id: string };
281
+
277
282
  /**
278
283
  * @zh 取消当前的 Run。
279
284
  * @en Cancels the current Run.
@@ -283,6 +288,17 @@ export class LangGraphClient<TStateType = unknown, TUpdateType = TStateType> ext
283
288
  this.runs.cancel((this.currentThread as any)!.thread_id, this.currentRun.run_id);
284
289
  }
285
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
+ }
286
302
  /**
287
303
  * @zh 发送消息到 LangGraph 后端。
288
304
  * @en Sends a message to the LangGraph backend.
@@ -292,7 +308,7 @@ export class LangGraphClient<TStateType = unknown, TUpdateType = TStateType> ext
292
308
  throw new Error("Thread or Assistant not initialized");
293
309
  }
294
310
  if (!this.currentThread) {
295
- await this.createThread();
311
+ await this.createThread({ graphId: this.currentAssistant!.graph_id });
296
312
  this.emit("thread", {
297
313
  event: "thread/create",
298
314
  data: {
@@ -1,6 +1,7 @@
1
+ import { LangGraphClientConfig } from "../LangGraphClient.js";
1
2
  import { ILangGraphClient } from "../types.js";
2
3
 
3
- export const createLangGraphServerClient = async (): Promise<ILangGraphClient> => {
4
+ export const createLangGraphServerClient = async (config: LangGraphClientConfig): Promise<ILangGraphClient> => {
4
5
  const { Client } = await import("@langchain/langgraph-sdk");
5
- return new Client() as unknown as ILangGraphClient;
6
+ return new Client(config) as unknown as ILangGraphClient;
6
7
  };
@@ -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
@@ -34,13 +34,7 @@ export type OnCompletionBehavior = "complete" | "continue";
34
34
  export type CancelAction = "interrupt" | "rollback";
35
35
 
36
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<
37
+ export type TypedAsyncGenerator<TStateType = unknown, TUpdateType = TStateType, TCustomType = unknown> = AsyncGenerator<
44
38
  | {
45
39
  values: ValuesStreamEvent<TStateType>;
46
40
  updates: UpdatesStreamEvent<TUpdateType>;
@@ -49,7 +43,7 @@ export type TypedAsyncGenerator<
49
43
  messages: MessagesStreamEvent;
50
44
  "messages-tuple": MessagesTupleStreamEvent;
51
45
  events: EventsStreamEvent;
52
- }[TStreamMode extends StreamMode[] ? TStreamMode[number] : TStreamMode]
46
+ }[StreamMode]
53
47
  | ErrorStreamEvent
54
48
  | MetadataStreamEvent
55
49
  | FeedbackStreamEvent
@@ -58,13 +52,13 @@ export type TypedAsyncGenerator<
58
52
  /**
59
53
  * 兼容 LangGraph SDK 的接口定义,方便进行无侵入式的扩展
60
54
  */
61
- export interface ILangGraphClient<TStateType = unknown, TUpdateType = TStateType> {
55
+ export interface ILangGraphClient<TStateType = {}, TUpdateType = TStateType> {
62
56
  assistants: {
63
57
  search(query?: { graphId?: string; metadata?: Metadata; limit?: number; offset?: number; sortBy?: AssistantSortBy; sortOrder?: SortOrder }): Promise<Assistant[]>;
64
58
  getGraph(assistantId: string, options?: { xray?: boolean | number }): Promise<AssistantGraph>;
65
59
  };
66
60
  threads: {
67
- create<ValuesType = TStateType>(payload?: {
61
+ create(payload?: {
68
62
  metadata?: Metadata;
69
63
  threadId?: string;
70
64
  ifExists?: OnConflictBehavior;
@@ -76,16 +70,9 @@ export interface ILangGraphClient<TStateType = unknown, TUpdateType = TStateType
76
70
  asNode: string;
77
71
  }>;
78
72
  }>;
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>>;
73
+ }): Promise<Thread<TStateType>>;
74
+ search(query?: { metadata?: Metadata; limit?: number; offset?: number; status?: ThreadStatus; sortBy?: ThreadSortBy; sortOrder?: SortOrder }): Promise<Thread<TStateType>[]>;
75
+ get(threadId: string): Promise<Thread<TStateType>>;
89
76
  delete(threadId: string): Promise<void>;
90
77
  };
91
78
  runs: {
@@ -97,32 +84,8 @@ export interface ILangGraphClient<TStateType = unknown, TUpdateType = TStateType
97
84
  status?: RunStatus;
98
85
  }
99
86
  ): 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>(
87
+
88
+ stream<TSubgraphs extends boolean = false>(
126
89
  threadId: string,
127
90
  assistantId: string,
128
91
  payload?: {
@@ -143,14 +106,14 @@ export interface ILangGraphClient<TStateType = unknown, TUpdateType = TStateType
143
106
  ifNotExists?: "create" | "reject";
144
107
  command?: Command;
145
108
  onRunCreated?: (params: { run_id: string; thread_id?: string }) => void;
146
- streamMode?: TStreamMode;
109
+ streamMode?: StreamMode[];
147
110
  streamSubgraphs?: TSubgraphs;
148
111
  streamResumable?: boolean;
149
112
  feedbackKeys?: string[];
150
113
  }
151
- ): TypedAsyncGenerator<TStreamMode, TSubgraphs, TStateType, TUpdateType>;
114
+ ): TypedAsyncGenerator<TSubgraphs, TStateType, TUpdateType>;
152
115
  joinStream(
153
- threadId: string | undefined | null,
116
+ threadId: string,
154
117
  runId: string,
155
118
  options?:
156
119
  | {
@@ -66,7 +66,7 @@ export const getHistoryContent = (thread: Thread) => {
66
66
  */
67
67
  export const createChatStore = (
68
68
  initClientName: string,
69
- config: LangGraphClientConfig,
69
+ config: Partial<LangGraphClientConfig>,
70
70
  context: {
71
71
  showHistory?: boolean;
72
72
  showGraph?: boolean;
@@ -113,7 +113,7 @@ export const createChatStore = (
113
113
  async function initClient() {
114
114
  const newClient = new LangGraphClient({
115
115
  ...config,
116
- client: config.client ?? (await createLangGraphServerClient()),
116
+ client: config.client ?? (await createLangGraphServerClient(config as LangGraphClientConfig)),
117
117
  });
118
118
  await newClient.initAssistant(currentAgent.get());
119
119
  currentAgent.set(newClient.getCurrentAssistant()!.graph_id);
@@ -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.