@langgraph-js/sdk 4.5.0 → 4.6.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 CHANGED
@@ -94,11 +94,17 @@ export declare class History {
94
94
  * @en Lists all sessions from remote
95
95
  */
96
96
  listRemoteSessions(options?: {
97
- sortOrder?: "asc" | "desc";
98
- sortBy?: "created_at" | "updated_at";
99
- offset?: number;
97
+ ids?: string[];
98
+ metadata?: Record<string, any>;
99
+ status?: "idle" | "busy" | "interrupted" | "error";
100
+ values?: any;
100
101
  limit?: number;
101
- }): Promise<Thread<unknown>[]>;
102
+ offset?: number;
103
+ sortBy?: "thread_id" | "status" | "created_at" | "updated_at";
104
+ sortOrder?: "asc" | "desc";
105
+ select?: Array<"thread_id" | "created_at" | "updated_at" | "metadata" | "config" | "context" | "status" | "values" | "interrupts">;
106
+ withoutDetails?: boolean;
107
+ }): Promise<Thread<unknown, unknown>[]>;
102
108
  /**
103
109
  * @zh 从远程同步会话到本地(仅同步元数据,不创建 Client)
104
110
  * @en Syncs sessions from remote to local (metadata only, no client created)
@@ -153,7 +153,7 @@ export declare class LangGraphClient<TStateType = unknown> extends EventEmitter<
153
153
  threadId?: string;
154
154
  ifExists?: import("@langchain/langgraph-sdk").OnConflictBehavior;
155
155
  graphId?: string;
156
- }): Promise<Thread<TStateType>>;
156
+ }): Promise<Thread<TStateType, unknown>>;
157
157
  search(query?: {
158
158
  metadata?: import("@langchain/langgraph-sdk").Metadata;
159
159
  limit?: number;
@@ -161,10 +161,10 @@ export declare class LangGraphClient<TStateType = unknown> extends EventEmitter<
161
161
  status?: import("@langchain/langgraph-sdk").ThreadStatus;
162
162
  sortBy?: import("@langgraph-js/pure-graph/dist/types.js").ThreadSortBy;
163
163
  sortOrder?: import("@langgraph-js/pure-graph/dist/types.js").SortOrder;
164
- }): Promise<Thread<TStateType>[]>;
165
- get(threadId: string): Promise<Thread<TStateType>>;
164
+ }): Promise<Thread<TStateType, unknown>[]>;
165
+ get(threadId: string): Promise<Thread<TStateType, unknown>>;
166
166
  delete(threadId: string): Promise<void>;
167
- updateState(threadId: string, thread: Partial<Thread<TStateType>>): Promise<Pick<import("@langchain/langgraph-sdk").Config, "configurable">>;
167
+ updateState(threadId: string, thread: Partial<Thread<TStateType, unknown>>): Promise<Pick<import("@langchain/langgraph-sdk").Config, "configurable">>;
168
168
  };
169
169
  /** 代理 runs 属性到内部 client */
170
170
  get runs(): ILangGraphClient["runs"];
@@ -185,24 +185,30 @@ export declare class LangGraphClient<TStateType = unknown> extends EventEmitter<
185
185
  createThread({ threadId, graphId }?: {
186
186
  threadId?: string;
187
187
  graphId?: string;
188
- }): Promise<Thread<TStateType>>;
188
+ }): Promise<Thread<TStateType, unknown>>;
189
189
  graphVisualize(): Promise<import("@langchain/langgraph-sdk").AssistantGraph>;
190
190
  /**
191
191
  * @zh 列出所有的 Thread。
192
192
  * @en Lists all Threads.
193
193
  */
194
194
  listThreads(options?: {
195
- sortOrder?: "asc" | "desc";
196
- sortBy?: "created_at" | "updated_at";
197
- offset?: number;
195
+ ids?: string[];
196
+ metadata?: Record<string, any>;
197
+ status?: "idle" | "busy" | "interrupted" | "error";
198
+ values?: any;
198
199
  limit?: number;
199
- }): Promise<Thread<TStateType>[]>;
200
+ offset?: number;
201
+ sortBy?: "thread_id" | "status" | "created_at" | "updated_at";
202
+ sortOrder?: "asc" | "desc";
203
+ select?: Array<"thread_id" | "created_at" | "updated_at" | "metadata" | "config" | "context" | "status" | "values" | "interrupts">;
204
+ withoutDetails?: boolean;
205
+ }): Promise<Thread<TStateType, unknown>[]>;
200
206
  deleteThread(threadId: string): Promise<void>;
