@langgraph-js/pure-graph 3.2.5 → 3.3.1

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 (33) hide show
  1. package/dist/adapter/fetch/index.js +4 -3
  2. package/dist/adapter/fetch/index.js.map +1 -1
  3. package/dist/adapter/nextjs/index.js +1 -1
  4. package/dist/adapter/zod.d.ts +1 -0
  5. package/dist/{createEndpoint-Cz6LSXYH.js → createEndpoint-D27R5Yhn.js} +53 -11
  6. package/dist/createEndpoint-D27R5Yhn.js.map +1 -0
  7. package/dist/createEndpoint.d.ts +2 -1
  8. package/dist/global.d.ts +15 -0
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.js +2 -2
  11. package/dist/{queue-swFecaQs.js → queue-CS3K-zd_.js} +2 -2
  12. package/dist/{queue-swFecaQs.js.map → queue-CS3K-zd_.js.map} +1 -1
  13. package/dist/remote/index.js +1 -1
  14. package/dist/{remote-threads-CrG03ZS7.js → remote-threads-UpSB8X-e.js} +19 -1
  15. package/dist/remote-threads-UpSB8X-e.js.map +1 -0
  16. package/dist/{sqlite-adapter-CJXgit1j.js → sqlite-adapter-D5g0LbUq.js} +15 -2
  17. package/dist/sqlite-adapter-D5g0LbUq.js.map +1 -0
  18. package/dist/storage/kysely/remote-threads.d.ts +4 -0
  19. package/dist/storage/kysely/sqlite-adapter.d.ts +5 -0
  20. package/dist/storage/kysely/threads.d.ts +3 -2
  21. package/dist/storage/kysely/types.d.ts +1 -0
  22. package/dist/storage/memory/threads.d.ts +4 -3
  23. package/dist/{stream-Dm5628RW.js → stream-Bleed1lp.js} +104 -13
  24. package/dist/stream-Bleed1lp.js.map +1 -0
  25. package/dist/threads/index.d.ts +9 -3
  26. package/dist/types.d.ts +10 -2
  27. package/dist/utils/titleGenerator.d.ts +20 -0
  28. package/dist/utils/titleGeneratorHelper.d.ts +17 -0
  29. package/package.json +87 -86
  30. package/dist/createEndpoint-Cz6LSXYH.js.map +0 -1
  31. package/dist/remote-threads-CrG03ZS7.js.map +0 -1
  32. package/dist/sqlite-adapter-CJXgit1j.js.map +0 -1
  33. package/dist/stream-Dm5628RW.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { L as LangGraphGlobal } from '../../stream-Dm5628RW.js';
1
+ import { L as LangGraphGlobal } from '../../stream-Bleed1lp.js';
2
2
 
