@langgraph-js/sdk 3.7.0 → 3.8.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.
Files changed (45) hide show
  1. package/README.md +29 -0
  2. package/dist/History.d.ts +115 -0
  3. package/dist/History.js +226 -0
  4. package/dist/LangGraphClient.d.ts +23 -2
  5. package/dist/LangGraphClient.js +118 -80
  6. package/dist/MessageProcessor.js +18 -24
  7. package/dist/SpendTime.js +4 -9
  8. package/dist/TestKit.d.ts +1 -1
  9. package/dist/TestKit.js +16 -15
  10. package/dist/ToolManager.js +4 -7
  11. package/dist/artifacts/index.js +1 -1
  12. package/dist/client/LanggraphServer.js +1 -1
  13. package/dist/client/LowJSServer.d.ts +3 -0
  14. package/dist/client/LowJSServer.js +80 -0
  15. package/dist/client/index.d.ts +2 -0
  16. package/dist/client/index.js +2 -0
  17. package/dist/client/utils/sse.d.ts +8 -0
  18. package/dist/client/utils/sse.js +151 -0
  19. package/dist/client/utils/stream.d.ts +15 -0
  20. package/dist/client/utils/stream.js +104 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.js +2 -0
  23. package/dist/react/ChatContext.d.ts +31 -20
  24. package/dist/react/ChatContext.js +10 -4
  25. package/dist/tool/ToolUI.js +3 -2
  26. package/dist/tool/createTool.js +3 -6
  27. package/dist/tool/utils.js +3 -4
  28. package/dist/ui-store/createChatStore.d.ts +33 -66
  29. package/dist/ui-store/createChatStore.js +261 -247
  30. package/dist/vue/ChatContext.d.ts +41 -21
  31. package/dist/vue/ChatContext.js +8 -2
  32. package/package.json +3 -1
  33. package/src/History.ts +294 -0
  34. package/src/LangGraphClient.ts +98 -48
  35. package/src/client/LanggraphServer.ts +1 -2
  36. package/src/client/LowJSServer.ts +80 -0
  37. package/src/client/index.ts +2 -0
  38. package/src/client/utils/sse.ts +176 -0
  39. package/src/client/utils/stream.ts +114 -0
  40. package/src/index.ts +2 -0
  41. package/src/react/ChatContext.ts +25 -16
  42. package/src/ui-store/createChatStore.ts +310 -236
  43. package/src/vue/ChatContext.ts +12 -0
  44. package/test/TestKit.test.ts +10 -2
  45. package/tsconfig.json +1 -1
@@ -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.
@@ -32,7 +33,11 @@ export interface ChatProviderProps {
32
33
  showHistory?: boolean;
33
34
  showGraph?: boolean;
34
35
  fallbackToAvailableAssistants?: boolean;
36
+ /** 初始化时是否自动激活最近的历史会话(默认 false,创建新会话) */
37
+ autoRestoreLastSession?: boolean;
35
38
  onInitError?: (error: any, currentAgent: string) => void;
39
+ client?: ILangGraphClient;
40
+ legacyMode?: boolean;
36
41
  }