201
207
  /**
202
208
  * @zh 从历史中恢复 Thread 数据。
203
209
  * @en Resets the Thread data from history.
204
210
  */
205
- resetThread(agent: string, threadId: string): Promise<Thread<TStateType>>;
211
+ resetThread(agent: string, threadId: string): Promise<Thread<TStateType, unknown>>;
206
212
  resetStream(): Promise<void>;
207
213
  cloneMessage(message: Message): Message;
208
214
  /**
@@ -283,7 +289,7 @@ export declare class LangGraphClient<TStateType = unknown> extends EventEmitter<
283
289
  * @zh 获取当前的 Thread。
284
290
  * @en Gets the current Thread.
285
291
  */
286
- getCurrentThread(): Thread<TStateType> | null;
292
+ getCurrentThread(): Thread<TStateType, unknown> | null;
287
293
  /**
288
294
  * @zh 获取当前的 Assistant。
289
295
  * @en Gets the current Assistant.
@@ -112,14 +112,39 @@ export class LangGraphClient extends EventEmitter {
112
112
  * @en Lists all Threads.
113
113
  */
114
114
  async listThreads(options = {}) {
115
- return this.threads.search({
116
- sortOrder: options.sortOrder || "desc",
117
- sortBy: options.sortBy || "updated_at",
118
- offset: options.offset || 0,
119
- limit: options.limit || 10,
120
- /** @ts-ignore: 用于删除不需要的字段 */
121
- without_details: true,
122
- });
115
+ const searchOptions = {};
116
+ if (options.ids)
117
+ searchOptions.ids = options.ids;
118
+ if (options.metadata)
119
+ searchOptions.metadata = options.metadata;
120
+ if (options.status)
121
+ searchOptions.status = options.status;
122
+ if (options.values)
123
+ searchOptions.values = options.values;
124
+ if (options.limit !== undefined)
125
+ searchOptions.limit = options.limit;
126
+ if (options.offset !== undefined)
127
+ searchOptions.offset = options.offset;
128
+ if (options.sortBy)
129
+ searchOptions.sortBy = options.sortBy;
130
+ if (options.sortOrder)
131
+ searchOptions.sortOrder = options.sortOrder;
132
+ if (options.select)
133
+ searchOptions.select = options.select;
134
+ if (options.withoutDetails !== undefined)
135
+ searchOptions.without_details = options.withoutDetails;
136
+ // 设置默认值
137
+ if (!options.sortBy)
138
+ searchOptions.sortBy = "updated_at";
139
+ if (!options.sortOrder)
140
+ searchOptions.sortOrder = "desc";
141
+ if (!options.limit)
142
+ searchOptions.limit = 10;
143
+ if (!options.offset)
144
+ searchOptions.offset = 0;
145
+ if (!options.withoutDetails)
146
+ searchOptions.without_details = true;
147
+ return this.threads.search(searchOptions);
123
148
  }
