@langgraph-js/sdk 3.7.1 → 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.
- package/dist/History.d.ts +115 -0
- package/dist/History.js +226 -0
- package/dist/LangGraphClient.d.ts +10 -1
- package/dist/LangGraphClient.js +17 -3
- package/dist/TestKit.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/react/ChatContext.d.ts +28 -20
- package/dist/react/ChatContext.js +3 -2
- package/dist/ui-store/createChatStore.d.ts +33 -66
- package/dist/ui-store/createChatStore.js +255 -225
- package/dist/vue/ChatContext.d.ts +38 -21
- package/dist/vue/ChatContext.js +5 -0
- package/package.json +1 -1
- package/src/History.ts +294 -0
- package/src/LangGraphClient.ts +25 -3
- package/src/index.ts +1 -0
- package/src/react/ChatContext.ts +5 -1
- package/src/ui-store/createChatStore.ts +310 -236
- package/src/vue/ChatContext.ts +7 -0
|
@@ -7,27 +7,13 @@ import { UnionTool } from "../tool/createTool.js";
|
|
|
7
7
|
import { createLangGraphServerClient } from "../client/LanggraphServer.js";
|
|
8
8
|
import { useArtifacts } from "../artifacts/index.js";
|
|
9
9
|
import { RevertChatToOptions } from "../time-travel/index.js";
|
|
10
|
+
import { History, SessionInfo } from "../History.js";
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
* @zh 格式化日期对象为时间字符串。
|
|
13
|
-
* @en Formats a Date object into a time string.
|
|
14
|
-
*/
|
|
15
|
-
export const formatTime = (date: Date) => {
|
|
16
|
-
return date.toLocaleTimeString();
|
|
17
|
-
};
|
|
12
|
+
// ============ 工具函数 ============
|
|
18
13
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* @en Formats a number into a string with thousand separators.
|
|
22
|
-
*/
|
|
23
|
-
export const formatTokens = (tokens: number) => {
|
|
24
|
-
return tokens.toLocaleString("en");
|
|
25
|
-
};
|
|
14
|
+
export const formatTime = (date: Date) => date.toLocaleTimeString();
|
|
15
|
+
export const formatTokens = (tokens: number) => tokens.toLocaleString("en");
|
|
26
16
|
|
|
27
|
-
/**
|
|
28
|
-
* @zh 获取消息内容的文本表示,处理不同类型的消息内容。
|
|
29
|
-
* @en Gets the text representation of message content, handling different types of message content.
|
|
30
|
-
*/
|
|
31
17
|
export const getMessageContent = (content: any) => {
|
|
32
18
|
if (typeof content === "string") return content;
|
|
33
19
|
if (Array.isArray(content)) {
|
|
@@ -43,52 +29,81 @@ export const getMessageContent = (content: any) => {
|
|
|
43
29
|
return JSON.stringify(content);
|
|
44
30
|
};
|
|
45
31
|
|
|
46
|
-
/**
|
|
47
|
-
* @zh 获取历史记录中 Thread 内容的文本表示。
|
|
48
|
-
* @en Gets the text representation of Thread content in history.
|
|
49
|
-
*/
|
|
50
32
|
export const getHistoryContent = (thread: Thread) => {
|
|
51
33
|
/** @ts-ignore */
|
|
52
34
|
const content: string | any[] = thread.title || thread.name || (thread?.values as any)?.messages?.[0]?.content;
|
|
53
35
|
if (content && Array.isArray(content)) {
|
|
54
|
-
return content.map((item: any) =>
|
|
55
|
-
if (item.type === "text") {
|
|
56
|
-
return item.text;
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
} else if (typeof content === "string") {
|
|
60
|
-
return content;
|
|
61
|
-
} else {
|
|
62
|
-
return "";
|
|
36
|
+
return content.map((item: any) => (item.type === "text" ? item.text : undefined)).filter(Boolean);
|
|
63
37
|
}
|
|
38
|
+
return typeof content === "string" ? content : "";
|
|
64
39
|
};
|
|
65
40
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
) => {
|
|
41
|
+
// ============ 类型定义 ============
|
|
42
|
+
|
|
43
|
+
interface ChatStoreContext {
|
|
44
|
+
showHistory?: boolean;
|
|
45
|
+
showGraph?: boolean;
|
|
46
|
+
fallbackToAvailableAssistants?: boolean;
|
|
47
|
+
onInit?: (client: LangGraphClient) => void;
|
|
48
|
+
/** 初始化时是否自动激活最近的历史会话(默认 false,创建新会话) */
|
|
49
|
+
autoRestoreLastSession?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ============ Store 创建函数 ============
|
|
53
|
+
|
|
54
|
+
export const createChatStore = (initClientName: string, config: Partial<LangGraphClientConfig>, context: ChatStoreContext = {}) => {
|
|
55
|
+
// ============ 状态原子 ============
|
|
56
|
+
|
|
57
|
+
// 会话管理
|
|
58
|
+
const history = atom<History | null>(null);
|
|
59
|
+
const sessions = atom<SessionInfo[]>([]);
|
|
80
60
|
const client = atom<LangGraphClient | null>(null);
|
|
61
|
+
const historyList = atom<Thread<{ messages: Message[] }>[]>([]);
|
|
62
|
+
|
|
63
|
+
// UI 状态
|
|
81
64
|
const renderMessages = atom<RenderMessage[]>([]);
|
|
82
65
|
const userInput = atom<string>("");
|
|
83
|
-
const loading = atom<boolean>(false);
|
|
84
|
-
const collapsedTools = atom<string[]>([]);
|
|
85
66
|
const inChatError = atom<string | null>(null);
|
|
86
|
-
const showHistory = atom<boolean>(context.showHistory ?? false);
|
|
87
67
|
const currentAgent = atom<string>(initClientName);
|
|
88
68
|
const currentChatId = atom<string | null>(null);
|
|
89
69
|
const currentNodeName = atom<string>("__start__");
|
|
90
70
|
|
|
71
|
+
// 工具和图表
|
|
91
72
|
const tools = atom<UnionTool<any>[]>([]);
|
|
73
|
+
const collapsedTools = atom<string[]>([]);
|
|
74
|
+
const showHistory = atom<boolean>(context.showHistory ?? false);
|
|
75
|
+
const showGraph = atom<boolean>(context.showGraph ?? false);
|
|
76
|
+
const graphVisualize = atom<AssistantGraph | null>(null);
|
|
77
|
+
|
|
78
|
+
// ============ 内部状态 ============
|
|
79
|
+
|
|
80
|
+
let cleanupCurrentClient: (() => void) | null = null;
|
|
81
|
+
|
|
82
|
+
// ============ 计算属性 ============
|
|
83
|
+
|
|
84
|
+
/** 基于 client.status 的 loading 状态 */
|
|
85
|
+
const loading = atom<boolean>(false);
|
|
86
|
+
|
|
87
|
+
const updateLoadingFromClientStatus = () => {
|
|
88
|
+
const c = client.get();
|
|
89
|
+
if (c) {
|
|
90
|
+
loading.set(c.status === "busy");
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// ============ UI 更新逻辑 ============
|
|
95
|
+
|
|
96
|
+
const updateUI = debounce((newClient: LangGraphClient) => {
|
|
97
|
+
if (client.get() !== newClient) return;
|
|
98
|
+
|
|
99
|
+
const messages = newClient.renderMessage;
|
|
100
|
+
const lastMessage = messages[messages.length - 1];
|
|
101
|
+
|
|
102
|
+
currentNodeName.set(lastMessage?.node_name || lastMessage?.name || "__start__");
|
|
103
|
+
renderMessages.set(messages);
|
|
104
|
+
}, 10);
|
|
105
|
+
// ============ 工具和图表辅助函数 ============
|
|
106
|
+
|
|
92
107
|
const refreshTools = async () => {
|
|
93
108
|
const c = client.get();
|
|
94
109
|
if (!c) return;
|
|
@@ -96,201 +111,284 @@ export const createChatStore = (
|
|
|
96
111
|
c.tools.bindTools(tools.get());
|
|
97
112
|
};
|
|
98
113
|
|
|
99
|
-
// 显示 langgraph 可视化图
|
|
100
|
-
const showGraph = atom<boolean>(context.showGraph ?? false);
|
|
101
|
-
const graphVisualize = atom<AssistantGraph | null>(null);
|
|
102
114
|
const refreshGraph = async () => {
|
|
103
|
-
if (showGraph.get())
|
|
115
|
+
if (showGraph.get()) {
|
|
116
|
+
graphVisualize.set((await client.get()?.graphVisualize()) || null);
|
|
117
|
+
}
|
|
104
118
|
};
|
|
105
|
-
const updateUI = debounce((newClient: LangGraphClient) => {
|
|
106
|
-
const messages = newClient.renderMessage;
|
|
107
|
-
const lastMessage = messages[messages.length - 1];
|
|
108
119
|
|
|
109
|
-
|
|
120
|
+
// ============ 会话管理核心逻辑 ============
|
|
110
121
|
|
|
111
|
-
renderMessages.set(messages);
|
|
112
|
-
}, 10);
|
|
113
|
-
/**
|
|
114
|
-
* @zh 初始化 LangGraph 客户端。
|
|
115
|
-
* @en Initializes the LangGraph client.
|
|
116
|
-
*/
|
|
117
122
|
async function initClient() {
|
|
118
|
-
const
|
|
123
|
+
const historyManager = new History({
|
|
119
124
|
...config,
|
|
120
125
|
client: config.client ?? (await createLangGraphServerClient(config as LangGraphClientConfig)),
|
|
121
126
|
});
|
|
122
|
-
await
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
await historyManager.init(currentAgent.get());
|
|
128
|
+
history.set(historyManager);
|
|
129
|
+
|
|
130
|
+
// 同步远程会话列表
|
|
131
|
+
await refreshSessionList();
|
|
132
|
+
|
|
133
|
+
// 根据配置决定初始化行为
|
|
134
|
+
const syncedSessions = sessions.get();
|
|
135
|
+
if (context.autoRestoreLastSession && syncedSessions.length > 0) {
|
|
136
|
+
// 自动激活最近的历史会话
|
|
137
|
+
await activateSession(syncedSessions[0].sessionId);
|
|
138
|
+
} else {
|
|
139
|
+
// 创建新会话
|
|
140
|
+
await createNewSession();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return historyManager;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function refreshSessionList() {
|
|
147
|
+
const historyManager = history.get();
|
|
148
|
+
if (!historyManager) return;
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const syncedSessions = await historyManager.syncFromRemote({ limit: 100 });
|
|
152
|
+
sessions.set(syncedSessions);
|
|
153
|
+
historyList.set(syncedSessions.filter((s) => s.thread).map((s) => s.thread!));
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.error("Failed to sync sessions:", error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function createNewSession() {
|
|
160
|
+
const historyManager = history.get();
|
|
161
|
+
if (!historyManager) return;
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const session = await historyManager.createSession();
|
|
165
|
+
await refreshSessionList();
|
|
166
|
+
await activateSession(session.sessionId);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error("Failed to create new session:", error);
|
|
169
|
+
inChatError.set((error as Error).message);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ============ 客户端事件监听器 ============
|
|
174
|
+
|
|
175
|
+
function setupClientListeners(newClient: LangGraphClient) {
|
|
176
|
+
const isActiveClient = () => client.get() === newClient;
|
|
131
177
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
178
|
+
const onStart = () => {
|
|
179
|
+
if (isActiveClient()) updateLoadingFromClientStatus();
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const onThread = () => {
|
|
183
|
+
if (!isActiveClient()) return;
|
|
184
|
+
|
|
185
|
+
const thread = newClient.getCurrentThread();
|
|
186
|
+
currentChatId.set(thread?.thread_id || null);
|
|
136
187
|
currentNodeName.set("__start__");
|
|
137
|
-
// 创建新会话时,需要自动刷新历史面板
|
|
138
|
-
refreshHistoryList();
|
|
139
|
-
});
|
|
140
188
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
189
|
+
const historyManager = history.get();
|
|
190
|
+
const activeSession = historyManager?.getActiveSession();
|
|
191
|
+
if (activeSession && thread) {
|
|
192
|
+
activeSession.thread = thread;
|
|
193
|
+
}
|
|
145
194
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
loading.set(false);
|
|
149
|
-
inChatError.set(event.data);
|
|
150
|
-
});
|
|
195
|
+
refreshSessionList();
|
|
196
|
+
};
|
|
151
197
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
198
|
+
const onDone = () => {
|
|
199
|
+
if (isActiveClient()) {
|
|
200
|
+
updateLoadingFromClientStatus();
|
|
201
|
+
updateUI(newClient);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
157
204
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
205
|
+
const onError = (event: any) => {
|
|
206
|
+
if (isActiveClient()) {
|
|
207
|
+
updateLoadingFromClientStatus();
|
|
208
|
+
inChatError.set(event.data);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const onMessage = () => {
|
|
213
|
+
if (isActiveClient()) {
|
|
214
|
+
currentChatId.set(newClient.getCurrentThread()?.thread_id || null);
|
|
215
|
+
updateLoadingFromClientStatus();
|
|
216
|
+
updateUI(newClient);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const onValue = () => {
|
|
221
|
+
if (isActiveClient()) {
|
|
222
|
+
currentChatId.set(newClient.getCurrentThread()?.thread_id || null);
|
|
223
|
+
updateLoadingFromClientStatus();
|
|
224
|
+
updateUI(newClient);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
newClient.on("start", onStart);
|
|
229
|
+
newClient.on("thread", onThread);
|
|
230
|
+
newClient.on("done", onDone);
|
|
231
|
+
newClient.on("error", onError);
|
|
232
|
+
newClient.on("message", onMessage);
|
|
233
|
+
newClient.on("value", onValue);
|
|
234
|
+
|
|
235
|
+
return () => {
|
|
236
|
+
newClient.off("start", onStart);
|
|
237
|
+
newClient.off("thread", onThread);
|
|
238
|
+
newClient.off("done", onDone);
|
|
239
|
+
newClient.off("error", onError);
|
|
240
|
+
newClient.off("message", onMessage);
|
|
241
|
+
newClient.off("value", onValue);
|
|
242
|
+
};
|
|
168
243
|
}
|
|
169
244
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
245
|
+
// ============ 会话激活逻辑 ============
|
|
246
|
+
|
|
247
|
+
async function activateSession(sessionId: string) {
|
|
248
|
+
const historyManager = history.get();
|
|
249
|
+
if (!historyManager) return;
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
if (cleanupCurrentClient) {
|
|
253
|
+
cleanupCurrentClient();
|
|
254
|
+
cleanupCurrentClient = null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
inChatError.set(null);
|
|
258
|
+
|
|
259
|
+
const session = await historyManager.activateSession(sessionId);
|
|
260
|
+
const activeClient = session.client;
|
|
261
|
+
|
|
262
|
+
if (activeClient) {
|
|
263
|
+
cleanupCurrentClient = setupClientListeners(activeClient);
|
|
264
|
+
context.onInit?.(activeClient);
|
|
265
|
+
client.set(activeClient);
|
|
266
|
+
currentChatId.set(sessionId);
|
|
267
|
+
|
|
268
|
+
const messages = activeClient.renderMessage;
|
|
269
|
+
renderMessages.set(messages);
|
|
270
|
+
|
|
271
|
+
const lastMessage = messages[messages.length - 1];
|
|
272
|
+
currentNodeName.set(lastMessage?.node_name || lastMessage?.name || "__start__");
|
|
273
|
+
|
|
274
|
+
updateLoadingFromClientStatus();
|
|
275
|
+
|
|
276
|
+
if (showGraph.get()) refreshGraph();
|
|
277
|
+
refreshTools();
|
|
278
|
+
|
|
279
|
+
const currentThread = activeClient.getCurrentThread() as any;
|
|
280
|
+
if (currentThread && (currentThread.status === "running" || currentThread.status === "pending")) {
|
|
281
|
+
await activeClient.resetStream();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error("Failed to activate session:", error);
|
|
286
|
+
inChatError.set((error as Error).message);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ============ 消息和交互逻辑 ============
|
|
291
|
+
|
|
292
|
+
async function sendMessage(message?: Message[], extraData?: SendMessageOptions, withoutCheck = false) {
|
|
293
|
+
const c = client.get();
|
|
294
|
+
if ((!withoutCheck && !userInput.get().trim() && !message?.length) || !c) return;
|
|
295
|
+
|
|
296
|
+
// 使用 client.status 判断是否正在加载
|
|
297
|
+
if (c.status === "busy") return;
|
|
176
298
|
|
|
177
|
-
loading.set(true);
|
|
178
299
|
inChatError.set(null);
|
|
179
300
|
try {
|
|
180
|
-
await
|
|
301
|
+
await c.sendMessage(message || userInput.get(), extraData);
|
|
181
302
|
} catch (e) {
|
|
182
303
|
const isThreadRunning = (e as Error).message.includes("422");
|
|
183
304
|
if (isThreadRunning) {
|
|
184
|
-
await
|
|
305
|
+
await c.resetStream();
|
|
185
306
|
} else {
|
|
186
307
|
throw e;
|
|
187
308
|
}
|
|
188
309
|
} finally {
|
|
189
310
|
userInput.set("");
|
|
190
|
-
|
|
311
|
+
updateLoadingFromClientStatus();
|
|
191
312
|
}
|
|
192
|
-
}
|
|
313
|
+
}
|
|
193
314
|
|
|
194
|
-
|
|
195
|
-
* @zh 停止当前的消息生成。
|
|
196
|
-
* @en Stops the current message generation.
|
|
197
|
-
*/
|
|
198
|
-
const stopGeneration = () => {
|
|
315
|
+
function stopGeneration() {
|
|
199
316
|
client.get()?.cancelRun();
|
|
200
|
-
}
|
|
317
|
+
}
|
|
201
318
|
|
|
202
|
-
|
|
203
|
-
* @zh 切换工具消息的折叠状态。
|
|
204
|
-
* @en Toggles the collapsed state of a tool message.
|
|
205
|
-
*/
|
|
206
|
-
const toggleToolCollapse = (toolId: string) => {
|
|
319
|
+
function toggleToolCollapse(toolId: string) {
|
|
207
320
|
const prev = collapsedTools.get();
|
|
208
321
|
collapsedTools.set(prev.includes(toolId) ? prev.filter((id) => id !== toolId) : [...prev, toolId]);
|
|
209
|
-
}
|
|
322
|
+
}
|
|
210
323
|
|
|
211
|
-
|
|
212
|
-
* @zh 切换历史记录面板的可见性。
|
|
213
|
-
* @en Toggles the visibility of the history panel.
|
|
214
|
-
*/
|
|
215
|
-
const toggleHistoryVisible = () => {
|
|
324
|
+
function toggleHistoryVisible() {
|
|
216
325
|
showHistory.set(!showHistory.get());
|
|
217
326
|
if (showHistory.get()) {
|
|
218
|
-
|
|
327
|
+
refreshSessionList();
|
|
219
328
|
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const historyList = atom<Thread<{ messages: Message[] }>[]>([]);
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* @zh 刷新历史记录列表。
|
|
226
|
-
* @en Refreshes the history list.
|
|
227
|
-
*/
|
|
228
|
-
const refreshHistoryList = async () => {
|
|
229
|
-
if (!client.get() || !showHistory.get()) return;
|
|
230
|
-
try {
|
|
231
|
-
const response = await client.get()?.listThreads();
|
|
232
|
-
historyList.set((response as Thread<{ messages: Message[] }>[]) || []);
|
|
233
|
-
} catch (error) {
|
|
234
|
-
console.error("Failed to fetch threads:", error);
|
|
235
|
-
}
|
|
236
|
-
};
|
|
329
|
+
}
|
|
237
330
|
|
|
238
|
-
|
|
239
|
-
* @zh 将一个 Thread 添加到历史记录列表的开头。
|
|
240
|
-
* @en Adds a Thread to the beginning of the history list.
|
|
241
|
-
*/
|
|
242
|
-
const addToHistory = (thread: Thread<{ messages: Message[] }>) => {
|
|
331
|
+
function addToHistory(thread: Thread<{ messages: Message[] }>) {
|
|
243
332
|
const prev = historyList.get();
|
|
244
333
|
historyList.set([thread, ...prev]);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function getToolUIRender(tool_name: string) {
|
|
337
|
+
const c = client.get();
|
|
338
|
+
if (!c) return null;
|
|
339
|
+
const toolsDefine = c.tools.getAllTools();
|
|
248
340
|
const tool = toolsDefine.find((i) => i.name === tool_name!)?.render;
|
|
249
|
-
return tool ? (message: RenderMessage) => tool(new ToolRenderData(message,
|
|
250
|
-
}
|
|
341
|
+
return tool ? (message: RenderMessage) => tool(new ToolRenderData(message, c)) : null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ============ 返回 Store API ============
|
|
251
345
|
|
|
252
346
|
const artifactHook = useArtifacts(renderMessages, client);
|
|
347
|
+
|
|
253
348
|
return {
|
|
254
349
|
data: {
|
|
350
|
+
// 核心客户端
|
|
255
351
|
client,
|
|
352
|
+
history,
|
|
353
|
+
sessions,
|
|
354
|
+
|
|
355
|
+
// UI 状态
|
|
256
356
|
renderMessages,
|
|
257
357
|
userInput,
|
|
258
358
|
loading,
|
|
259
359
|
inChatError,
|
|
260
360
|
currentAgent,
|
|
261
|
-
collapsedTools,
|
|
262
|
-
showHistory,
|
|
263
|
-
historyList,
|
|
264
361
|
currentChatId,
|
|
265
|
-
showGraph,
|
|
266
|
-
graphVisualize,
|
|
267
362
|
currentNodeName,
|
|
363
|
+
|
|
364
|
+
// 工具和图表
|
|
268
365
|
tools,
|
|
366
|
+
collapsedTools,
|
|
367
|
+
showGraph,
|
|
368
|
+
graphVisualize,
|
|
369
|
+
|
|
370
|
+
// 历史记录
|
|
371
|
+
showHistory,
|
|
372
|
+
historyList,
|
|
373
|
+
|
|
269
374
|
...artifactHook.data,
|
|
270
375
|
},
|
|
271
376
|
mutations: {
|
|
272
|
-
|
|
273
|
-
setTools(new_tools: UnionTool<any>[]) {
|
|
274
|
-
tools.set(new_tools);
|
|
275
|
-
refreshTools();
|
|
276
|
-
},
|
|
277
|
-
isFELocking() {
|
|
278
|
-
return client.get()?.isFELocking(renderMessages.get());
|
|
279
|
-
},
|
|
280
|
-
getClient() {
|
|
281
|
-
return client.get();
|
|
282
|
-
},
|
|
377
|
+
// 初始化
|
|
283
378
|
initClient,
|
|
379
|
+
getClient: () => client.get(),
|
|
380
|
+
getHistory: () => history.get(),
|
|
381
|
+
|
|
382
|
+
// 会话管理
|
|
383
|
+
activateSession,
|
|
384
|
+
createNewSession,
|
|
385
|
+
refreshSessionList,
|
|
386
|
+
refreshHistoryList: refreshSessionList, // 向后兼容
|
|
387
|
+
|
|
388
|
+
// 消息操作
|
|
284
389
|
sendMessage,
|
|
285
390
|
stopGeneration,
|
|
286
|
-
|
|
287
|
-
toggleHistoryVisible,
|
|
288
|
-
refreshHistoryList,
|
|
289
|
-
addToHistory,
|
|
290
|
-
/**
|
|
291
|
-
* @zh 回滚到指定的消息。
|
|
292
|
-
* @en Reverts to the specified message.
|
|
293
|
-
*/
|
|
391
|
+
setUserInput: (input: string) => userInput.set(input),
|
|
294
392
|
async revertChatTo(messageId: string, resend = false, sendOptions?: SendMessageOptions & RevertChatToOptions) {
|
|
295
393
|
await client.get()?.revertChatTo(messageId, sendOptions || {});
|
|
296
394
|
if (resend) {
|
|
@@ -299,67 +397,43 @@ export const createChatStore = (
|
|
|
299
397
|
updateUI(client.get()!);
|
|
300
398
|
}
|
|
301
399
|
},
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
},
|
|
309
|
-
/**
|
|
310
|
-
* @zh 设置当前的 Agent 并重新初始化客户端。
|
|
311
|
-
* @en Sets the current Agent and reinitializes the client.
|
|
312
|
-
*/
|
|
313
|
-
setCurrentAgent(agent: string) {
|
|
314
|
-
currentAgent.set(agent);
|
|
315
|
-
return initClient().then(() => {
|
|
316
|
-
if (showHistory.get()) {
|
|
317
|
-
refreshHistoryList();
|
|
318
|
-
}
|
|
319
|
-
});
|
|
400
|
+
|
|
401
|
+
// 工具操作
|
|
402
|
+
refreshTools,
|
|
403
|
+
setTools(new_tools: UnionTool<any>[]) {
|
|
404
|
+
tools.set(new_tools);
|
|
405
|
+
refreshTools();
|
|
320
406
|
},
|
|
407
|
+
toggleToolCollapse,
|
|
408
|
+
getToolUIRender,
|
|
409
|
+
isFELocking: () => client.get()?.isFELocking(renderMessages.get()),
|
|
410
|
+
|
|
411
|
+
// UI 切换
|
|
412
|
+
toggleHistoryVisible,
|
|
321
413
|
toggleGraphVisible() {
|
|
322
414
|
showGraph.set(!showGraph.get());
|
|
323
|
-
if (showGraph.get())
|
|
324
|
-
refreshGraph();
|
|
325
|
-
}
|
|
415
|
+
if (showGraph.get()) refreshGraph();
|
|
326
416
|
},
|
|
327
417
|
refreshGraph,
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
client.get()?.reset();
|
|
334
|
-
inChatError.set(null);
|
|
335
|
-
loading.set(false);
|
|
336
|
-
},
|
|
337
|
-
/**
|
|
338
|
-
* @zh 切换到指定的历史聊天会话。
|
|
339
|
-
* @en Switches to the specified historical chat session.
|
|
340
|
-
*/
|
|
341
|
-
async toHistoryChat(
|
|
342
|
-
thread: Thread<{
|
|
343
|
-
messages: Message[];
|
|
344
|
-
}>
|
|
345
|
-
) {
|
|
346
|
-
inChatError.set(null);
|
|
347
|
-
loading.set(false);
|
|
348
|
-
const nowThread = await client.get()?.resetThread(thread.metadata?.graph_id as string, thread.thread_id);
|
|
349
|
-
if (nowThread) {
|
|
350
|
-
client.get()?.resetStream();
|
|
351
|
-
}
|
|
352
|
-
return nowThread;
|
|
418
|
+
|
|
419
|
+
// Agent 切换
|
|
420
|
+
setCurrentAgent(agent: string) {
|
|
421
|
+
currentAgent.set(agent);
|
|
422
|
+
return initClient();
|
|
353
423
|
},
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
424
|
+
|
|
425
|
+
// 历史记录(兼容旧 API)
|
|
426
|
+
addToHistory,
|
|
427
|
+
createNewChat: createNewSession,
|
|
428
|
+
toHistoryChat: (thread: Thread<{ messages: Message[] }>) => activateSession(thread.thread_id),
|
|
358
429
|
async deleteHistoryChat(thread: Thread<{ messages: Message[] }>) {
|
|
359
|
-
|
|
360
|
-
|
|
430
|
+
const historyManager = history.get();
|
|
431
|
+
if (historyManager) {
|
|
432
|
+
await historyManager.deleteSession(thread.thread_id);
|
|
433
|
+
await refreshSessionList();
|
|
434
|
+
}
|
|
361
435
|
},
|
|
362
|
-
|
|
436
|
+
|
|
363
437
|
...artifactHook.mutation,
|
|
364
438
|
},
|
|
365
439
|
};
|
package/src/vue/ChatContext.ts
CHANGED
|
@@ -55,6 +55,8 @@ export interface ChatProviderProps {
|
|
|
55
55
|
showHistory?: boolean;
|
|
56
56
|
showGraph?: boolean;
|
|
57
57
|
fallbackToAvailableAssistants?: boolean;
|
|
58
|
+
/** 初始化时是否自动激活最近的历史会话(默认 false,创建新会话) */
|
|
59
|
+
autoRestoreLastSession?: boolean;
|
|
58
60
|
onInitError?: (error: any, currentAgent: string) => void;
|
|
59
61
|
client?: ILangGraphClient;
|
|
60
62
|
legacyMode?: boolean;
|
|
@@ -88,6 +90,7 @@ export const useChatProvider = (props: ChatProviderProps) => {
|
|
|
88
90
|
showHistory: props.showHistory,
|
|
89
91
|
showGraph: props.showGraph,
|
|
90
92
|
fallbackToAvailableAssistants: props.fallbackToAvailableAssistants,
|
|
93
|
+
autoRestoreLastSession: props.autoRestoreLastSession,
|
|
91
94
|
}
|
|
92
95
|
);
|
|
93
96
|
|
|
@@ -149,6 +152,10 @@ export const ChatProvider = defineComponent({
|
|
|
149
152
|
type: Boolean as PropType<boolean>,
|
|
150
153
|
default: false,
|
|
151
154
|
},
|
|
155
|
+
autoRestoreLastSession: {
|
|
156
|
+
type: Boolean as PropType<boolean>,
|
|
157
|
+
default: false,
|
|
158
|
+
},
|
|
152
159
|
onInitError: {
|
|
153
160
|
type: Function as PropType<(error: any, currentAgent: string) => void>,
|
|
154
161
|
default: undefined,
|