37
42
  /**
38
43
  * @zh Chat Provider Hook,用于在 setup 中直接使用
@@ -45,51 +50,57 @@ export declare const useChatProvider: (props: ChatProviderProps) => {
45
50
  currentArtifactId: PreinitializedWritableAtom<[string, string] | null> & object;
46
51
  showArtifact: PreinitializedWritableAtom<boolean> & object;
47
52
  client: PreinitializedWritableAtom<import("../LangGraphClient.js").LangGraphClient<unknown> | null> & object;
53
+ history: PreinitializedWritableAtom<import("../History.js").History | null> & object;
54
+ sessions: PreinitializedWritableAtom<import("../History.js").SessionInfo[]> & object;
48
55
  renderMessages: PreinitializedWritableAtom<import("../LangGraphClient.js").RenderMessage[]> & object;
49
56
  userInput: PreinitializedWritableAtom<string> & object;
50
57
  loading: PreinitializedWritableAtom<boolean> & object;
51
58
  inChatError: PreinitializedWritableAtom<string | null> & object;
52
59
  currentAgent: PreinitializedWritableAtom<string> & object;
60
+ currentChatId: PreinitializedWritableAtom<string | null> & object;
61
+ currentNodeName: PreinitializedWritableAtom<string> & object;
62
+ tools: PreinitializedWritableAtom<import("../index.js").UnionTool<any, Object, any>[]> & object;
53
63
  collapsedTools: PreinitializedWritableAtom<string[]> & object;
64
+ showGraph: PreinitializedWritableAtom<boolean> & object;
65
+ graphVisualize: PreinitializedWritableAtom<import("@langchain/langgraph-sdk").AssistantGraph | null> & object;
54
66
  showHistory: PreinitializedWritableAtom<boolean> & object;
55
67
  historyList: PreinitializedWritableAtom<import("@langchain/langgraph-sdk").Thread<{
56
68
  messages: import("@langchain/langgraph-sdk").Message[];
57
69
  }>[]> & object;
58
- currentChatId: PreinitializedWritableAtom<string | null> & object;
59
- showGraph: PreinitializedWritableAtom<boolean> & object;
60
- graphVisualize: PreinitializedWritableAtom<import("@langchain/langgraph-sdk").AssistantGraph | null> & object;
61
- currentNodeName: PreinitializedWritableAtom<string> & object;
62
- tools: PreinitializedWritableAtom<import("../index.js").UnionTool<any, Object, any>[]> & object;
63
70
  };
64
71
  mutations: {
65
72
  setCurrentArtifactById: (id: string, tool_id: string) => void;
66
73
  setShowArtifact: (show: boolean) => void;
67
- refreshTools: () => Promise<void>;
68
- setTools(new_tools: import("../index.js").UnionTool<any>[]): void;
69
- isFELocking(): boolean | undefined;
70
- getClient(): import("../LangGraphClient.js").LangGraphClient<unknown> | null;
71
- initClient: () => Promise<import("../LangGraphClient.js").LangGraphClient<unknown>>;
74
+ initClient: () => Promise<import("../History.js").History>;
75
+ getClient: () => import("../LangGraphClient.js").LangGraphClient<unknown> | null;
76
+ getHistory: () => import("../History.js").History | null;
77
+ activateSession: (sessionId: string) => Promise<void>;
78
+ createNewSession: () => Promise<void>;
79
+ refreshSessionList: () => Promise<void>;
80
+ refreshHistoryList: () => Promise<void>;
72
81
  sendMessage: (message?: import("@langchain/langgraph-sdk").Message[], extraData?: import("../LangGraphClient.js").SendMessageOptions, withoutCheck?: boolean) => Promise<void>;
73
82
  stopGeneration: () => void;
83
+ setUserInput: (input: string) => void;
84
+ revertChatTo(messageId: string, resend?: boolean, sendOptions?: import("../LangGraphClient.js").SendMessageOptions & import("../time-travel/index.js").RevertChatToOptions): Promise<void>;
85
+ refreshTools: () => Promise<void>;
86
+ setTools(new_tools: import("../index.js").UnionTool<any>[]): void;
74
87
  toggleToolCollapse: (toolId: string) => void;
88
+ getToolUIRender: (tool_name: string) => ((message: import("../LangGraphClient.js").RenderMessage) => Object) | null;
89
+ isFELocking: () => boolean | undefined;
75
90
  toggleHistoryVisible: () => void;
76
- refreshHistoryList: () => Promise<void>;
91
+ toggleGraphVisible(): void;
92
+ refreshGraph: () => Promise<void>;
93
+ setCurrentAgent(agent: string): Promise<import("../History.js").History>;
77
94
  addToHistory: (thread: import("@langchain/langgraph-sdk").Thread<{
78
95
  messages: import("@langchain/langgraph-sdk").Message[];
79
96
  }>) => void;
80
- revertChatTo(messageId: string, resend?: boolean, sendOptions?: import("../LangGraphClient.js").SendMessageOptions & import("../time-travel/index.js").RevertChatToOptions): Promise<void>;
81
- setUserInput(input: string): void;
82
- setCurrentAgent(agent: string): Promise<void>;
83
- toggleGraphVisible(): void;
84
- refreshGraph: () => Promise<void>;
85
- createNewChat(): void;
86
- toHistoryChat(thread: import("@langchain/langgraph-sdk").Thread<{
97
+ createNewChat: () => Promise<void>;
98
+ toHistoryChat: (thread: import("@langchain/langgraph-sdk").Thread<{
87
99
  messages: import("@langchain/langgraph-sdk").Message[];
88
- }>): Promise<import("@langchain/langgraph-sdk").Thread<unknown> | undefined>;
100
+ }>) => Promise<void>;
89
101
  deleteHistoryChat(thread: import("@langchain/langgraph-sdk").Thread<{
90
102
  messages: import("@langchain/langgraph-sdk").Message[];
91
103
  }>): Promise<void>;
92
- getToolUIRender: (tool_name: string) => ((message: import("../LangGraphClient.js").RenderMessage) => Object) | null;
93
104
  };
94
105
  }>;
95
106
  };
@@ -122,6 +133,10 @@ export declare const ChatProvider: import("vue").DefineComponent<import("vue").E
122
133
  type: PropType<boolean>;
123
134
  default: boolean;
124
135
  };
136
+ autoRestoreLastSession: {
137
+ type: PropType<boolean>;
138
+ default: boolean;
139
+ };
125
140
  onInitError: {
126
141
  type: PropType<(error: any, currentAgent: string) => void>;
127
142
  default: undefined;
@@ -153,6 +168,10 @@ export declare const ChatProvider: import("vue").DefineComponent<import("vue").E
153
168
  type: PropType<boolean>;
154
169
  default: boolean;
155
170
  };
171
+ autoRestoreLastSession: {
172
+ type: PropType<boolean>;
173
+ default: boolean;
174
+ };
156
175
  onInitError: {
157
176
  type: PropType<(error: any, currentAgent: string) => void>;
158
177
  default: undefined;
@@ -160,9 +179,10 @@ export declare const ChatProvider: import("vue").DefineComponent<import("vue").E
160
179
  }>> & Readonly<{}>, {
161
180
  apiUrl: string;
162
181
  defaultHeaders: Record<string, string>;
163
- showHistory: boolean;
164
182
  showGraph: boolean;
183
+ showHistory: boolean;
165
184
  defaultAgent: string;
166
185
  withCredentials: boolean;
186
+ autoRestoreLastSession: boolean;
167
187
  onInitError: (error: any, currentAgent: string) => void;
168
188
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -45,10 +45,13 @@ 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,
51
53
  fallbackToAvailableAssistants: props.fallbackToAvailableAssistants,
54
+ autoRestoreLastSession: props.autoRestoreLastSession,
52
55
  });
53
56
  const unionStore = useUnionStoreVue(store, useStore);
54
57
  // 提供 store 给子组件
@@ -104,6 +107,10 @@ export const ChatProvider = defineComponent({
104
107
  type: Boolean,
105
108
  default: false,
106
109
  },
110
+ autoRestoreLastSession: {
111
+ type: Boolean,
112
+ default: false,
113
+ },
107
114
  onInitError: {
108
115
  type: Function,
109
116
  default: undefined,
@@ -115,8 +122,7 @@ export const ChatProvider = defineComponent({
115
122
  unionStore,
116
123
  });
117
124
  return () => {
118
- var _a;
119
- return (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots);
125
+ return slots.default?.();
120
126
  };
121
127
  },
122
128
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langgraph-js/sdk",
3
- "version": "3.7.0",
3
+ "version": "3.8.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",
@@ -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/History.ts ADDED
@@ -0,0 +1,294 @@
1
+ import { LangGraphClient, LangGraphClientConfig } from "./LangGraphClient.js";
2
+ import type { Thread } from "@langchain/langgraph-sdk";
3
+
4
+ export interface SessionInfo {
5
+ /** 会话唯一标识,同时也是 threadId */
6
+ sessionId: string;
7
+ /** LangGraphClient 实例(懒加载) */
8
+ client?: LangGraphClient;
9
+ /** Thread 信息(懒加载,第一条消息后才创建) */
10
+ thread?: Thread<any>;
11
+ /** Agent 名称 */
12
+ agentName: string;
13
+ }
14
+
15
+ export interface CreateSessionOptions {
16
+ /** 会话 ID / Thread ID,不提供则自动生成 */
17
+ sessionId?: string;
18
+ /** Agent 名称 */
19
+ agentName?: string;
20
+ /** 是否从已有 Thread 恢复会话 */
21
+ restore?: boolean;
22
+ /** Graph ID */
23
+ graphId?: string;
24
+ }
25
+
26
+ /**
27
+ * @zh History 类用于管理多个 LangGraphClient 实例,支持多会话场景
28
+ * @en History class manages multiple LangGraphClient instances for multi-session scenarios
29
+ */
30
+ export class History {
31
+ /** 存储所有会话的 Map */
32
+ private sessions: Map<string, SessionInfo> = new Map();
33
+ /** 当前活跃的会话 ID */
34
+ private activeSessionId: string | null = null;
35
+ /** 客户端配置,用于创建新的 LangGraphClient 实例 */
36
+ private clientConfig: LangGraphClientConfig;
37
+ /** 虚拟 Client,用于查询操作(不绑定特定 Thread) */
38
+ private virtualClient: LangGraphClient;
39
+
40
+ constructor(clientConfig: LangGraphClientConfig) {
41
+ this.clientConfig = clientConfig;
42
+ this.virtualClient = new LangGraphClient(clientConfig);
43
+ }
44
+
45
+ /**
46
+ * @zh 创建新会话(延迟创建 Thread,直到发送第一条消息)
47
+ * @en Creates a new session (lazy Thread creation until first message)
48
+ */
49
+ async createSession(options: CreateSessionOptions = {}): Promise<SessionInfo> {
50
+ const sessionId = options.sessionId || this.generateSessionId();
51
+ const agentName = options.agentName || this.virtualClient.getCurrentAssistant()?.graph_id;
52
+
53
+ if (!agentName) {
54
+ throw new Error("Agent name is required. Please call init() first or provide agentName.");
55
+ }
56
+
57
+ if (this.sessions.has(sessionId)) {
58
+ throw new Error(`Session ${sessionId} already exists`);
59
+ }
60
+
61
+ const sessionInfo: SessionInfo = {
62
+ sessionId,
63
+ agentName,
64
+ };
65
+
66
+ // 如果是从已有 Thread 恢复,则立即获取 Thread
67
+ if (options.restore) {
68
+ sessionInfo.thread = (await this.virtualClient.threads.get(sessionId)) as Thread<any>;
69
+ }
70
+ // 否则 thread 为 undefined,等待第一条消息时由 Client 自动创建
71
+
72
+ this.sessions.set(sessionId, sessionInfo);
73
+
74
+ return sessionInfo;
75
+ }
76
+
77
+ /**
78
+ * @zh 激活指定会话(懒加载创建 Client)
79
+ * @en Activates the specified session (lazy load client)
80
+ */
81
+ async activateSession(sessionId: string): Promise<SessionInfo> {
82
+ const session = this.sessions.get(sessionId);
83
+ if (!session) {
84
+ throw new Error(`Session ${sessionId} not found`);
85
+ }
86
+
87
+ // 懒加载:只在激活时创建 Client
88
+ if (!session.client) {
89
+ const client = new LangGraphClient(this.clientConfig);
90
+ await client.initAssistant(session.agentName);
91
+
92
+ // 只有在有 thread 的情况下才重置(恢复已有会话)
93
+ // 新会话的 thread 会在发送第一条消息时自动创建
94
+ if (session.thread) {
95
+ await client.resetThread(session.agentName, sessionId);
96
+ }
97
+
98
+ session.client = client;
99
+ }
100
+ const lastSession = this.activeSessionId && this.sessions.get(this.activeSessionId);
101
+ // 空闲的 client 就需要销毁
102
+ if (lastSession && lastSession.client?.status === "idle") {
103
+ lastSession.client?.reset();
104
+ lastSession.client = undefined;
105
+ }
106
+ this.activeSessionId = sessionId;
107
+ return session;
108
+ }
109
+
110
+ /**
111
+ * @zh 获取当前活跃的会话
112
+ * @en Gets the current active session
113
+ */
114
+ getActiveSession(): SessionInfo | null {
115
+ if (!this.activeSessionId) {
116
+ return null;
117
+ }
118
+ return this.sessions.get(this.activeSessionId) || null;
119
+ }
120
+
121
+ /**
122
+ * @zh 获取指定会话
123
+ * @en Gets the specified session
124
+ */
125
+ getSession(sessionId: string): SessionInfo | null {
126
+ return this.sessions.get(sessionId) || null;
127
+ }
128
+
129
+ /**
130
+ * @zh 获取所有会话
131
+ * @en Gets all sessions
132
+ */
133
+ getAllSessions(): SessionInfo[] {
134
+ return Array.from(this.sessions.values());
135
+ }
136
+
137
+ /**
138
+ * @zh 删除指定会话
139
+ * @en Deletes the specified session
140
+ */
141
+ async deleteSession(sessionId: string): Promise<boolean> {
142
+ const session = this.sessions.get(sessionId);
143
+ if (!session) {
144
+ return false;
145
+ }
146
+
147
+ // 如果删除的是当前活跃会话,清空活跃会话
148
+ if (this.activeSessionId === sessionId) {
149
+ this.activeSessionId = null;
150
+ }
151
+
152
+ // 删除对应的远程 Thread
153
+ try {
154
+ await this.virtualClient.deleteThread(sessionId);
155
+ } catch (error) {
156
+ console.warn(`Failed to delete thread for session ${sessionId}:`, error);
157
+ }
158
+
159
+ this.sessions.delete(sessionId);
160
+ return true;
161
+ }
162
+
163
+ /**
164
+ * @zh 清空所有会话
165
+ * @en Clears all sessions
166
+ */
167
+ async clearAllSessions(): Promise<void> {
168
+ const sessionIds = Array.from(this.sessions.keys());
169
+ for (const sessionId of sessionIds) {
170
+ await this.deleteSession(sessionId);
171
+ }
172
+ this.activeSessionId = null;
173
+ }
174
+
175
+ /**
176
+ * @zh 获取会话数量
177
+ * @en Gets the number of sessions
178
+ */
179
+ getSessionCount(): number {
180
+ return this.sessions.size;
181
+ }
182
+
183
+ /**
184
+ * @zh 生成会话 ID (UUID v4 格式)
185
+ * @en Generates a session ID (UUID v4 format)
186
+ */
187
+ private generateSessionId(): string {
188
+ // 优先使用 crypto.randomUUID (Node.js 15.6+, 浏览器支持)
189
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
190
+ return crypto.randomUUID();
191
+ }
192
+
193
+ // 降级方案:生成符合 UUID v4 格式的随机 ID
194
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
195
+ const r = (Math.random() * 16) | 0;
196
+ const v = c === "x" ? r : (r & 0x3) | 0x8;
197
+ return v.toString(16);
198
+ });
199
+ }
200
+
201
+ /**
202
+ * @zh 从已有的 Thread 添加会话(仅添加元数据,不创建 Client)
203
+ * @en Adds a session from an existing Thread (metadata only, no client created)
204
+ */
205
+ async addSessionFromThread(threadId: string, agentName?: string): Promise<SessionInfo> {
206
+ const agent = agentName || this.virtualClient.getCurrentAssistant()?.graph_id;
207
+ if (!agent) {
208
+ throw new Error("Agent name is required. Please call init() first or provide agentName.");
209
+ }
210
+
211
+ return this.createSession({
212
+ sessionId: threadId,
213
+ agentName: agent,
214
+ restore: true,
215
+ });
216
+ }
217
+
218
+ /**
219
+ * @zh 获取当前活跃的客户端
220
+ * @en Gets the current active client
221
+ */
222
+ getActiveClient(): LangGraphClient | null {
223
+ const session = this.getActiveSession();
224
+ return session?.client || null;
225
+ }
226
+
227
+ /**
228
+ * @zh 从远程列出所有会话
229
+ * @en Lists all sessions from remote
230
+ */
231
+ async listRemoteSessions(
232
+ options: {
233
+ sortOrder?: "asc" | "desc";
234
+ sortBy?: "created_at" | "updated_at";
235
+ offset?: number;
236
+ limit?: number;
237
+ } = {}
238
+ ) {
239
+ return this.virtualClient.listThreads(options);
240
+ }
241
+
242
+ /**
243
+ * @zh 从远程同步会话到本地(仅同步元数据,不创建 Client)
244
+ * @en Syncs sessions from remote to local (metadata only, no client created)
245
+ */
246
+ async syncFromRemote(
247
+ options: {
248
+ limit?: number;
249
+ agentName?: string;
250
+ } = {}
251
+ ): Promise<SessionInfo[]> {
252
+ const agentName = options.agentName || this.virtualClient.getCurrentAssistant()?.graph_id;
253
+ if (!agentName) {
254
+ throw new Error("Agent name is required. Please call init() first or provide agentName.");
255
+ }
256
+
257
+ const threads = await this.listRemoteSessions({
258
+ limit: options.limit || 100,
259
+ sortBy: "updated_at",
260
+ sortOrder: "desc",
261
+ });
262
+
263
+ const syncedSessions: SessionInfo[] = [];
264
+
265
+ for (const thread of threads) {
266
+ const threadId = thread.thread_id;
267
+
268
+ // 更新或创建会话信息
269
+ if (this.sessions.has(threadId)) {
270
+ const session = this.sessions.get(threadId)!;
271
+ session.thread = thread;
272
+ syncedSessions.push(session);
273
+ } else {
274
+ const sessionInfo: SessionInfo = {
275
+ sessionId: threadId,
276
+ thread,
277
+ agentName,
278
+ };
279
+ this.sessions.set(threadId, sessionInfo);
280
+ syncedSessions.push(sessionInfo);
281
+ }
282
+ }
283
+
284
+ return syncedSessions;
285
+ }
286
+
287
+ /**
288
+ * @zh 初始化 History(必须先调用)
289
+ * @en Initializes History (must be called first)
290
+ */
291
+ async init(agentName?: string) {
292
+ return this.virtualClient.initAssistant(agentName);
293
+ }
294
+ }