124
149
  async deleteThread(threadId) {
125
150
  return this.threads.delete(threadId);
package/dist/TestKit.d.ts CHANGED
@@ -146,169 +146,6 @@ export declare class TestLangGraphChat {
146
146
  */
147
147
  findLast(type: "human" | "ai" | "tool", options?: {
148
148
  before?: (item: RenderMessage) => boolean;
149
- }): ({
150
- additional_kwargs?: {
151
- [x: string]: unknown;
152
- } | undefined;
153
- content: string | ({
154
- type: "text";
155
- text: string;
156
- } | {
157
- type: "image_url";
158
- image_url: string | {
159
- url: string;
160
- detail?: ("auto" | "low" | "high") | undefined;
161
- };
162
- })[];
163
- id?: string | undefined;
164
- name?: string | undefined;
165
- response_metadata?: Record<string, unknown> | undefined;
166
- } & {
167
- type: "human";
168
- example?: boolean | undefined;
169
- } & {
170
- name?: string;
171
- node_name?: string;
172
- tool_input?: string;
173
- additional_kwargs?: {
174
- create_time: string;
175
- update_time: string;
176
- done?: boolean;
177
- tool_calls?: {
178
- function: {
179
- arguments: string;
180
- };
181
- }[];
182
- };
183
- usage_metadata?: {
184
- total_tokens: number;
185
- input_tokens: number;
186
- output_tokens: number;
187
- };
188
- tool_call_id?: string;
189
- sub_messages?: RenderMessage[];
190
- spend_time?: number;
191
- unique_id?: string;
192
- done?: boolean;
193
- }) | ({
194
- additional_kwargs?: {
195
- [x: string]: unknown;
196
- } | undefined;
197
- content: string | ({
198
- type: "text";
199
- text: string;
200
- } | {
201
- type: "image_url";
202
- image_url: string | {
203
- url: string;
204
- detail?: ("auto" | "low" | "high") | undefined;
205
- };
206
- })[];
207
- id?: string | undefined;
208
- name?: string | undefined;
209
- response_metadata?: Record<string, unknown> | undefined;
210
- } & {
211
- type: "ai";
212
- example?: boolean | undefined;
213
- tool_calls?: {
214
- name: string;
215
- args: {
216
- [x: string]: any;
217
- };
218
- id?: string | undefined;
219
- type?: "tool_call" | undefined;
220
- }[] | undefined;
221
- invalid_tool_calls?: {
222
- name?: string | undefined;
223
- args?: string | undefined;
224
- id?: string | undefined;
225
- error?: string | undefined;
226
- type?: "invalid_tool_call" | undefined;
227
- }[] | undefined;
228
- usage_metadata?: {
229
- input_tokens: number;
230
- output_tokens: number;
231
- total_tokens: number;
232
- input_token_details?: {
233
- audio?: number | undefined;
234
- cache_read?: number | undefined;
235
- cache_creation?: number | undefined;
236
- } | undefined;
237
- output_token_details?: {
238
- audio?: number | undefined;
239
- reasoning?: number | undefined;
240
- } | undefined;
241
- } | undefined;
242
- } & {
243
- name?: string;
244
- node_name?: string;
245
- tool_input?: string;
246
- additional_kwargs?: {
247
- create_time: string;
248
- update_time: string;
249
- done?: boolean;
250
- tool_calls?: {
251
- function: {
252
- arguments: string;
253
- };
254
- }[];
255
- };
256
- usage_metadata?: {
257
- total_tokens: number;
258
- input_tokens: number;
259
- output_tokens: number;
260
- };
261
- tool_call_id?: string;
262
- sub_messages?: RenderMessage[];
263
- spend_time?: number;
264
- unique_id?: string;
265
- done?: boolean;
266
- }) | ({
267
- additional_kwargs?: {
268
- [x: string]: unknown;
269
- } | undefined;
270
- content: string | ({
271
- type: "text";
272
- text: string;
273
- } | {
274
- type: "image_url";
275
- image_url: string | {
276
- url: string;
277
- detail?: ("auto" | "low" | "high") | undefined;
278
- };
279
- })[];
280
- id?: string | undefined;
281
- name?: string | undefined;
282
- response_metadata?: Record<string, unknown> | undefined;
283
- } & {
284
- type: "tool";
285
- status?: "error" | "success" | undefined;
286
- tool_call_id: string;
287
- artifact?: any;
288
- } & {
289
- name?: string;
290
- node_name?: string;
291
- tool_input?: string;
292
- additional_kwargs?: {
293
- create_time: string;
294
- update_time: string;
295
- done?: boolean;
296
- tool_calls?: {
297
- function: {
298
- arguments: string;
299
- };
300
- }[];
301
- };
302
- usage_metadata?: {
303
- total_tokens: number;
304
- input_tokens: number;
305
- output_tokens: number;
306
- };
307
- tool_call_id?: string;
308
- sub_messages?: RenderMessage[];
309
- spend_time?: number;
310
- unique_id?: string;
311
- done?: boolean;
312
- });
149
+ }): RenderMessage;
313
150
  }
314
151
  export {};
@@ -26,7 +26,9 @@ export declare const useChat: () => UnionStore<{
26
26
  showHistory: import("nanostores").PreinitializedWritableAtom<boolean> & object;
27
27
  historyList: import("nanostores").PreinitializedWritableAtom<import("@langchain/langgraph-sdk").Thread<{
28
28
  messages: import("@langchain/langgraph-sdk").Message[];
29
- }>[]> & object;
29
+ }, unknown>[]> & object;
30
+ historyPagination: import("nanostores").PreinitializedWritableAtom<import("../ui-store/createChatStore.js").HistoryPagination> & object;
31
+ historyFilter: import("nanostores").PreinitializedWritableAtom<import("../ui-store/createChatStore.js").HistoryFilter> & object;
30
32
  };