3
3
  async function ensureInitialized(attachGraphPromise) {
4
4
  if (globalThis.LG_INIT_PROMISE === void 0) {
@@ -278,6 +278,7 @@ export declare const ThreadSearchPayloadSchema: z.ZodObject<{
278
278
  config: "config";
279
279
  context: "context";
280
280
  interrupts: "interrupts";
281
+ title: "title";
281
282
  }>>>;
282
283
  without_details: z.ZodOptional<z.ZodBoolean>;
283
284
  }, z.core.$strip>;
@@ -1,4 +1,32 @@
1
- import { L as LangGraphGlobal, a as streamState, g as getGraph, G as GRAPHS } from './stream-Dm5628RW.js';
1
+ import { L as LangGraphGlobal, a as streamState, g as getGraph, G as GRAPHS } from './stream-Bleed1lp.js';
2
+
3
+ async function generateThreadTitle(threads, threadId, graphId, runId) {
4
+ const logContext = { threadId, graphId, runId };
5
+ try {
6
+ const thread = await threads.get(threadId);
7
+ const state = thread.values;
8
+ if (!state?.messages || !Array.isArray(state.messages) || state.messages.length === 0) {
9
+ return;
10
+ }
11
+ const titleGenerator = LangGraphGlobal.getTitleGenerator();
12
+ const title = await titleGenerator(state, {
13
+ thread_id: threadId,
14
+ graph_id: graphId,
15
+ run_id: runId
16
+ });
17
+ if (title) {
18
+ const success = await threads.setTitleIfNull(threadId, title);
19
+ if (!success) {
20
+ return;
21
+ }
22
+ }
23
+ } catch (error) {
24
+ console.warn("Failed to generate thread title:", {
25
+ ...logContext,
26
+ error: error instanceof Error ? error.message : String(error)
27
+ });
28
+ }
29
+ }
2
30
 
3
31
  const AssistantEndpoint = {
4
32
  async search(query) {
@@ -153,16 +181,30 @@ const createEndpoint = () => {
153
181
  }
154
182
  };
155
183
  const threads = getThreads();
156
- for await (const data of streamState(
157
- threads,
158
- threads.createRun(threadId, assistantId, payload),
159
- payload,
160
- {
161
- attempt: 0,
162
- getGraph
184
+ const runPromise = threads.createRun(threadId, assistantId, payload);
185
+ try {
186
+ for await (const data of streamState(threads, runPromise, payload, { attempt: 0, getGraph })) {
187
+ yield data;
188
+ }
189
+ const run = await runPromise;
190
+ await generateThreadTitle(
191
+ threads,
192
+ threadId,
193
+ assistantId,
194
+ run.run_id
195
+ );
196
+ } catch (error) {
197
+ try {
198
+ const run = await runPromise;
199
+ await generateThreadTitle(
200
+ threads,
201
+ threadId,
202
+ assistantId,
203
+ run.run_id
204
+ );
205
+ } catch {
163
206
  }
164
- )) {
165
- yield data;
207
+ throw error;
166
208
  }
167
209
  },
168
210
  async *joinStream(threadId, runId, options) {
@@ -220,4 +262,4 @@ const createEndpoint = () => {
220
262
  };
221
263
 
222
264
  export { AssistantEndpoint as A, createEndpoint as c };
223
- //# sourceMappingURL=createEndpoint-Cz6LSXYH.js.map
265
+ //# sourceMappingURL=createEndpoint-D27R5Yhn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createEndpoint-D27R5Yhn.js","sources":["../src/utils/titleGeneratorHelper.ts","../src/createEndpoint.ts"],"sourcesContent":["/**\n * 标题生成辅助函数\n * 用于在流结束后为 thread 生成标题\n */\n\nimport type { BaseThreadsManager } from '../threads/index.js';\nimport { LangGraphGlobal } from '../global.js';\n\n/**\n * 为 thread 生成并保存标题\n * 在流结束后调用,避免侵入流处理逻辑\n *\n * @param threads ThreadsManager 实例\n * @param threadId 线程 ID\n * @param graphId Graph ID\n * @param runId Run ID\n */\nexport async function generateThreadTitle(\n threads: BaseThreadsManager<{ messages: any[] }>,\n threadId: string,\n graphId: string,\n runId: string,\n): Promise<void> {\n const logContext = { threadId, graphId, runId };\n\n try {\n // 1. 获取 thread 以检查是否有 messages\n const thread = await threads.get(threadId);\n const state = thread.values;\n\n // 没有消息则跳过\n if (!state?.messages || !Array.isArray(state.messages) || state.messages.length === 0) {\n return;\n }\n\n // 2. 调用全局标题生成器\n const titleGenerator = LangGraphGlobal.getTitleGenerator();\n const title = await titleGenerator(state, {\n thread_id: threadId,\n graph_id: graphId,\n run_id: runId,\n });\n\n // 3. 使用原子操作保存标题(仅当标题为空时)\n if (title) {\n const success = await threads.setTitleIfNull(threadId, title);\n if (!success) {\n // 已有标题,跳过(正常情况,非错误)\n return;\n }\n }\n } catch (error) {\n // 标题生成失败不应影响主流程\n console.warn('Failed to generate thread title:', {\n ...logContext,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n}\n","import { StreamEvent } from '@langchain/core/tracers/log_stream';\nimport { streamState } from './graph/stream.js';\nimport { Assistant, Run, StreamMode, Metadata, AssistantGraph } from '@langchain/langgraph-sdk';\nimport { getGraph, GRAPHS } from './utils/getGraph.js';\nimport { LangGraphGlobal } from './global.js';\nimport { AssistantSortBy, CancelAction, ILangGraphClient, RunStatus, SortOrder, StreamInputData } from './types.js';\nimport type { BaseStreamQueueInterface } from './queue/stream_queue.js';\nimport type { EventMessage } from './queue/event_message.js';\nimport { generateThreadTitle } from './utils/titleGeneratorHelper.js';\nimport { BaseThreadsManager } from './threads/index.js';\nexport { registerGraph } from './utils/getGraph.js';\n\nexport const AssistantEndpoint: ILangGraphClient['assistants'] = {\n async search(query?: {\n graphId?: string;\n metadata?: Metadata;\n limit?: number;\n offset?: number;\n sortBy?: AssistantSortBy;\n sortOrder?: SortOrder;\n }): Promise<Assistant[]> {\n let results = Object.entries(GRAPHS).map(\n ([graphId, _]) =>\n ({\n assistant_id: graphId,\n graph_id: graphId,\n config: {},\n metadata: {},\n version: 1,\n name: graphId,\n description: '',\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n }) as Assistant,\n );\n\n // Filter by graphId\n if (query?.graphId) {\n results = results.filter((a) => a.graph_id === query.graphId);\n }\n\n // Filter by metadata (simple implementation - check if all metadata keys/values match)\n if (query?.metadata && Object.keys(query.metadata).length > 0) {\n results = results.filter((assistant) => {\n return Object.entries(query.metadata!).every(([key, value]) => {\n return assistant.metadata && assistant.metadata[key] === value;\n });\n });\n }\n\n // Sort results\n if (query?.sortBy) {\n results.sort((a, b) => {\n const aValue = a[query.sortBy!];\n const bValue = b[query.sortBy!];\n const comparison = aValue < bValue ? -1 : aValue > bValue ? 1 : 0;\n return query.sortOrder === 'desc' ? -comparison : comparison;\n });\n }\n\n // Pagination\n const offset = query?.offset ?? 0;\n const limit = query?.limit;\n const paginatedResults = limit ? results.slice(offset, offset + limit) : results.slice(offset);\n\n return paginatedResults;\n },\n\n async count(query?: { graphId?: string; metadata?: Metadata }): Promise<number> {\n const results = await this.search(query);\n return results.length;\n },\n\n async get(assistantId: string): Promise<Assistant> {\n const assistant = Object.entries(GRAPHS).find(([graphId, _]) => graphId === assistantId);\n if (!assistant) {\n throw new Error(`Assistant not found: ${assistantId}`);\n }\n return {\n assistant_id: assistantId,\n graph_id: assistantId,\n config: {},\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n metadata: {},\n version: 1,\n name: assistantId,\n description: '',\n } as Assistant;\n },\n\n async delete(assistantId: string): Promise<void> {\n // ⚠️ 删除 assistant 不可用 - assistants 是从注册的图中生成的,不能删除\n throw new Error('Deleting assistants is not supported. Assistants are generated from registered graphs.');\n },\n\n async update(\n assistantId: string,\n updates: Partial<Pick<Assistant, 'name' | 'description' | 'metadata' | 'config'>>,\n ): Promise<Assistant> {\n // ⚠️ 更新 assistant 不可用 - assistants 是从注册的图中生成的,不能更新\n throw new Error('Updating assistants is not supported. Assistants are generated from registered graphs.');\n },\n\n async getGraph(assistantId: string, options?: { xray?: boolean | number }): Promise<AssistantGraph> {\n const config = {};\n const graph = await getGraph(assistantId, config);\n const drawable = await graph.getGraphAsync({\n ...config,\n xray: options?.xray ?? undefined,\n });\n return drawable.toJSON() as AssistantGraph;\n },\n\n async getSchemas(assistantId: string): Promise<{ graph_id: string; state_schema: any }> {\n const compiledGraph = await getGraph(assistantId, {});\n const builder = compiledGraph.builder;\n console.log(builder);\n return {\n graph_id: assistantId,\n /** @ts-ignore */\n state_schema: builder._inputDefinition,\n /** @ts-ignore */\n input_schema: builder._inputDefinition,\n /** @ts-ignore */\n output_schema: builder._outputDefinition,\n /** @ts-ignore */\n config_schema: builder._configSchema,\n /** @ts-ignoreß */\n context_schema: builder._configSchema,\n };\n },\n\n async getVersions(assistantId: string, options?: { limit?: number; offset?: number }): Promise<Assistant[]> {\n // ⚠️ 版本管理不可用 - 当前实现不支持多版本\n const assistant = await this.get(assistantId);\n const offset = options?.offset ?? 0;\n const limit = options?.limit;\n const results = limit ? [assistant].slice(offset, offset + limit) : [assistant].slice(offset);\n return results;\n },\n\n async setLatest(assistantId: string, version: number): Promise<Assistant> {\n // Fake\n const item = await this.get(assistantId);\n item.version = version;\n return item;\n },\n\n async create(params: {\n assistantId?: string;\n graphId: string;\n name?: string;\n description?: string;\n metadata?: Metadata;\n config?: any;\n ifExists?: 'raise' | 'do_nothing';\n }): Promise<Assistant> {\n // ⚠️ 创建 assistant 不可用 - assistants 是从注册的图中自动生成的,不能动态创建\n // 返回假数据以通过测试\n console.warn(\n '⚠️ Creating assistants is not supported. Assistants are generated from registered graphs. Returning mock data.',\n );\n const graphExists = Object.keys(GRAPHS).includes(params.graphId);\n\n if (!graphExists) {\n if (params.ifExists === 'raise') {\n throw new Error(`Graph not found: ${params.graphId}`);\n }\n // 如果 graph 不存在,我们仍然返回假数据\n }\n\n return {\n assistant_id: params.assistantId || params.graphId,\n graph_id: params.graphId,\n name: params.name || params.graphId,\n description: params.description || '',\n metadata: params.metadata || {},\n config: params.config || {},\n version: 1,\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n } as Assistant;\n },\n};\n\nexport const createEndpoint = () => {\n const getThreads = () => {\n return LangGraphGlobal.globalThreadsManager;\n };\n return {\n assistants: AssistantEndpoint,\n get threads() {\n return LangGraphGlobal.globalThreadsManager;\n },\n runs: {\n list(\n threadId: string,\n options?: {\n limit?: number;\n offset?: number;\n status?: RunStatus;\n },\n ): Promise<Run[]> {\n return getThreads().listRuns(threadId, options);\n },\n async cancel(threadId: string, runId: string, wait?: boolean, action?: CancelAction): Promise<void> {\n return await LangGraphGlobal.globalMessageQueue.cancelQueue(runId);\n },\n async *stream(threadId: string, assistantId: string, payload: StreamInputData) {\n payload.config = {\n ...(payload.config ?? {}),\n configurable: {\n ...(payload.config?.configurable ?? {}),\n graph_id: assistantId,\n thread_id: threadId,\n },\n };\n const threads = getThreads();\n const runPromise = threads.createRun(threadId, assistantId, payload);\n\n try {\n // 执行流处理\n for await (const data of streamState(threads, runPromise, payload, { attempt: 0, getGraph })) {\n yield data;\n }\n\n // 流结束后生成标题\n const run = await runPromise;\n await generateThreadTitle(\n threads as BaseThreadsManager<{ messages: any[] }>,\n threadId,\n assistantId,\n run.run_id,\n );\n } catch (error) {\n // 即使流失败,也尝试生成标题(如果已有部分 state)\n try {\n const run = await runPromise;\n await generateThreadTitle(\n threads as BaseThreadsManager<{ messages: any[] }>,\n threadId,\n assistantId,\n run.run_id,\n );\n } catch {\n // 忽略标题生成错误\n }\n throw error;\n }\n },\n async *joinStream(\n threadId: string,\n runId: string,\n options?:\n | {\n signal?: AbortSignal;\n cancelOnDisconnect?: boolean;\n lastEventId?: string;\n streamMode?: StreamMode | StreamMode[];\n }\n | AbortSignal,\n ): AsyncGenerator<{ id?: string; event: StreamEvent; data: any }> {\n // 处理参数兼容性\n const config = options && typeof options === 'object' && 'signal' in options ? options : {};\n const signal =\n (options instanceof AbortSignal ? options : config.signal) || new AbortController().signal;\n\n let queue: BaseStreamQueueInterface | null = null;\n let generator: AsyncGenerator<EventMessage, void, unknown> | null = null;\n\n try {\n // 获取队列实例\n queue = await LangGraphGlobal.globalMessageQueue.getQueue(runId);\n\n // 获取历史数据\n const allData = await queue.getAll();\n for (const eventMessage of allData) {\n // 检查是否被取消\n if (signal.aborted) {\n return;\n }\n\n yield {\n id: eventMessage.id,\n event: eventMessage.event as unknown as StreamEvent,\n data: eventMessage.data,\n };\n // 如果是流结束信号,停止监听\n if (\n eventMessage.event === '__stream_end__' ||\n eventMessage.event === '__stream_error__' ||\n eventMessage.event === '__stream_cancel__'\n ) {\n return;\n }\n }\n\n // 监听队列数据并转换格式\n generator = queue.onDataReceive();\n for await (const eventMessage of generator) {\n // 检查是否被取消\n if (signal.aborted) {\n break;\n }\n\n // 转换 EventMessage 为期望的格式\n const event = eventMessage.event as unknown as StreamEvent;\n const data = eventMessage.data;\n\n yield {\n id: eventMessage.id,\n event,\n data,\n };\n\n // 如果是流结束信号,停止监听\n if (\n eventMessage.event === '__stream_end__' ||\n eventMessage.event === '__stream_error__' ||\n eventMessage.event === '__stream_cancel__'\n ) {\n break;\n }\n }\n } catch (error) {\n // 如果队列不存在或其他错误,记录警告但不抛出错误\n if (!(error instanceof Error) || !error.message.includes('does not exist')) {\n console.warn('Join stream failed:', error);\n }\n } finally {\n // 清理生成器,释放资源\n if (generator) {\n try {\n await generator.return(undefined);\n } catch (e) {\n // 忽略生成器清理错误\n }\n }\n // 注意:不在这里清理队列,队列由 streamState 在运行完成时统一清理\n }\n },\n },\n };\n};\n"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import { StreamEvent } from '@langchain/core/tracers/log_stream';
2
2
  import { Assistant, Run, StreamMode, Metadata, AssistantGraph } from '@langchain/langgraph-sdk';
3
3
  import { AssistantSortBy, CancelAction, ILangGraphClient, RunStatus, SortOrder, StreamInputData } from './types.js';
4
4
  import type { EventMessage } from './queue/event_message.js';
5
+ import { BaseThreadsManager } from './threads/index.js';
5
6
  export { registerGraph } from './utils/getGraph.js';
6
7
  export declare const AssistantEndpoint: ILangGraphClient['assistants'];
7
8
  export declare const createEndpoint: () => {
@@ -43,7 +44,7 @@ export declare const createEndpoint: () => {
43
44
  ifExists?: "raise" | "do_nothing";
44
45
  }): Promise<Assistant>;
45
46
  };
46
- readonly threads: import("./index.js").BaseThreadsManager<unknown>;
47
+ readonly threads: BaseThreadsManager<unknown>;
47
48
  runs: {
48
49
  list(threadId: string, options?: {
49
50
  limit?: number;
package/dist/global.d.ts CHANGED
@@ -1,10 +1,25 @@
1
1
  import { BaseCheckpointSaver } from '@langchain/langgraph-checkpoint';
2
2
  import { BaseStreamQueueInterface, StreamQueueManager } from './queue/stream_queue.js';
3
3
  import { BaseThreadsManager } from './threads/index.js';
4
+ import { TitleGenerator } from './utils/titleGenerator.js';
4
5
  export declare class LangGraphGlobal {
5
6
  static globalMessageQueue: StreamQueueManager<BaseStreamQueueInterface>;
6
7
  static globalCheckPointer: BaseCheckpointSaver;
7
8
  static globalThreadsManager: BaseThreadsManager;
8
9
  static isInitialized: Promise<void> | null;
10
+ /**
11
+ * 全局标题生成器
12
+ * 可通过 setTitleGenerator 替换
13
+ */
14
+ private static _titleGenerator;
15
+ /**
16
+ * 设置自定义标题生成器
17
+ * @param generator 标题生成函数,传入 null 可禁用标题生成
18
+ */
19
+ static setTitleGenerator(generator: TitleGenerator): void;
20
+ /**
21
+ * 获取当前标题生成器
22
+ */
23
+ static getTitleGenerator(): TitleGenerator;
9
24
  static initGlobal(): Promise<void>;
10
25
  }
package/dist/index.d.ts CHANGED
@@ -4,3 +4,4 @@ export * from './global';
4
4
  export * from './threads/index';
5
5
  export * from './utils/createStateEntrypoint';
6
6
  export * from './agents/index';
7
+ export * from './utils/titleGenerator';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- export { A as AssistantEndpoint, c as createEndpoint } from './createEndpoint-Cz6LSXYH.js';
2
- export { L as LangGraphGlobal, r as registerGraph } from './stream-Dm5628RW.js';
1
+ export { A as AssistantEndpoint, c as createEndpoint } from './createEndpoint-D27R5Yhn.js';
2
+ export { L as LangGraphGlobal, d as defaultTitleGenerator, r as registerGraph } from './stream-Bleed1lp.js';
3
3
  import { entrypoint, getPreviousState, getConfig, Command } from '@langchain/langgraph';
4
4
  import { schemaMetaRegistry } from '@langchain/langgraph/zod';
5
5
  import { getDefaultsForSchema } from 'zod-defaults';
@@ -1,4 +1,4 @@
1
- import { B as BaseStreamQueue, C as CancelEventMessage } from './stream-Dm5628RW.js';
1
+ import { B as BaseStreamQueue, C as CancelEventMessage } from './stream-Bleed1lp.js';
2
2
  import { createClient } from 'redis';
3
3
 
4
4
  let sharedRedisClient = null;
@@ -254,4 +254,4 @@ class RedisStreamQueue extends BaseStreamQueue {
254
254
  }
255
255
 
256
256
  export { RedisStreamQueue };
257
- //# sourceMappingURL=queue-swFecaQs.js.map
257
+ //# sourceMappingURL=queue-CS3K-zd_.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"queue-swFecaQs.js","sources":["../src/storage/redis/queue.ts"],"sourcesContent":["import { CancelEventMessage, EventMessage } from '../../queue/event_message.js';\nimport { BaseStreamQueue } from '../../queue/stream_queue.js';\nimport { BaseStreamQueueInterface } from '../../queue/stream_queue.js';\nimport { createClient, RedisClientType } from 'redis';\n\n// 全局共享的 Redis 连接池\nlet sharedRedisClient: RedisClientType | null = null;\nlet connectionRefCount = 0;\nlet connectionPromise: Promise<void> | null = null;\nlet releaseTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\n/**\n * 获取共享的 Redis 连接(线程安全)\n */\nasync function getSharedRedisClient(): Promise<RedisClientType> {\n // 如果已有连接,直接返回\n if (sharedRedisClient && sharedRedisClient.isOpen) {\n connectionRefCount++;\n return sharedRedisClient;\n }\n\n // 如果正在建立连接,等待现有 Promise\n if (connectionPromise) {\n await connectionPromise;\n if (sharedRedisClient) {\n connectionRefCount++;\n return sharedRedisClient;\n }\n // 连接失败,继续尝试创建\n }\n\n // 创建新的连接 Promise,防止并发创建多个连接\n connectionPromise = (async () => {\n const client = createClient({\n url: process.env.REDIS_URL,\n });\n await client.connect();\n sharedRedisClient = client as RedisClientType;\n })();\n\n try {\n await connectionPromise;\n connectionRefCount++;\n return sharedRedisClient!;\n } catch (error) {\n // 连接失败时清理状态\n connectionPromise = null;\n throw error;\n } finally {\n connectionPromise = null;\n }\n}\n\n/**\n * 释放 Redis 连接引用\n */\nasync function releaseRedisClient(): Promise<void> {\n if (connectionRefCount > 0) {\n connectionRefCount--;\n }\n\n // 引用计数为 0 且超过一定时间没有新连接时才关闭\n // 避免频繁开关连接\n if (connectionRefCount <= 0 && sharedRedisClient) {\n // 清理之前的延迟关闭定时器\n if (releaseTimeoutId) {\n clearTimeout(releaseTimeoutId);\n releaseTimeoutId = null;\n }\n\n // 延迟关闭,给其他队列复用连接的机会\n releaseTimeoutId = setTimeout(async () => {\n if (connectionRefCount <= 0 && sharedRedisClient) {\n try {\n await sharedRedisClient.quit();\n } catch (e) {\n // 忽略关闭错误\n }\n sharedRedisClient = null;\n connectionRefCount = 0;\n releaseTimeoutId = null;\n }\n }, 5000);\n }\n}\n\n/**\n * Redis Stream 实现的消息队列,用于存储消息\n * 使用 Redis Streams 替代 pub/sub,支持集群模式\n */\nexport class RedisStreamQueue extends BaseStreamQueue implements BaseStreamQueueInterface {\n private redis: RedisClientType | null = null;\n private streamKey: string;\n private listKey: string;\n private isConnected = false;\n public cancelSignal: AbortController;\n private lastStreamId: string = '0'; // 最后读取的 Stream ID\n private pollInterval: number = 100; // 轮询间隔(毫秒)\n private connectionReady: Promise<void>;\n\n constructor(readonly id: string, readonly compressMessages: boolean = true, readonly ttl: number = 300) {\n super(id, true, ttl);\n this.streamKey = `stream:${this.id}`;\n this.listKey = `queue:${this.id}`;\n this.cancelSignal = new AbortController();\n\n // 异步初始化 Redis 连接\n this.connectionReady = this.initConnection();\n }\n\n /**\n * 初始化 Redis 连接(使用共享连接池)\n */\n private async initConnection(): Promise<void> {\n try {\n this.redis = await getSharedRedisClient();\n this.isConnected = true;\n } catch (error) {\n console.error('Failed to connect to Redis:', error);\n throw error;\n }\n }\n\n /**\n * 确保连接已建立\n */\n private async ensureConnected(): Promise<void> {\n if (!this.isConnected || !this.redis) {\n await this.connectionReady;\n }\n }\n\n /**\n * 推送消息到 Redis Stream 和 List\n * - Stream: 用于实时推送(集群友好)\n * - List: 用于 getAll() 批量获取历史数据\n */\n async push(item: EventMessage): Promise<void> {\n await this.ensureConnected();\n if (!this.redis) throw new Error('Redis connection not available');\n\n const encodedData = await this.encodeData(item);\n // 将 Uint8Array 转换为 base64 字符串,以便存储到 Redis Stream\n const dataString = Buffer.from(encodedData).toString('base64');\n const serializedData = Buffer.from(encodedData);\n\n // 推送到 Stream(实时推送)\n // 注意:xAdd 的第三个参数必须是简单的键值对对象,值必须是字符串\n await this.redis.xAdd(this.streamKey, '*', { data: dataString });\n\n // 设置 Stream TTL\n await this.redis.expire(this.streamKey, this.ttl);\n\n // 同时推送到 List(用于 getAll)\n await this.redis.rPush(this.listKey, serializedData);\n await this.redis.expire(this.listKey, this.ttl);\n\n this.emit('dataChange', dataString);\n }\n\n /**\n * 异步生成器:使用 Redis Streams XREAD 轮询消费队列数据\n */\n async *onDataReceive(): AsyncGenerator<EventMessage, void, unknown> {\n let isStreamEnded = false;\n let isCleanupDone = false;\n\n // 等待连接建立\n await this.ensureConnected();\n if (!this.redis) {\n throw new Error('Redis connection not available');\n }\n\n // 检查是否已取消\n if (this.cancelSignal.signal.aborted) {\n return;\n }\n\n // 监听取消信号\n const abortHandler = () => {\n isStreamEnded = true;\n };\n this.cancelSignal.signal.addEventListener('abort', abortHandler);\n\n const cleanup = () => {\n if (isCleanupDone) return;\n isCleanupDone = true;\n try {\n this.cancelSignal.signal.removeEventListener('abort', abortHandler);\n } catch (e) {\n console.error('Error removing abort listener:', e);\n }\n };\n\n try {\n while (!isStreamEnded && !this.cancelSignal.signal.aborted) {\n // 检查 Redis 连接是否仍然有效\n if (!this.redis || !this.isConnected) {\n break;\n }\n\n // 从 Stream 读取新消息(XREAD 阻塞读取)\n const streams = await this.redis.xRead([{ key: this.streamKey, id: this.lastStreamId }], {\n BLOCK: this.pollInterval,\n COUNT: 10,\n });\n\n if (streams && streams.length > 0) {\n for (const stream of streams) {\n for (const message of stream.messages) {\n // 更新最后读取的 ID\n this.lastStreamId = message.id;\n\n // 解析消息:从 base64 字符串转换回 Uint8Array\n const dataString = message.message.data as string;\n const data = Buffer.from(dataString, 'base64');\n const item = (await this.decodeData(data)) as EventMessage;\n\n // 检查是否为流结束或错误信号\n if (\n item.event === '__stream_end__' ||\n item.event === '__stream_error__' ||\n item.event === '__stream_cancel__'\n ) {\n // 延迟 300ms 后结束,确保消息被消费\n await new Promise((resolve) => setTimeout(resolve, 300));\n isStreamEnded = true;\n\n if (item.event === '__stream_cancel__') {\n await this.cancel();\n }\n }\n\n yield item;\n\n if (isStreamEnded) {\n break;\n }\n }\n if (isStreamEnded) {\n break;\n }\n }\n }\n\n // 轮询间隔\n if (!isStreamEnded && !this.cancelSignal.signal.aborted) {\n await new Promise((resolve) => setTimeout(resolve, this.pollInterval));\n }\n }\n } finally {\n cleanup();\n }\n }\n\n /**\n * 获取队列中的所有数据(从 List 获取历史数据)\n */\n async getAll(): Promise<EventMessage[]> {\n await this.ensureConnected();\n if (!this.redis) return [];\n\n const data = await this.redis.lRange(this.listKey, 0, -1);\n\n if (!data || data.length === 0) {\n return [];\n }\n\n if (this.compressMessages) {\n return (await Promise.all(\n data.map((item: Buffer | string) => {\n // 处理 Buffer 或字符串类型\n const buffer = typeof item === 'string' ? Buffer.from(item, 'binary') : item;\n return this.decodeData(buffer);\n }),\n )) as EventMessage[];\n } else {\n return data.map((item: string) => JSON.parse(item) as EventMessage);\n }\n }\n\n /**\n * 清空队列\n */\n async clear(): Promise<void> {\n if (this.isConnected && this.redis) {\n // 同时清空 Stream 和 List\n await Promise.all([this.redis.del(this.streamKey), this.redis.del(this.listKey)]);\n }\n }\n\n /**\n * 销毁队列实例,释放 Redis 连接引用\n */\n async destroy(): Promise<void> {\n await this.clear();\n this.isConnected = false;\n this.redis = null;\n await releaseRedisClient();\n }\n\n /**\n * 取消操作\n */\n async cancel(): Promise<void> {\n // First abort to stop any waiting generators\n this.cancelSignal.abort('user cancel this run');\n // Then push the cancel message to signal other consumers\n await this.push(new CancelEventMessage());\n }\n\n /**\n * 复制队列到另一个队列\n */\n async copyToQueue(toId: string, ttl?: number): Promise<RedisStreamQueue> {\n await this.ensureConnected();\n if (!this.redis) throw new Error('Redis connection not available');\n\n const queue = new RedisStreamQueue(toId, this.compressMessages, ttl ?? this.ttl);\n await queue.ensureConnected();\n\n if (!queue.redis) throw new Error('Target Redis connection not available');\n\n // 复制 List\n await this.redis.copy(this.listKey, queue.listKey);\n await this.redis.expire(queue.listKey, ttl ?? this.ttl);\n\n // 复制 Stream(需要遍历并重新添加)\n const allStreamData = await this.redis.xRange(this.streamKey, '-', '+');\n if (allStreamData && allStreamData.length > 0) {\n for (const message of allStreamData) {\n // 确保所有值都是字符串,Redis Streams 只支持 string 值\n const fields: Record<string, string> = {};\n for (const [key, value] of Object.entries(message.message)) {\n fields[key] = String(value);\n }\n await this.redis.xAdd(queue.streamKey, '*', fields);\n }\n await this.redis.expire(queue.streamKey, ttl ?? this.ttl);\n }\n\n return queue;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"queue-CS3K-zd_.js","sources":["../src/storage/redis/queue.ts"],"sourcesContent":["import { CancelEventMessage, EventMessage } from '../../queue/event_message.js';\nimport { BaseStreamQueue } from '../../queue/stream_queue.js';\nimport { BaseStreamQueueInterface } from '../../queue/stream_queue.js';\nimport { createClient, RedisClientType } from 'redis';\n\n// 全局共享的 Redis 连接池\nlet sharedRedisClient: RedisClientType | null = null;\nlet connectionRefCount = 0;\nlet connectionPromise: Promise<void> | null = null;\nlet releaseTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\n/**\n * 获取共享的 Redis 连接(线程安全)\n */\nasync function getSharedRedisClient(): Promise<RedisClientType> {\n // 如果已有连接,直接返回\n if (sharedRedisClient && sharedRedisClient.isOpen) {\n connectionRefCount++;\n return sharedRedisClient;\n }\n\n // 如果正在建立连接,等待现有 Promise\n if (connectionPromise) {\n await connectionPromise;\n if (sharedRedisClient) {\n connectionRefCount++;\n return sharedRedisClient;\n }\n // 连接失败,继续尝试创建\n }\n\n // 创建新的连接 Promise,防止并发创建多个连接\n connectionPromise = (async () => {\n const client = createClient({\n url: process.env.REDIS_URL,\n });\n await client.connect();\n sharedRedisClient = client as RedisClientType;\n })();\n\n try {\n await connectionPromise;\n connectionRefCount++;\n return sharedRedisClient!;\n } catch (error) {\n // 连接失败时清理状态\n connectionPromise = null;\n throw error;\n } finally {\n connectionPromise = null;\n }\n}\n\n/**\n * 释放 Redis 连接引用\n */\nasync function releaseRedisClient(): Promise<void> {\n if (connectionRefCount > 0) {\n connectionRefCount--;\n }\n\n // 引用计数为 0 且超过一定时间没有新连接时才关闭\n // 避免频繁开关连接\n if (connectionRefCount <= 0 && sharedRedisClient) {\n // 清理之前的延迟关闭定时器\n if (releaseTimeoutId) {\n clearTimeout(releaseTimeoutId);\n releaseTimeoutId = null;\n }\n\n // 延迟关闭,给其他队列复用连接的机会\n releaseTimeoutId = setTimeout(async () => {\n if (connectionRefCount <= 0 && sharedRedisClient) {\n try {\n await sharedRedisClient.quit();\n } catch (e) {\n // 忽略关闭错误\n }\n sharedRedisClient = null;\n connectionRefCount = 0;\n releaseTimeoutId = null;\n }\n }, 5000);\n }\n}\n\n/**\n * Redis Stream 实现的消息队列,用于存储消息\n * 使用 Redis Streams 替代 pub/sub,支持集群模式\n */\nexport class RedisStreamQueue extends BaseStreamQueue implements BaseStreamQueueInterface {\n private redis: RedisClientType | null = null;\n private streamKey: string;\n private listKey: string;\n private isConnected = false;\n public cancelSignal: AbortController;\n private lastStreamId: string = '0'; // 最后读取的 Stream ID\n private pollInterval: number = 100; // 轮询间隔(毫秒)\n private connectionReady: Promise<void>;\n\n constructor(readonly id: string, readonly compressMessages: boolean = true, readonly ttl: number = 300) {\n super(id, true, ttl);\n this.streamKey = `stream:${this.id}`;\n this.listKey = `queue:${this.id}`;\n this.cancelSignal = new AbortController();\n\n // 异步初始化 Redis 连接\n this.connectionReady = this.initConnection();\n }\n\n /**\n * 初始化 Redis 连接(使用共享连接池)\n */\n private async initConnection(): Promise<void> {\n try {\n this.redis = await getSharedRedisClient();\n this.isConnected = true;\n } catch (error) {\n console.error('Failed to connect to Redis:', error);\n throw error;\n }\n }\n\n /**\n * 确保连接已建立\n */\n private async ensureConnected(): Promise<void> {\n if (!this.isConnected || !this.redis) {\n await this.connectionReady;\n }\n }\n\n /**\n * 推送消息到 Redis Stream 和 List\n * - Stream: 用于实时推送(集群友好)\n * - List: 用于 getAll() 批量获取历史数据\n */\n async push(item: EventMessage): Promise<void> {\n await this.ensureConnected();\n if (!this.redis) throw new Error('Redis connection not available');\n\n const encodedData = await this.encodeData(item);\n // 将 Uint8Array 转换为 base64 字符串,以便存储到 Redis Stream\n const dataString = Buffer.from(encodedData).toString('base64');\n const serializedData = Buffer.from(encodedData);\n\n // 推送到 Stream(实时推送)\n // 注意:xAdd 的第三个参数必须是简单的键值对对象,值必须是字符串\n await this.redis.xAdd(this.streamKey, '*', { data: dataString });\n\n // 设置 Stream TTL\n await this.redis.expire(this.streamKey, this.ttl);\n\n // 同时推送到 List(用于 getAll)\n await this.redis.rPush(this.listKey, serializedData);\n await this.redis.expire(this.listKey, this.ttl);\n\n this.emit('dataChange', dataString);\n }\n\n /**\n * 异步生成器:使用 Redis Streams XREAD 轮询消费队列数据\n */\n async *onDataReceive(): AsyncGenerator<EventMessage, void, unknown> {\n let isStreamEnded = false;\n let isCleanupDone = false;\n\n // 等待连接建立\n await this.ensureConnected();\n if (!this.redis) {\n throw new Error('Redis connection not available');\n }\n\n // 检查是否已取消\n if (this.cancelSignal.signal.aborted) {\n return;\n }\n\n // 监听取消信号\n const abortHandler = () => {\n isStreamEnded = true;\n };\n this.cancelSignal.signal.addEventListener('abort', abortHandler);\n\n const cleanup = () => {\n if (isCleanupDone) return;\n isCleanupDone = true;\n try {\n this.cancelSignal.signal.removeEventListener('abort', abortHandler);\n } catch (e) {\n console.error('Error removing abort listener:', e);\n }\n };\n\n try {\n while (!isStreamEnded && !this.cancelSignal.signal.aborted) {\n // 检查 Redis 连接是否仍然有效\n if (!this.redis || !this.isConnected) {\n break;\n }\n\n // 从 Stream 读取新消息(XREAD 阻塞读取)\n const streams = await this.redis.xRead([{ key: this.streamKey, id: this.lastStreamId }], {\n BLOCK: this.pollInterval,\n COUNT: 10,\n });\n\n if (streams && streams.length > 0) {\n for (const stream of streams) {\n for (const message of stream.messages) {\n // 更新最后读取的 ID\n this.lastStreamId = message.id;\n\n // 解析消息:从 base64 字符串转换回 Uint8Array\n const dataString = message.message.data as string;\n const data = Buffer.from(dataString, 'base64');\n const item = (await this.decodeData(data)) as EventMessage;\n\n // 检查是否为流结束或错误信号\n if (\n item.event === '__stream_end__' ||\n item.event === '__stream_error__' ||\n item.event === '__stream_cancel__'\n ) {\n // 延迟 300ms 后结束,确保消息被消费\n await new Promise((resolve) => setTimeout(resolve, 300));\n isStreamEnded = true;\n\n if (item.event === '__stream_cancel__') {\n await this.cancel();\n }\n }\n\n yield item;\n\n if (isStreamEnded) {\n break;\n }\n }\n if (isStreamEnded) {\n break;\n }\n }\n }\n\n // 轮询间隔\n if (!isStreamEnded && !this.cancelSignal.signal.aborted) {\n await new Promise((resolve) => setTimeout(resolve, this.pollInterval));\n }\n }\n } finally {\n cleanup();\n }\n }\n\n /**\n * 获取队列中的所有数据(从 List 获取历史数据)\n */\n async getAll(): Promise<EventMessage[]> {\n await this.ensureConnected();\n if (!this.redis) return [];\n\n const data = await this.redis.lRange(this.listKey, 0, -1);\n\n if (!data || data.length === 0) {\n return [];\n }\n\n if (this.compressMessages) {\n return (await Promise.all(\n data.map((item: Buffer | string) => {\n // 处理 Buffer 或字符串类型\n const buffer = typeof item === 'string' ? Buffer.from(item, 'binary') : item;\n return this.decodeData(buffer);\n }),\n )) as EventMessage[];\n } else {\n return data.map((item: string) => JSON.parse(item) as EventMessage);\n }\n }\n\n /**\n * 清空队列\n */\n async clear(): Promise<void> {\n if (this.isConnected && this.redis) {\n // 同时清空 Stream 和 List\n await Promise.all([this.redis.del(this.streamKey), this.redis.del(this.listKey)]);\n }\n }\n\n /**\n * 销毁队列实例,释放 Redis 连接引用\n */\n async destroy(): Promise<void> {\n await this.clear();\n this.isConnected = false;\n this.redis = null;\n await releaseRedisClient();\n }\n\n /**\n * 取消操作\n */\n async cancel(): Promise<void> {\n // First abort to stop any waiting generators\n this.cancelSignal.abort('user cancel this run');\n // Then push the cancel message to signal other consumers\n await this.push(new CancelEventMessage());\n }\n\n /**\n * 复制队列到另一个队列\n */\n async copyToQueue(toId: string, ttl?: number): Promise<RedisStreamQueue> {\n await this.ensureConnected();\n if (!this.redis) throw new Error('Redis connection not available');\n\n const queue = new RedisStreamQueue(toId, this.compressMessages, ttl ?? this.ttl);\n await queue.ensureConnected();\n\n if (!queue.redis) throw new Error('Target Redis connection not available');\n\n // 复制 List\n await this.redis.copy(this.listKey, queue.listKey);\n await this.redis.expire(queue.listKey, ttl ?? this.ttl);\n\n // 复制 Stream(需要遍历并重新添加)\n const allStreamData = await this.redis.xRange(this.streamKey, '-', '+');\n if (allStreamData && allStreamData.length > 0) {\n for (const message of allStreamData) {\n // 确保所有值都是字符串,Redis Streams 只支持 string 值\n const fields: Record<string, string> = {};\n for (const [key, value] of Object.entries(message.message)) {\n fields[key] = String(value);\n }\n await this.redis.xAdd(queue.streamKey, '*', fields);\n }\n await this.redis.expire(queue.streamKey, ttl ?? this.ttl);\n }\n\n return queue;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { Hono } from 'hono';
2
2
  import { PostgresAdapter } from '../pg-adapter-BFtir1GE.js';
3
- import { K as KyselyThreadsManager } from '../stream-Dm5628RW.js';
3
+ import { K as KyselyThreadsManager } from '../stream-Bleed1lp.js';
4
4
  import { Pool } from 'pg';
5
5
 
6
6
  class RemoteServer {
@@ -249,7 +249,25 @@ class RemoteKyselyThreadsManager {
249
249
  const response = await remotePost(`${this.serverUrl}/threads/${threadId}/copy`);
250
250
  return response.data;
251
251
  }
252
+ /**
253
+ * 原子性地设置标题(仅当标题为空时)
254
+ */
255
+ async setTitleIfNull(threadId, title) {
256
+ try {
257
+ const response = await remotePost(`${this.serverUrl}/threads/${threadId}/title`, {
258
+ title
259
+ });
260
+ return response.data.success;
261
+ } catch (error) {
262
+ const thread = await this.get(threadId);
263
+ if (thread?.title === null || thread?.title === void 0) {
264
+ await this.set(threadId, { title });
265
+ return true;
266
+ }
267
+ return false;
268
+ }
269
+ }
252
270
  }
253
271
 
254
272
  export { RemoteKyselyThreadsManager };
255
- //# sourceMappingURL=remote-threads-CrG03ZS7.js.map
273
+ //# sourceMappingURL=remote-threads-UpSB8X-e.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-threads-UpSB8X-e.js","sources":["../src/storage/remote/types.ts","../src/storage/remote/fetch.ts","../src/storage/kysely/remote-threads.ts"],"sourcesContent":["/**\n * Remote PostgreSQL Adapter 类型定义\n * 定义 API 请求/响应格式和错误类型\n */\n\nimport { Metadata, OnConflictBehavior, Run, Thread, ThreadStatus, Command } from '@langgraph-js/sdk';\nimport { RunStatus, SortOrder, ThreadSortBy } from '../../types';\n\n/**\n * API 响应通用格式\n */\nexport interface RemoteResponse<T = any> {\n success: boolean;\n data?: T;\n error?: {\n code: string;\n message: string;\n };\n}\n\n/**\n * 错误码枚举\n */\nexport enum RemoteErrorCode {\n // 网络错误\n NETWORK_ERROR = 'NETWORK_ERROR',\n CONNECTION_TIMEOUT = 'CONNECTION_TIMEOUT',\n\n // 服务器错误\n INTERNAL_ERROR = 'INTERNAL_ERROR',\n\n // 业务错误\n THREAD_NOT_FOUND = 'THREAD_NOT_FOUND',\n THREAD_BUSY = 'THREAD_BUSY',\n RUN_NOT_FOUND = 'RUN_NOT_FOUND',\n GRAPH_NOT_FOUND = 'GRAPH_NOT_FOUND',\n INVALID_REQUEST = 'INVALID_REQUEST',\n}\n\n/**\n * 远程 API 错误类\n */\nexport class RemoteApiError extends Error {\n constructor(\n public code: string,\n message: string,\n public statusCode?: number\n ) {\n super(message);\n this.name = 'RemoteApiError';\n }\n}\n\n/**\n * Setup API 响应\n */\nexport interface SetupResponse {\n message: string;\n}\n\n/**\n * Create Thread API 请求\n */\nexport interface CreateThreadRequest {\n metadata?: Metadata;\n threadId?: string;\n ifExists?: OnConflictBehavior;\n graphId?: string;\n supersteps?: Array<{ updates: Array<{ values: unknown; command?: Command; asNode: string }> }>;\n}\n\n/**\n * Search Threads API 请求\n */\nexport interface SearchThreadsRequest {\n ids?: string[];\n metadata?: Metadata;\n limit?: number;\n offset?: number;\n status?: ThreadStatus;\n sortBy?: ThreadSortBy;\n sortOrder?: SortOrder;\n values?: unknown;\n select?: Array<'thread_id' | 'created_at' | 'updated_at' | 'metadata' | 'config' | 'context' | 'status' | 'values' | 'interrupts'>;\n /**\n * @deprecated Use `select` parameter instead for fine-grained field control\n */\n withoutDetails?: boolean;\n}\n\n/**\n * Update Thread API 请求\n */\nexport interface UpdateThreadRequest {\n metadata?: Metadata;\n status?: ThreadStatus;\n values?: any;\n interrupts?: Record<string, any>;\n}\n\n/**\n * Update State API 请求\n */\nexport interface UpdateStateRequest {\n values?: any;\n}\n\n/**\n * Update State API 响应\n */\nexport interface UpdateStateResponse {\n configurable: Record<string, any>;\n}\n\n/**\n * Create Run API 请求\n */\nexport interface CreateRunRequest {\n metadata?: Metadata;\n}\n\n/**\n * List Runs API 请求\n */\nexport interface ListRunsRequest {\n limit?: number;\n offset?: number;\n status?: RunStatus;\n}\n\n/**\n * Update Run API 请求\n */\nexport interface UpdateRunRequest {\n status?: RunStatus;\n metadata?: Metadata;\n multitask_strategy?: 'reject' | 'interrupt' | 'rollback';\n}\n","/**\n * Remote PostgreSQL Adapter - 简化的 Fetch 工具函数\n */\n\nimport { RemoteResponse } from './types';\nimport { RemoteApiError, RemoteErrorCode } from './types';\n\n/**\n * 发起 HTTP 请求的简化函数\n */\nasync function request<T>(\n url: string,\n method: string,\n options?: {\n query?: Record<string, string | number | boolean>;\n body?: any;\n },\n): Promise<RemoteResponse<T>> {\n try {\n // 添加查询参数\n let requestUrl = url;\n if (options?.query) {\n const searchParams = new URLSearchParams();\n Object.entries(options.query).forEach(([key, value]) => {\n searchParams.append(key, String(value));\n });\n requestUrl += `?${searchParams.toString()}`;\n }\n\n // 发起请求\n const response = await fetch(requestUrl, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n },\n body: options?.body ? JSON.stringify(options.body) : undefined,\n });\n\n // 解析响应\n const data: RemoteResponse<T> = (await response.json()) as any;\n\n // 检查响应状态\n if (!response.ok || !data.success) {\n throw new RemoteApiError(\n data.error?.code || RemoteErrorCode.INTERNAL_ERROR,\n data.error?.message || 'Unknown error',\n response.status,\n );\n }\n\n return data;\n } catch (error) {\n // 如果是 RemoteApiError,直接抛出\n if (error instanceof RemoteApiError) {\n throw error;\n }\n\n // 网络错误转换为 RemoteApiError\n throw new RemoteApiError(\n RemoteErrorCode.NETWORK_ERROR,\n `Network error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n}\n\n/**\n * GET 请求\n */\nexport async function remoteGet<T>(\n url: string,\n query?: Record<string, string | number | boolean>,\n): Promise<RemoteResponse<T>> {\n return request<T>(url, 'GET', { query });\n}\n\n/**\n * POST 请求\n */\nexport async function remotePost<T>(\n url: string,\n body?: any,\n query?: Record<string, string | number | boolean>,\n): Promise<RemoteResponse<T>> {\n return request<T>(url, 'POST', { body, query });\n}\n\n/**\n * PUT 请求\n */\nexport async function remotePut<T>(\n url: string,\n body?: any,\n query?: Record<string, string | number | boolean>,\n): Promise<RemoteResponse<T>> {\n return request<T>(url, 'PUT', { body, query });\n}\n\n/**\n * DELETE 请求\n */\nexport async function remoteDelete<T>(\n url: string,\n query?: Record<string, string | number | boolean>,\n): Promise<RemoteResponse<T>> {\n return request<T>(url, 'DELETE', { query });\n}\n","/**\n * Remote Kysely Threads Manager\n * 通过 HTTP/REST API 与远程 PostgreSQL 服务器通信\n */\n\nimport { BaseThreadsManager } from '../../threads';\nimport { Metadata, OnConflictBehavior, Run, Thread, ThreadState } from '@langgraph-js/sdk';\nimport { RunStatus, SortOrder, ThreadSortBy } from '../../types';\nimport { remoteGet, remotePost, remotePut, remoteDelete } from '../remote/fetch';\nimport { RemoteApiError, RemoteErrorCode } from '../remote/types';\n\n/**\n * 远程 Kysely Threads Manager\n */\nexport class RemoteKyselyThreadsManager<ValuesType = unknown> implements BaseThreadsManager<ValuesType> {\n constructor(\n private serverUrl: string,\n private httpClient?: typeof fetch,\n ) {\n // 确保服务器 URL 没有尾部斜杠\n this.serverUrl = serverUrl.replace(/\\/$/, '');\n this.httpClient = httpClient || fetch;\n }\n\n /**\n * 初始化数据库\n */\n async setup(): Promise<void> {\n await remotePost(`${this.serverUrl}/setup`);\n }\n\n /**\n * 创建线程\n */\n async create(payload?: {\n metadata?: Metadata;\n threadId?: string;\n ifExists?: OnConflictBehavior;\n graphId?: string;\n supersteps?: Array<{ updates: Array<{ values: unknown; command?: any; asNode: string }> }>;\n }): Promise<Thread<ValuesType>> {\n const response = await remotePost<Thread<ValuesType>>(`${this.serverUrl}/threads`, payload);\n return response.data as Thread<ValuesType>;\n }\n\n /**\n * 搜索线程\n */\n async search(query?: {\n ids?: string[];\n metadata?: Metadata;\n limit?: number;\n offset?: number;\n status?: any;\n sortBy?: ThreadSortBy;\n sortOrder?: SortOrder;\n values?: ValuesType;\n select?: Array<\n | 'thread_id'\n | 'created_at'\n | 'updated_at'\n | 'metadata'\n | 'config'\n | 'context'\n | 'status'\n | 'values'\n | 'interrupts'\n >;\n /**\n * @deprecated Use `select` parameter instead for fine-grained field control\n */\n withoutDetails?: boolean;\n }): Promise<Thread<ValuesType>[]> {\n const params: Record<string, string | number | boolean> = {};\n\n if (query?.ids !== undefined && query.ids.length > 0) {\n params.ids = JSON.stringify(query.ids);\n }\n if (query?.metadata !== undefined) {\n params.metadata = JSON.stringify(query.metadata);\n }\n if (query?.limit !== undefined) {\n params.limit = query.limit;\n }\n if (query?.offset !== undefined) {\n params.offset = query.offset;\n }\n if (query?.status !== undefined) {\n params.status = query.status;\n }\n if (query?.sortBy !== undefined) {\n params.sortBy = query.sortBy;\n }\n if (query?.sortOrder !== undefined) {\n params.sortOrder = query.sortOrder;\n }\n if (query?.values !== undefined) {\n params.values = JSON.stringify(query.values);\n }\n if (query?.select !== undefined) {\n params.select = JSON.stringify(query.select);\n }\n if (query?.withoutDetails !== undefined) {\n params.withoutDetails = query.withoutDetails;\n }\n\n const response = await remoteGet<Thread<ValuesType>[]>(`${this.serverUrl}/threads`, params);\n return response.data as Thread<ValuesType>[];\n }\n\n /**\n * 获取线程\n */\n async get(threadId: string): Promise<Thread<ValuesType>> {\n const response = await remoteGet<Thread<ValuesType>>(`${this.serverUrl}/threads/${threadId}`);\n return response.data as Thread<ValuesType>;\n }\n\n /**\n * 更新线程\n */\n async set(threadId: string, thread: Partial<Thread<ValuesType>>): Promise<void> {\n await remotePut(`${this.serverUrl}/threads/${threadId}`, thread);\n }\n\n /**\n * 删除线程\n */\n async delete(threadId: string): Promise<void> {\n await remoteDelete(`${this.serverUrl}/threads/${threadId}`);\n }\n\n /**\n * 更新状态\n */\n async updateState(\n threadId: string,\n thread: Partial<Thread<ValuesType>>,\n ): Promise<{ configurable: Record<string, any> }> {\n const response = await remotePost<{ configurable: Record<string, any> }>(\n `${this.serverUrl}/threads/${threadId}/state`,\n thread,\n );\n return response.data as { configurable: Record<string, any> };\n }\n\n /**\n * 创建运行\n */\n async createRun(threadId: string, assistantId: string, payload?: { metadata?: Metadata }): Promise<Run> {\n const response = await remotePost<Run>(`${this.serverUrl}/threads/${threadId}/runs`, payload || {}, {\n assistantId,\n });\n return response.data as Run;\n }\n\n /**\n * 列出运行\n */\n async listRuns(\n threadId: string,\n options?: { limit?: number; offset?: number; status?: RunStatus },\n ): Promise<Run[]> {\n const params: Record<string, string | number> = {};\n\n if (options?.limit !== undefined) {\n params.limit = options.limit;\n }\n if (options?.offset !== undefined) {\n params.offset = options.offset;\n }\n if (options?.status !== undefined) {\n params.status = options.status;\n }\n\n const response = await remoteGet<Run[]>(`${this.serverUrl}/threads/${threadId}/runs`, params);\n return response.data as Run[];\n }\n\n /**\n * 更新运行\n */\n async updateRun(runId: string, run: Partial<Run>): Promise<void> {\n await remotePut(`${this.serverUrl}/runs/${runId}`, run);\n }\n\n // New methods for Threads API\n\n /**\n * 计算线程数量\n */\n async count(query?: { ids?: string[]; metadata?: Metadata; status?: any; values?: ValuesType }): Promise<number> {\n const params: Record<string, string> = {};\n\n if (query?.ids !== undefined && query.ids.length > 0) {\n params.ids = JSON.stringify(query.ids);\n }\n if (query?.metadata !== undefined) {\n params.metadata = JSON.stringify(query.metadata);\n }\n if (query?.status !== undefined) {\n params.status = query.status;\n }\n if (query?.values !== undefined) {\n params.values = JSON.stringify(query.values);\n }\n\n const response = await remoteGet<number>(`${this.serverUrl}/threads/count`, params);\n return response.data as number;\n }\n\n /**\n * 更新线程元数据\n */\n async patch(\n threadId: string,\n updates: Partial<Omit<Thread<ValuesType>, 'thread_id' | 'created_at' | 'updated_at'>>,\n ): Promise<Thread<ValuesType>> {\n const response = await remotePost<Thread<ValuesType>>(`${this.serverUrl}/threads/${threadId}`, updates);\n return response.data as Thread<ValuesType>;\n }\n\n /**\n * 获取线程状态\n */\n async getState(threadId: string, options?: { subgraphs?: boolean; checkpointId?: string }): Promise<ThreadState> {\n const params: Record<string, boolean | string> = {};\n\n if (options?.subgraphs !== undefined) {\n params.subgraphs = options.subgraphs;\n }\n if (options?.checkpointId !== undefined) {\n params.checkpointId = options.checkpointId;\n }\n\n const response = await remotePost<ThreadState>(`${this.serverUrl}/threads/${threadId}/state`, params);\n return response.data as ThreadState;\n }\n\n /**\n * 获取线程历史\n */\n async getStateHistory(\n threadId: string,\n options?: {\n limit?: number;\n before?: string;\n filter?: { source?: string; step?: number };\n },\n ): Promise<ThreadState[]> {\n const params: Record<string, number | string> = {};\n\n if (options?.limit !== undefined) {\n params.limit = options.limit;\n }\n if (options?.before !== undefined) {\n params.before = options.before;\n }\n\n const response = await remotePost<ThreadState[]>(`${this.serverUrl}/threads/${threadId}/history`, params);\n return response.data as ThreadState[];\n }\n\n /**\n * 复制线程\n */\n async copy(threadId: string): Promise<Thread<ValuesType>> {\n const response = await remotePost<Thread<ValuesType>>(`${this.serverUrl}/threads/${threadId}/copy`);\n return response.data as Thread<ValuesType>;\n }\n\n /**\n * 原子性地设置标题(仅当标题为空时)\n */\n async setTitleIfNull(threadId: string, title: string): Promise<boolean> {\n try {\n const response = await remotePost<{ success: boolean }>(`${this.serverUrl}/threads/${threadId}/title`, {\n title,\n });\n return (response.data as { success: boolean }).success;\n } catch (error) {\n // 如果远程端不支持此操作,回退到普通设置\n const thread = await this.get(threadId);\n /** @ts-ignore */\n if (thread?.title === null || thread?.title === undefined) {\n await this.set(threadId, { title } as Partial<Thread<ValuesType>>);\n return true;\n }\n return false;\n }\n }\n}\n"],"names":["RemoteErrorCode"],"mappings":"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"}
@@ -73,9 +73,11 @@ class SQLiteAdapter {
73
73
  metadata TEXT NOT NULL DEFAULT '{}',
74
74
  status TEXT NOT NULL DEFAULT 'idle',
75
75
  "values" TEXT,
76
- interrupts TEXT NOT NULL DEFAULT '{}'
76
+ interrupts TEXT NOT NULL DEFAULT '{}',
77
+ title TEXT
77
78
  )
78
79
  `.execute(db);
80
+ await this.addColumnIfNotExists(db, "threads", "title", "TEXT");
79
81
  await sql`
80
82
  CREATE TABLE IF NOT EXISTS runs (
81
83
  run_id TEXT PRIMARY KEY,
@@ -90,6 +92,17 @@ class SQLiteAdapter {
90
92
  )
91
93
  `.execute(db);
92
94
  }
95
+ /**
96
+ * 幂等地添加列(如果列不存在)
97
+ * SQLite 不支持 IF NOT EXISTS for ALTER TABLE ADD COLUMN
98
+ */
99
+ async addColumnIfNotExists(db, table, column, type) {
100
+ const columns = await sql`PRAGMA table_info(${sql.raw(table)})`.execute(db);
101
+ const columnNames = columns.rows.map((row) => row.name);
102
+ if (!columnNames.includes(column)) {
103
+ await sql`ALTER TABLE ${sql.raw(table)} ADD COLUMN ${sql.raw(column)} ${sql.raw(type)}`.execute(db);
104
+ }
105
+ }
93
106
  async createIndexes(db) {
94
107
  await sql`CREATE INDEX IF NOT EXISTS idx_threads_status ON threads(status)`.execute(db);
95
108
  await sql`CREATE INDEX IF NOT EXISTS idx_threads_created_at ON threads(created_at)`.execute(db);
@@ -100,4 +113,4 @@ class SQLiteAdapter {
100
113
  }
101
114
 
102
115
  export { SQLiteAdapter };
103
- //# sourceMappingURL=sqlite-adapter-CJXgit1j.js.map
116
+ //# sourceMappingURL=sqlite-adapter-D5g0LbUq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-adapter-D5g0LbUq.js","sources":["../src/storage/kysely/sqlite-adapter.ts"],"sourcesContent":["import { Kysely, sql, SqlBool, Expression, SqliteDialect } from 'kysely';\nimport { DatabaseAdapter } from './adapter';\nimport { Database } from './types';\n\n/**\n * SQLite 适配器\n * - 时间类型:TEXT (ISO 8601 字符串)\n * - JSON 类型:TEXT\n * - JSON 查询:使用 json_extract 函数\n */\nexport class SQLiteAdapter implements DatabaseAdapter {\n db: Kysely<Database>;\n private pragmaPromise: Promise<void> | null = null;\n\n constructor(database: Kysely<any>) {\n this.db = database;\n }\n\n /**\n * 设置 SQLite PRAGMA 配置,解决锁问题\n * 使用 Promise 缓存确保并发安全且只执行一次\n */\n private ensurePragma(): Promise<void> {\n if (!this.pragmaPromise) {\n this.pragmaPromise = this.doEnsurePragma();\n }\n return this.pragmaPromise;\n }\n\n private async doEnsurePragma(): Promise<void> {\n // 锁等待超时 5 秒\n await sql`PRAGMA busy_timeout = 5000`.execute(this.db);\n // WAL 模式 - 读写并发\n await sql`PRAGMA journal_mode = WAL`.execute(this.db);\n // 平衡安全与性能\n await sql`PRAGMA synchronous = NORMAL`.execute(this.db);\n // 自动清理 WAL\n await sql`PRAGMA wal_autocheckpoint = 1000`.execute(this.db);\n }\n dateToDb(date: Date): string {\n // SQLite 存储为 ISO 8601 字符串\n return date.toISOString();\n }\n\n dbToDate(dbValue: any): Date {\n // SQLite 返回字符串,需要转换为 Date\n return new Date(dbValue);\n }\n\n jsonToDb(obj: any): string {\n // SQLite 存储为 JSON 字符串\n return JSON.stringify(obj);\n }\n\n dbToJson(dbValue: any): any {\n // SQLite 返回字符串,需要解析\n if (typeof dbValue === 'string') {\n try {\n return JSON.parse(dbValue);\n } catch {\n return dbValue;\n }\n }\n return dbValue;\n }\n\n buildJsonQuery(\n db: Kysely<Database>,\n field: 'metadata' | 'interrupts',\n key: string,\n value: any,\n ): Expression<SqlBool> {\n // SQLite json_extract 的行为:\n // - 字符串: 返回不带引号的文本\n // - 数字: 返回数字\n // - 布尔值: 返回 1/0\n // - null: 返回 NULL\n // 所以比较时需要根据类型处理\n\n // 构建 JSON 路径\n // 在 SQLite 中,所有键都需要用双引号括起来以确保正确解析\n // json_extract 语法: $.key 或 $.\"key-with-special\"\n const jsonPath = `$.${JSON.stringify(key)}`;\n\n // 构建 NULL 处理条件\n // 注意:SQLite 中 json_extract 对键不存在和值为 null 都返回 NULL\n // 使用 json_type 函数区分:\n // - 键不存在:json_type 返回 NULL\n // - 值为 null:json_type 返回 'null' 字符串\n if (value === null) {\n return sql<boolean>`\n json_type(${sql.ref(field)}, ${sql.lit(jsonPath)}) = 'null'\n `;\n }\n\n // 处理其他类型的值\n let compareValue: any;\n if (typeof value === 'string') {\n // 字符串: json_extract 返回不带引号的字符串,直接比较\n compareValue = value;\n } else if (typeof value === 'number') {\n // 数字: json_extract 返回数字\n compareValue = value;\n } else if (typeof value === 'boolean') {\n // 布尔值: json_extract 返回 1/0\n compareValue = value ? 1 : 0;\n } else {\n // 其他类型(对象、数组): 使用 JSON 字符串\n compareValue = JSON.stringify(value);\n }\n\n // 构建普通比较条件\n return sql<boolean>`json_extract(${sql.ref(field)}, ${sql.lit(jsonPath)}) = ${sql.lit(compareValue)}`;\n }\n\n now(): string {\n return new Date().toISOString();\n }\n\n async createTables(db: Kysely<Database>): Promise<void> {\n // 先设置 PRAGMA\n await this.ensurePragma();\n\n // 创建 threads 表\n await sql`\n CREATE TABLE IF NOT EXISTS threads (\n thread_id TEXT PRIMARY KEY,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n metadata TEXT NOT NULL DEFAULT '{}',\n status TEXT NOT NULL DEFAULT 'idle',\n \"values\" TEXT,\n interrupts TEXT NOT NULL DEFAULT '{}',\n title TEXT\n )\n `.execute(db);\n\n // 迁移:为已存在的表添加 title 字段(幂等)\n await this.addColumnIfNotExists(db, 'threads', 'title', 'TEXT');\n\n // 创建 runs 表\n await sql`\n CREATE TABLE IF NOT EXISTS runs (\n run_id TEXT PRIMARY KEY,\n thread_id TEXT NOT NULL,\n assistant_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending',\n metadata TEXT NOT NULL DEFAULT '{}',\n multitask_strategy TEXT NOT NULL DEFAULT 'reject',\n FOREIGN KEY (thread_id) REFERENCES threads(thread_id) ON DELETE CASCADE\n )\n `.execute(db);\n }\n\n /**\n * 幂等地添加列(如果列不存在)\n * SQLite 不支持 IF NOT EXISTS for ALTER TABLE ADD COLUMN\n */\n private async addColumnIfNotExists(db: Kysely<Database>, table: string, column: string, type: string): Promise<void> {\n // 查询表的列信息\n const columns = await sql<{ name: string }>`PRAGMA table_info(${sql.raw(table)})`.execute(db);\n const columnNames = columns.rows.map((row) => row.name);\n\n if (!columnNames.includes(column)) {\n await sql`ALTER TABLE ${sql.raw(table)} ADD COLUMN ${sql.raw(column)} ${sql.raw(type)}`.execute(db);\n }\n }\n\n async createIndexes(db: Kysely<Database>): Promise<void> {\n await sql`CREATE INDEX IF NOT EXISTS idx_threads_status ON threads(status)`.execute(db);\n await sql`CREATE INDEX IF NOT EXISTS idx_threads_created_at ON threads(created_at)`.execute(db);\n await sql`CREATE INDEX IF NOT EXISTS idx_threads_updated_at ON threads(updated_at)`.execute(db);\n await sql`CREATE INDEX IF NOT EXISTS idx_runs_thread_id ON runs(thread_id)`.execute(db);\n await sql`CREATE INDEX IF NOT EXISTS idx_runs_status ON runs(status)`.execute(db);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -121,4 +121,8 @@ export declare class RemoteKyselyThreadsManager<ValuesType = unknown> implements
121
121
  * 复制线程
122
122
  */
123
123
  copy(threadId: string): Promise<Thread<ValuesType>>;
124
+ /**
125
+ * 原子性地设置标题(仅当标题为空时)
126
+ */
127
+ setTitleIfNull(threadId: string, title: string): Promise<boolean>;
124
128
  }
@@ -24,5 +24,10 @@ export declare class SQLiteAdapter implements DatabaseAdapter {
24
24
  buildJsonQuery(db: Kysely<Database>, field: 'metadata' | 'interrupts', key: string, value: any): Expression<SqlBool>;
25
25
  now(): string;
26
26
  createTables(db: Kysely<Database>): Promise<void>;
27
+ /**
28
+ * 幂等地添加列(如果列不存在)
29
+ * SQLite 不支持 IF NOT EXISTS for ALTER TABLE ADD COLUMN
30
+ */
31
+ private addColumnIfNotExists;
27
32
  createIndexes(db: Kysely<Database>): Promise<void>;
28
33
  }
@@ -1,6 +1,6 @@
1
1
  import { BaseThreadsManager } from '../../threads/index.js';
2
- import { Command, Config, Metadata, OnConflictBehavior, Run, Thread, ThreadState, ThreadStatus } from '@langgraph-js/sdk';
3
- import { RunStatus, SortOrder, ThreadSortBy } from '../../types';
2
+ import { Command, Config, Metadata, OnConflictBehavior, Run, ThreadState, ThreadStatus } from '@langgraph-js/sdk';
3
+ import { RunStatus, SortOrder, ThreadSortBy, Thread } from '../../types';
4
4
  import { DatabaseAdapter } from './adapter';
5
5
  /**
6
6
  * 使用 Kysely 实现的统一 ThreadsManager
@@ -70,4 +70,5 @@ export declare class KyselyThreadsManager<ValuesType = unknown> implements BaseT
70
70
  }): Promise<ThreadState[]>;
71
71
  copy(threadId: string): Promise<Thread<ValuesType>>;
72
72
  private saveCheckpoint;
73
+ setTitleIfNull(threadId: string, title: string): Promise<boolean>;
73
74
  }
@@ -10,6 +10,7 @@ export interface ThreadsTable {
10
10
  status: string;
11
11
  values: any;
12
12
  interrupts: Record<string, any>;
13
+ title: string | null;
13
14
  }
14
15
  export interface CheckpointsTable {
15
16
  checkpoint_id: string;
@@ -1,6 +1,6 @@
1
1
  import { BaseThreadsManager } from '../../threads/index.js';
2
- import { Command, Config, Metadata, OnConflictBehavior, Run, Thread, ThreadState, ThreadStatus } from '@langgraph-js/sdk';
3
- import { RunStatus, SortOrder, ThreadSortBy } from '../../types';
2
+ import { Command, Config, Metadata, OnConflictBehavior, Run, ThreadState, ThreadStatus } from '@langgraph-js/sdk';
3
+ import { RunStatus, SortOrder, ThreadSortBy, Thread } from '../../types';
4
4
  export declare class MemoryThreadsManager<ValuesType = unknown> implements BaseThreadsManager<ValuesType> {
5
5
  private threads;
6
6
  private checkpoints;
@@ -27,7 +27,7 @@ export declare class MemoryThreadsManager<ValuesType = unknown> implements BaseT
27
27
  sortBy?: ThreadSortBy;
28
28
  sortOrder?: SortOrder;
29
29
  values?: ValuesType;
30
- select?: Array<'thread_id' | 'created_at' | 'updated_at' | 'metadata' | 'config' | 'context' | 'status' | 'values' | 'interrupts'>;
30
+ select?: Array<'thread_id' | 'created_at' | 'updated_at' | 'metadata' | 'config' | 'context' | 'status' | 'values' | 'interrupts' | 'title'>;
31
31
  withoutDetails?: boolean;
32
32
  }): Promise<Thread<ValuesType>[]>;
33
33
  private deepEqual;
@@ -66,4 +66,5 @@ export declare class MemoryThreadsManager<ValuesType = unknown> implements BaseT
66
66
  }): Promise<ThreadState[]>;
67
67
  copy(threadId: string): Promise<Thread<ValuesType>>;
68
68
  saveCheckpoint(threadId: string, values: any, next: string[], config: Config, metadata?: Metadata): Promise<void>;
69
+ setTitleIfNull(threadId: string, title: string): Promise<boolean>;
69
70
  }