31
33
  mutations: {
32
34
  setCurrentArtifactById: (id: string, tool_id: string) => void;
@@ -62,6 +64,10 @@ export declare const useChat: () => UnionStore<{
62
64
  deleteHistoryChat(thread: import("@langchain/langgraph-sdk").Thread<{
63
65
  messages: import("@langchain/langgraph-sdk").Message[];
64
66
  }>): Promise<void>;
67
+ setHistoryPage(page: number): void;
68
+ setHistoryPageSize(pageSize: number): void;
69
+ setHistoryFilter(filter: Partial<import("../ui-store/createChatStore.js").HistoryFilter>): void;
70
+ resetHistoryFilter(): void;
65
71
  };
66
72
  }>;
67
73
  interface ChatProviderProps {
@@ -26,7 +26,9 @@ export declare const useChat: () => UnionStoreSolid<{
26
26
  showHistory: PreinitializedWritableAtom<boolean> & object;
27
27
  historyList: PreinitializedWritableAtom<import("@langchain/langgraph-sdk").Thread<{
28
28
  messages: import("@langchain/langgraph-sdk").Message[];
29
- }>[]> & object;
29
+ }, unknown>[]> & object;
30
+ historyPagination: PreinitializedWritableAtom<import("../ui-store/createChatStore.js").HistoryPagination> & object;
31
+ historyFilter: PreinitializedWritableAtom<import("../ui-store/createChatStore.js").HistoryFilter> & object;
30
32
  };
31
33
  mutations: {
32
34
  setCurrentArtifactById: (id: string, tool_id: string) => void;
@@ -62,6 +64,10 @@ export declare const useChat: () => UnionStoreSolid<{
62
64
  deleteHistoryChat(thread: import("@langchain/langgraph-sdk").Thread<{
63
65
  messages: import("@langchain/langgraph-sdk").Message[];
64
66
  }>): Promise<void>;
67
+ setHistoryPage(page: number): void;
68
+ setHistoryPageSize(pageSize: number): void;
69
+ setHistoryFilter(filter: Partial<import("../ui-store/createChatStore.js").HistoryFilter>): void;
70
+ resetHistoryFilter(): void;
65
71
  };
66
72
  }>;
67
73
  interface ChatProviderProps {
@@ -17,6 +17,17 @@ interface ChatStoreContext {
17
17
  /** 初始化时是否自动激活最近的历史会话(默认 false,创建新会话) */
18
18
  autoRestoreLastSession?: boolean;
19
19
  }
20
+ export interface HistoryPagination {
21
+ page: number;
22
+ pageSize: number;
23
+ total: number;
24
+ }
25
+ export interface HistoryFilter {
26
+ metadata: Record<string, any> | null;
27
+ status: "idle" | "busy" | "interrupted" | "error" | null;
28
+ sortBy: "thread_id" | "status" | "created_at" | "updated_at";
29
+ sortOrder: "asc" | "desc";
30
+ }
20
31
  export declare const createChatStore: (initClientName: string, config: Partial<LangGraphClientConfig>, context?: ChatStoreContext) => {
21
32
  data: {
22
33
  artifacts: import("nanostores").PreinitializedWritableAtom<import("../artifacts/index.js").ComposedArtifact[]> & object;
@@ -42,7 +53,9 @@ export declare const createChatStore: (initClientName: string, config: Partial<L
42
53
  showHistory: import("nanostores").PreinitializedWritableAtom<boolean> & object;
43
54
  historyList: import("nanostores").PreinitializedWritableAtom<Thread<{
44
55
  messages: Message[];
45
- }>[]> & object;
56
+ }, unknown>[]> & object;
57
+ historyPagination: import("nanostores").PreinitializedWritableAtom<HistoryPagination> & object;
58
+ historyFilter: import("nanostores").PreinitializedWritableAtom<HistoryFilter> & object;
46
59
  };
47
60
  mutations: {
48
61
  setCurrentArtifactById: (id: string, tool_id: string) => void;
@@ -78,6 +91,10 @@ export declare const createChatStore: (initClientName: string, config: Partial<L
78
91
  deleteHistoryChat(thread: Thread<{
79
92
  messages: Message[];
80
93
  }>): Promise<void>;
94
+ setHistoryPage(page: number): void;
95
+ setHistoryPageSize(pageSize: number): void;
96
+ setHistoryFilter(filter: Partial<HistoryFilter>): void;
97
+ resetHistoryFilter(): void;
81
98
  };
82
99
  };
83
100
  export {};
@@ -70,6 +70,19 @@ export const createChatStore = (initClientName, config, context = {}) => {
70
70
  const showHistory = atom(context.showHistory ?? false);
71
71
  const showGraph = atom(context.showGraph ?? false);
72
72
  const graphVisualize = atom(null);
73
+ // 分页状态
74
+ const historyPagination = atom({
75
+ page: 1,
76
+ pageSize: 10,
77
+ total: 0,
78
+ });
79
+ // 历史记录筛选状态
80
+ const historyFilter = atom({
81
+ metadata: null,
82
+ status: null,
83
+ sortBy: "updated_at",
84
+ sortOrder: "desc",
85
+ });
73
86
  // ============ 内部状态 ============
74
87
  let cleanupCurrentClient = null;
75
88
  // ============ 计算属性 ============
@@ -113,7 +126,6 @@ export const createChatStore = (initClientName, config, context = {}) => {
113
126
  });
114
127
  await historyManager.init(currentAgent.get(), { fallbackToAvailableAssistants: context.fallbackToAvailableAssistants });
115
128
  history.set(historyManager);
116
- // 同步远程会话列表
117
129
  // 根据配置决定初始化行为
118
130
  if (context.autoRestoreLastSession) {
119
131
  await refreshSessionList();
@@ -137,9 +149,35 @@ export const createChatStore = (initClientName, config, context = {}) => {
137
149
  if (!historyManager)
138
150
  return;
139
151
  try {
140
- const syncedSessions = await historyManager.syncFromRemote({ limit: 10 });
141
- sessions.set(syncedSessions);
142
- historyList.set(syncedSessions.filter((s) => s.thread).map((s) => s.thread));
152
+ const pagination = historyPagination.get();
153
+ const filter = historyFilter.get();
154
+ // 计算偏移量
155
+ const offset = (pagination.page - 1) * pagination.pageSize;
156
+ // 使用 listRemoteSessions 支持筛选
157
+ const threads = await historyManager.listRemoteSessions({
158
+ limit: pagination.pageSize,
159
+ offset,
160
+ metadata: filter.metadata || undefined,
161
+ status: filter.status || undefined,
162
+ sortBy: filter.sortBy,
163
+ sortOrder: filter.sortOrder,
164
+ withoutDetails: true,
165
+ });
166
+ // 注意:后端可能不返回总数,这里需要根据返回的记录数判断是否有下一页
167
+ // 如果返回的记录数小于 pageSize,说明没有更多数据了
168
+ const hasMore = threads.length === pagination.pageSize;
169
+ const estimatedTotal = (pagination.page - 1) * pagination.pageSize + threads.length;
170
+ sessions.set(threads.map((thread) => ({
171
+ sessionId: thread.thread_id,
172
+ thread,
173
+ agentName: currentAgent.get(),
174
+ })));
175
+ historyList.set(threads);
176
+ // 更新分页状态(注意:这里只是估计值,实际总数可能需要从后端获取)
177
+ historyPagination.set({
178
+ ...pagination,
179
+ total: hasMore ? pagination.page * pagination.pageSize + 1 : estimatedTotal,
180
+ });
143
181
  }
144
182
  catch (error) {
145
183
  console.error("Failed to sync sessions:", error);
@@ -364,6 +402,9 @@ export const createChatStore = (initClientName, config, context = {}) => {
364
402
  // 历史记录
365
403
  showHistory,
366
404
  historyList,
405
+ // 分页和筛选
406
+ historyPagination,
407
+ historyFilter,
367
408
  ...artifactHook.data,
368
409
  },
369
410
  mutations: {
@@ -429,6 +470,46 @@ export const createChatStore = (initClientName, config, context = {}) => {
429
470
  await refreshSessionList();
430
471
  }
431
472
  },
473
+ // 分页和筛选操作
474
+ setHistoryPage(page) {
475
+ historyPagination.set({
476
+ ...historyPagination.get(),
477
+ page,
478
+ });
479
+ refreshSessionList();
480
+ },
481
+ setHistoryPageSize(pageSize) {
482
+ historyPagination.set({
483
+ ...historyPagination.get(),
484
+ pageSize,
485
+ page: 1, // 重置到第一页
486
+ });
487
+ refreshSessionList();
488
+ },
489
+ setHistoryFilter(filter) {
490
+ historyFilter.set({
491
+ ...historyFilter.get(),
492
+ ...filter,
493
+ });
494
+ historyPagination.set({
495
+ ...historyPagination.get(),
496
+ page: 1, // 筛选变更时重置到第一页
497
+ });
498
+ refreshSessionList();
499
+ },
500
+ resetHistoryFilter() {
501
+ historyFilter.set({
502
+ metadata: null,
503
+ status: null,
504
+ sortBy: "updated_at",
505
+ sortOrder: "desc",
506
+ });
507
+ historyPagination.set({
508
+ ...historyPagination.get(),
509
+ page: 1,
510
+ });
511
+ refreshSessionList();
512
+ },
432
513
  ...artifactHook.mutation,
433
514
  },
434
515
  };
@@ -70,7 +70,9 @@ export declare const useChatProvider: (props: ChatProviderProps) => {
70
70
  showHistory: PreinitializedWritableAtom<boolean> & object;
71
71
  historyList: PreinitializedWritableAtom<import("@langchain/langgraph-sdk").Thread<{
72
72
  messages: import("@langchain/langgraph-sdk").Message[];
73
- }>[]> & object;
73
+ }, unknown>[]> & object;
74
+ historyPagination: PreinitializedWritableAtom<import("../ui-store/createChatStore.js").HistoryPagination> & object;
75
+ historyFilter: PreinitializedWritableAtom<import("../ui-store/createChatStore.js").HistoryFilter> & object;
74
76
  };
75
77
  mutations: {
76
78
  setCurrentArtifactById: (id: string, tool_id: string) => void;
@@ -106,6 +108,10 @@ export declare const useChatProvider: (props: ChatProviderProps) => {
106
108
  deleteHistoryChat(thread: import("@langchain/langgraph-sdk").Thread<{
107
109
  messages: import("@langchain/langgraph-sdk").Message[];
108
110
  }>): Promise<void>;
111
+ setHistoryPage(page: number): void;
112
+ setHistoryPageSize(pageSize: number): void;
113
+ setHistoryFilter(filter: Partial<import("../ui-store/createChatStore.js").HistoryFilter>): void;
114
+ resetHistoryFilter(): void;
109
115
  };
110
116
  }>;
111
117
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langgraph-js/sdk",
3
- "version": "4.5.0",
3
+ "version": "4.6.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",
package/src/History.ts CHANGED
@@ -230,10 +230,16 @@ export class History {
230
230
  */
231
231
  async listRemoteSessions(
232
232
  options: {
233
- sortOrder?: "asc" | "desc";
234
- sortBy?: "created_at" | "updated_at";
235
- offset?: number;
233
+ ids?: string[];
234
+ metadata?: Record<string, any>;
235
+ status?: "idle" | "busy" | "interrupted" | "error";
236
+ values?: any;
236
237
  limit?: number;
238
+ offset?: number;
239
+ sortBy?: "thread_id" | "status" | "created_at" | "updated_at";
240
+ sortOrder?: "asc" | "desc";
241
+ select?: Array<"thread_id" | "created_at" | "updated_at" | "metadata" | "config" | "context" | "status" | "values" | "interrupts">;
242
+ withoutDetails?: boolean;
237
243
  } = {}
238
244
  ) {
239
245
  return this.virtualClient.listThreads(options);
@@ -212,20 +212,39 @@ export class LangGraphClient<TStateType = unknown> extends EventEmitter<LangGrap
212
212
  */
213
213
  async listThreads(
214
214
  options: {
215
- sortOrder?: "asc" | "desc";
216
- sortBy?: "created_at" | "updated_at";
217
- offset?: number;
215
+ ids?: string[];
216
+ metadata?: Record<string, any>;
217
+ status?: "idle" | "busy" | "interrupted" | "error";
218
+ values?: any;
218
219
  limit?: number;
220
+ offset?: number;
221
+ sortBy?: "thread_id" | "status" | "created_at" | "updated_at";
222
+ sortOrder?: "asc" | "desc";
223
+ select?: Array<"thread_id" | "created_at" | "updated_at" | "metadata" | "config" | "context" | "status" | "values" | "interrupts">;
224
+ withoutDetails?: boolean;
219
225
  } = {}
220
226
  ) {
221
- return this.threads.search({
222
- sortOrder: options.sortOrder || "desc",
223
- sortBy: options.sortBy || "updated_at",
224
- offset: options.offset || 0,
225
- limit: options.limit || 10,
226
- /** @ts-ignore: 用于删除不需要的字段 */
227
- without_details: true,
228
- });
227
+ const searchOptions: any = {};
228
+
229
+ if (options.ids) searchOptions.ids = options.ids;
230
+ if (options.metadata) searchOptions.metadata = options.metadata;
231
+ if (options.status) searchOptions.status = options.status;
232
+ if (options.values) searchOptions.values = options.values;
233
+ if (options.limit !== undefined) searchOptions.limit = options.limit;
234
+ if (options.offset !== undefined) searchOptions.offset = options.offset;
235
+ if (options.sortBy) searchOptions.sortBy = options.sortBy;
236
+ if (options.sortOrder) searchOptions.sortOrder = options.sortOrder;
237
+ if (options.select) searchOptions.select = options.select;
238
+ if (options.withoutDetails !== undefined) searchOptions.without_details = options.withoutDetails;
239
+
240
+ // 设置默认值
241
+ if (!options.sortBy) searchOptions.sortBy = "updated_at";
242
+ if (!options.sortOrder) searchOptions.sortOrder = "desc";
243
+ if (!options.limit) searchOptions.limit = 10;
244
+ if (!options.offset) searchOptions.offset = 0;
245
+ if (!options.withoutDetails) searchOptions.without_details = true;
246
+
247
+ return this.threads.search(searchOptions);
229
248
  }
230
249
  async deleteThread(threadId: string) {
231
250
  return this.threads.delete(threadId);
package/src/TestKit.ts CHANGED
@@ -292,7 +292,7 @@ export class TestLangGraphChat {
292
292
  * const lastHuman = testChat.findLast("human");
293
293
  * ```
294
294
  */
295
- findLast(type: "human" | "ai" | "tool", options: { before?: (item: RenderMessage) => boolean } = {}) {
295
+ findLast(type: "human" | "ai" | "tool", options: { before?: (item: RenderMessage) => boolean } = {}): RenderMessage {
296
296
  const messages = this.getMessages();
297
297
 
298
298
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -61,6 +61,21 @@ interface ChatStoreContext {
61
61
  autoRestoreLastSession?: boolean;
62
62
  }
63
63
 
64
+ // 分页状态类型
65
+ export interface HistoryPagination {
66
+ page: number;
67
+ pageSize: number;
68
+ total: number;
69
+ }
70
+
71
+ // 历史记录筛选类型
72
+ export interface HistoryFilter {
73
+ metadata: Record<string, any> | null;
74
+ status: "idle" | "busy" | "interrupted" | "error" | null;
75
+ sortBy: "thread_id" | "status" | "created_at" | "updated_at";
76
+ sortOrder: "asc" | "desc";
77
+ }
78
+
64
79
  // ============ Store 创建函数 ============
65
80
 
66
81
  export const createChatStore = (initClientName: string, config: Partial<LangGraphClientConfig>, context: ChatStoreContext = {}) => {
@@ -91,6 +106,21 @@ export const createChatStore = (initClientName: string, config: Partial<LangGrap
91
106
  const showGraph = atom<boolean>(context.showGraph ?? false);
92
107
  const graphVisualize = atom<AssistantGraph | null>(null);
93
108
 
109
+ // 分页状态
110
+ const historyPagination = atom<HistoryPagination>({
111
+ page: 1,
112
+ pageSize: 10,
113
+ total: 0,
114
+ });
115
+
116
+ // 历史记录筛选状态
117
+ const historyFilter = atom<HistoryFilter>({
118
+ metadata: null,
119
+ status: null,
120
+ sortBy: "updated_at",
121
+ sortOrder: "desc",
122
+ });
123
+
94
124
  // ============ 内部状态 ============
95
125
 
96
126
  let cleanupCurrentClient: (() => void) | null = null;
@@ -145,8 +175,6 @@ export const createChatStore = (initClientName: string, config: Partial<LangGrap
145
175
  await historyManager.init(currentAgent.get(), { fallbackToAvailableAssistants: context.fallbackToAvailableAssistants });
146
176
  history.set(historyManager);
147
177
 
148
- // 同步远程会话列表
149
-
150
178
  // 根据配置决定初始化行为
151
179
  if (context.autoRestoreLastSession) {
152
180
  await refreshSessionList();
@@ -170,9 +198,46 @@ export const createChatStore = (initClientName: string, config: Partial<LangGrap
170
198
  if (!historyManager) return;
171
199
 
172
200
  try {
173
- const syncedSessions = await historyManager.syncFromRemote({ limit: 10 });
174
- sessions.set(syncedSessions);
175
- historyList.set(syncedSessions.filter((s) => s.thread).map((s) => s.thread!));
201
+ const pagination = historyPagination.get();
202
+ const filter = historyFilter.get();
203
+
204
+ // 计算偏移量
205
+ const offset = (pagination.page - 1) * pagination.pageSize;
206
+
207
+ // 使用 listRemoteSessions 支持筛选
208
+ const threads = await historyManager.listRemoteSessions({
209
+ limit: pagination.pageSize,
210
+ offset,
211
+ metadata: filter.metadata || undefined,
212
+ status: filter.status || undefined,
213
+ sortBy: filter.sortBy,
214
+ sortOrder: filter.sortOrder,
215
+ withoutDetails: true,
216
+ });
217
+
218
+ // 注意:后端可能不返回总数,这里需要根据返回的记录数判断是否有下一页
219
+ // 如果返回的记录数小于 pageSize,说明没有更多数据了
220
+ const hasMore = threads.length === pagination.pageSize;
221
+ const estimatedTotal = (pagination.page - 1) * pagination.pageSize + threads.length;
222
+
223
+ sessions.set(
224
+ threads.map(
225
+ (thread) =>
226
+ ({
227
+ sessionId: thread.thread_id,
228
+ thread,
229
+ agentName: currentAgent.get(),
230
+ }) as SessionInfo
231
+ )
232
+ );
233
+
234
+ historyList.set(threads as Thread<{ messages: Message[] }>[]);
235
+
236
+ // 更新分页状态(注意:这里只是估计值,实际总数可能需要从后端获取)
237
+ historyPagination.set({
238
+ ...pagination,
239
+ total: hasMore ? pagination.page * pagination.pageSize + 1 : estimatedTotal,
240
+ });
176
241
  } catch (error) {
177
242
  console.error("Failed to sync sessions:", error);
178
243
  }
@@ -427,6 +492,10 @@ export const createChatStore = (initClientName: string, config: Partial<LangGrap
427
492
  showHistory,
428
493
  historyList,
429
494
 
495
+ // 分页和筛选
496
+ historyPagination,
497
+ historyFilter,
498
+
430
499
  ...artifactHook.data,
431
500
  },
432
501
  mutations: {
@@ -500,6 +569,47 @@ export const createChatStore = (initClientName: string, config: Partial<LangGrap
500
569
  }
501
570
  },
502
571
 
572
+ // 分页和筛选操作
573
+ setHistoryPage(page: number) {
574
+ historyPagination.set({
575
+ ...historyPagination.get(),
576
+ page,
577
+ });
578
+ refreshSessionList();
579
+ },
580
+ setHistoryPageSize(pageSize: number) {
581
+ historyPagination.set({
582
+ ...historyPagination.get(),
583
+ pageSize,
584
+ page: 1, // 重置到第一页
585
+ });
586
+ refreshSessionList();
587
+ },
588
+ setHistoryFilter(filter: Partial<HistoryFilter>) {
589
+ historyFilter.set({
590
+ ...historyFilter.get(),
591
+ ...filter,
592
+ });
593
+ historyPagination.set({
594
+ ...historyPagination.get(),
595
+ page: 1, // 筛选变更时重置到第一页
596
+ });
597
+ refreshSessionList();
598
+ },
599
+ resetHistoryFilter() {
600
+ historyFilter.set({
601
+ metadata: null,
602
+ status: null,
603
+ sortBy: "updated_at",
604
+ sortOrder: "desc",
605
+ });
606
+ historyPagination.set({
607
+ ...historyPagination.get(),
608
+ page: 1,
609
+ });
610
+ refreshSessionList();
611
+ },
612
+
503
613
  ...artifactHook.mutation,
504
614
  },
505
615
  };