@langgraph-js/pure-graph 2.6.0 → 2.7.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/README.md +183 -15
- package/dist/adapter/fetch/assistants.d.ts +9 -0
- package/dist/adapter/fetch/context.d.ts +4 -0
- package/dist/adapter/fetch/index.d.ts +8 -0
- package/dist/adapter/fetch/runs.d.ts +21 -0
- package/dist/adapter/fetch/threads.d.ts +17 -0
- package/dist/adapter/fetch/utils.d.ts +44 -0
- package/dist/adapter/hono/index.js +7 -209
- package/dist/adapter/hono/index.js.map +1 -1
- package/dist/adapter/nextjs/index.js +2 -2
- package/dist/adapter/nextjs/router.d.ts +3 -3
- package/dist/{createEndpoint-BMYbyDjM.js → createEndpoint-BEEfOyPg.js} +2 -2
- package/dist/{createEndpoint-BMYbyDjM.js.map → createEndpoint-BEEfOyPg.js.map} +1 -1
- package/dist/index-B-gojNlM.js +557 -0
- package/dist/index-B-gojNlM.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/{queue-D1FBVnEa.js → queue-g6fOz7D5.js} +2 -2
- package/dist/{queue-D1FBVnEa.js.map → queue-g6fOz7D5.js.map} +1 -1
- package/dist/router-Dk7saRUW.js +30 -0
- package/dist/router-Dk7saRUW.js.map +1 -0
- package/dist/{stream-Bgw3QUMg.js → stream-Blquv56P.js} +3 -3
- package/dist/{stream-Bgw3QUMg.js.map → stream-Blquv56P.js.map} +1 -1
- package/package.json +1 -1
- package/dist/adapter/hono/assistants.d.ts +0 -3
- package/dist/adapter/hono/runs.d.ts +0 -6
- package/dist/adapter/hono/threads.d.ts +0 -3
- package/dist/adapter/nextjs/endpoint.d.ts +0 -35
- package/dist/router-BfL7mrw7.js +0 -218
- package/dist/router-BfL7mrw7.js.map +0 -1
- package/dist/zod-B6xyK6pu.js +0 -112
- package/dist/zod-B6xyK6pu.js.map +0 -1
- /package/dist/adapter/{hono → fetch}/endpoint.d.ts +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { L as LangGraphGlobal, a as streamState, g as getGraph, G as GRAPHS } from './stream-
|
|
1
|
+
import { L as LangGraphGlobal, a as streamState, g as getGraph, G as GRAPHS } from './stream-Blquv56P.js';
|
|
2
2
|
|
|
3
3
|
const AssistantEndpoint = {
|
|
4
4
|
async search(query) {
|
|
@@ -108,4 +108,4 @@ const createEndpoint = () => {
|
|
|
108
108
|
};
|
|
109
109
|
|
|
110
110
|
export { AssistantEndpoint as A, createEndpoint as c };
|
|
111
|
-
//# sourceMappingURL=createEndpoint-
|
|
111
|
+
//# sourceMappingURL=createEndpoint-BEEfOyPg.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createEndpoint-
|
|
1
|
+
{"version":3,"file":"createEndpoint-BEEfOyPg.js","sources":["../src/createEndpoint.ts"],"sourcesContent":["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';\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 if (query?.graphId) {\n return [\n {\n assistant_id: query.graphId,\n graph_id: query.graphId,\n config: {},\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n metadata: {},\n version: 1,\n name: query.graphId,\n description: '',\n } as Assistant,\n ];\n }\n return 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 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\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 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 for await (const data of streamState(\n threads,\n threads.createRun(threadId, assistantId, payload),\n payload,\n {\n attempt: 0,\n getGraph,\n },\n )) {\n yield data;\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 try {\n // 获取 Redis 队列实例\n const queue = LangGraphGlobal.globalMessageQueue.getQueue(runId);\n\n // 监听队列数据并转换格式\n for await (const eventMessage of queue.onDataReceive()) {\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 console.warn('Join stream failed:', error);\n }\n },\n },\n };\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
import { s as serialiseAsDict, L as LangGraphGlobal } from './stream-Blquv56P.js';
|
|
2
|
+
import { c as createEndpoint } from './createEndpoint-BEEfOyPg.js';
|
|
3
|
+
import z from 'zod';
|
|
4
|
+
import camelcaseKeys from 'camelcase-keys';
|
|
5
|
+
|
|
6
|
+
const client = createEndpoint();
|
|
7
|
+
|
|
8
|
+
const AssistantConfigurable = z.object({
|
|
9
|
+
thread_id: z.string().optional(),
|
|
10
|
+
thread_ts: z.string().optional()
|
|
11
|
+
}).catchall(z.unknown());
|
|
12
|
+
const AssistantConfig = z.object({
|
|
13
|
+
tags: z.array(z.string()).optional(),
|
|
14
|
+
recursion_limit: z.number().int().optional(),
|
|
15
|
+
configurable: AssistantConfigurable.optional()
|
|
16
|
+
}).catchall(z.unknown()).describe("The configuration of an assistant.");
|
|
17
|
+
z.object({
|
|
18
|
+
assistant_id: z.string().uuid(),
|
|
19
|
+
graph_id: z.string(),
|
|
20
|
+
config: AssistantConfig,
|
|
21
|
+
created_at: z.string(),
|
|
22
|
+
updated_at: z.string(),
|
|
23
|
+
metadata: z.object({}).catchall(z.any())
|
|
24
|
+
});
|
|
25
|
+
const MetadataSchema = z.object({
|
|
26
|
+
source: z.union([z.literal("input"), z.literal("loop"), z.literal("update"), z.string()]).optional(),
|
|
27
|
+
step: z.number().optional(),
|
|
28
|
+
writes: z.any().nullable().optional(),
|
|
29
|
+
parents: z.any().optional()
|
|
30
|
+
}).catchall(z.unknown());
|
|
31
|
+
const SendSchema = z.object({
|
|
32
|
+
node: z.string(),
|
|
33
|
+
input: z.unknown().optional()
|
|
34
|
+
});
|
|
35
|
+
const CommandSchema = z.object({
|
|
36
|
+
update: z.union([z.any(), z.array(z.tuple([z.string(), z.any()]))]).nullable().optional(),
|
|
37
|
+
resume: z.unknown().optional(),
|
|
38
|
+
goto: z.union([SendSchema, z.array(SendSchema), z.string(), z.array(z.string())]).optional()
|
|
39
|
+
});
|
|
40
|
+
z.object({
|
|
41
|
+
limit: z.number().int().optional(),
|
|
42
|
+
offset: z.number().int().optional()
|
|
43
|
+
});
|
|
44
|
+
const ThreadIdParamSchema = z.object({
|
|
45
|
+
thread_id: z.string().uuid()
|
|
46
|
+
});
|
|
47
|
+
const RunIdParamSchema = z.object({
|
|
48
|
+
thread_id: z.string().uuid(),
|
|
49
|
+
run_id: z.string().uuid()
|
|
50
|
+
});
|
|
51
|
+
const AssistantsSearchSchema = z.object({
|
|
52
|
+
graph_id: z.string().optional(),
|
|
53
|
+
metadata: MetadataSchema.optional(),
|
|
54
|
+
limit: z.number().int().optional(),
|
|
55
|
+
offset: z.number().int().optional()
|
|
56
|
+
});
|
|
57
|
+
const AssistantGraphQuerySchema = z.object({
|
|
58
|
+
xray: z.string().optional()
|
|
59
|
+
});
|
|
60
|
+
const RunStreamPayloadSchema = z.object({
|
|
61
|
+
assistant_id: z.union([z.string().uuid(), z.string()]),
|
|
62
|
+
checkpoint_id: z.string().optional(),
|
|
63
|
+
input: z.any().optional(),
|
|
64
|
+
command: CommandSchema.optional(),
|
|
65
|
+
metadata: MetadataSchema.optional(),
|
|
66
|
+
config: AssistantConfig.optional(),
|
|
67
|
+
webhook: z.string().optional(),
|
|
68
|
+
interrupt_before: z.union([z.literal("*"), z.array(z.string())]).optional(),
|
|
69
|
+
interrupt_after: z.union([z.literal("*"), z.array(z.string())]).optional(),
|
|
70
|
+
on_disconnect: z.enum(["cancel", "continue"]).optional().default("continue"),
|
|
71
|
+
multitask_strategy: z.enum(["reject", "rollback", "interrupt", "enqueue"]).optional(),
|
|
72
|
+
stream_mode: z.array(z.enum(["values", "messages", "messages-tuple", "updates", "events", "debug", "custom"])).optional(),
|
|
73
|
+
stream_subgraphs: z.boolean().optional(),
|
|
74
|
+
stream_resumable: z.boolean().optional(),
|
|
75
|
+
after_seconds: z.number().optional(),
|
|
76
|
+
if_not_exists: z.enum(["create", "reject"]).optional(),
|
|
77
|
+
on_completion: z.enum(["complete", "continue"]).optional(),
|
|
78
|
+
feedback_keys: z.array(z.string()).optional(),
|
|
79
|
+
langsmith_tracer: z.unknown().optional()
|
|
80
|
+
}).describe("Payload for creating a stateful run.");
|
|
81
|
+
const RunListQuerySchema = z.object({
|
|
82
|
+
limit: z.coerce.number().int().optional(),
|
|
83
|
+
offset: z.coerce.number().int().optional(),
|
|
84
|
+
status: z.enum(["pending", "running", "error", "success", "timeout", "interrupted"]).optional()
|
|
85
|
+
});
|
|
86
|
+
const RunCancelQuerySchema = z.object({
|
|
87
|
+
wait: z.coerce.boolean().optional().default(false),
|
|
88
|
+
action: z.enum(["interrupt", "rollback"]).optional().default("interrupt")
|
|
89
|
+
});
|
|
90
|
+
const RunJoinStreamQuerySchema = z.object({
|
|
91
|
+
cancel_on_disconnect: z.coerce.boolean().optional().default(false),
|
|
92
|
+
last_event_id: z.string().optional(),
|
|
93
|
+
stream_mode: z.enum(["values", "messages", "messages-tuple", "updates", "events", "debug", "custom"]).optional()
|
|
94
|
+
});
|
|
95
|
+
const ThreadCreatePayloadSchema = z.object({
|
|
96
|
+
thread_id: z.string().uuid().describe("The ID of the thread. If not provided, an ID is generated.").optional(),
|
|
97
|
+
metadata: MetadataSchema.optional(),
|
|
98
|
+
if_exists: z.union([z.literal("raise"), z.literal("do_nothing")]).optional()
|
|
99
|
+
}).describe("Payload for creating a thread.");
|
|
100
|
+
const ThreadSearchPayloadSchema = z.object({
|
|
101
|
+
metadata: MetadataSchema.describe("Metadata to search for.").optional(),
|
|
102
|
+
status: z.enum(["idle", "busy", "interrupted", "error"]).describe("Filter by thread status.").optional(),
|
|
103
|
+
values: z.any().describe("Filter by thread values.").optional(),
|
|
104
|
+
limit: z.number().int().gte(1).lte(1e3).describe("Maximum number to return.").optional(),
|
|
105
|
+
offset: z.number().int().gte(0).describe("Offset to start from.").optional(),
|
|
106
|
+
sort_by: z.enum(["thread_id", "status", "created_at", "updated_at"]).describe("Sort by field.").optional(),
|
|
107
|
+
sort_order: z.enum(["asc", "desc"]).describe("Sort order.").optional()
|
|
108
|
+
}).describe("Payload for listing threads.");
|
|
109
|
+
const ThreadStateUpdate = z.object({
|
|
110
|
+
values: z.union([z.record(z.string(), z.unknown()), z.array(z.record(z.string(), z.unknown()))]).nullish()
|
|
111
|
+
// as_node: z.string().optional(),
|
|
112
|
+
// checkpoint_id: z.string().optional(),
|
|
113
|
+
// checkpoint: CheckpointSchema.nullish(),
|
|
114
|
+
}).describe("Payload for adding state to a thread.");
|
|
115
|
+
|
|
116
|
+
function parsePathParams(url, pattern) {
|
|
117
|
+
const patternParts = pattern.split("/").filter((p) => p);
|
|
118
|
+
const urlParts = new URL(url).pathname.split("/").filter((p) => p);
|
|
119
|
+
const params = {};
|
|
120
|
+
patternParts.forEach((part, index) => {
|
|
121
|
+
if (part.startsWith(":")) {
|
|
122
|
+
const paramName = part.slice(1);
|
|
123
|
+
params[paramName] = urlParts[index];
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
return params;
|
|
127
|
+
}
|
|
128
|
+
function parseQueryParams(url) {
|
|
129
|
+
const searchParams = new URL(url).searchParams;
|
|
130
|
+
const params = {};
|
|
131
|
+
searchParams.forEach((value, key) => {
|
|
132
|
+
if (!isNaN(Number(value)) && value !== "") {
|
|
133
|
+
params[key] = Number(value);
|
|
134
|
+
} else if (value === "true" || value === "false") {
|
|
135
|
+
params[key] = value === "true";
|
|
136
|
+
} else {
|
|
137
|
+
params[key] = value;
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return params;
|
|
141
|
+
}
|
|
142
|
+
function validate(schema, data) {
|
|
143
|
+
const result = schema.safeParse(data);
|
|
144
|
+
if (!result.success) {
|
|
145
|
+
throw new ValidationError(result.error.issues);
|
|
146
|
+
}
|
|
147
|
+
return result.data;
|
|
148
|
+
}
|
|
149
|
+
class ValidationError extends Error {
|
|
150
|
+
errors;
|
|
151
|
+
constructor(errors) {
|
|
152
|
+
super("Validation failed");
|
|
153
|
+
this.name = "ValidationError";
|
|
154
|
+
this.errors = errors;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function jsonResponse(data, status = 200, headers) {
|
|
158
|
+
return new Response(JSON.stringify(data), {
|
|
159
|
+
status,
|
|
160
|
+
headers: {
|
|
161
|
+
"Content-Type": "application/json",
|
|
162
|
+
...headers
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function errorResponse(error, status = 500) {
|
|
167
|
+
if (error instanceof ValidationError) {
|
|
168
|
+
return jsonResponse(
|
|
169
|
+
{
|
|
170
|
+
error: "Validation Error",
|
|
171
|
+
details: error.errors
|
|
172
|
+
},
|
|
173
|
+
400
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
return jsonResponse(
|
|
177
|
+
{
|
|
178
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
179
|
+
},
|
|
180
|
+
status
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
function createSSEStream(streamFn) {
|
|
184
|
+
let controller;
|
|
185
|
+
let isClosed = false;
|
|
186
|
+
const stream = new ReadableStream({
|
|
187
|
+
async start(ctrl) {
|
|
188
|
+
controller = ctrl;
|
|
189
|
+
const encoder = new TextEncoder();
|
|
190
|
+
const writer = {
|
|
191
|
+
writeSSE: async ({ data, event, id }) => {
|
|
192
|
+
if (isClosed) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
let message = "";
|
|
197
|
+
if (id) {
|
|
198
|
+
message += `id: ${id}
|
|
199
|
+
`;
|
|
200
|
+
}
|
|
201
|
+
if (event) {
|
|
202
|
+
message += `event: ${event}
|
|
203
|
+
`;
|
|
204
|
+
}
|
|
205
|
+
message += `data: ${data}
|
|
206
|
+
|
|
207
|
+
`;
|
|
208
|
+
controller.enqueue(encoder.encode(message));
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (!isClosed) {
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
close: () => {
|
|
216
|
+
if (!isClosed) {
|
|
217
|
+
isClosed = true;
|
|
218
|
+
try {
|
|
219
|
+
controller.close();
|
|
220
|
+
} catch (error) {
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
try {
|
|
226
|
+
await streamFn(writer);
|
|
227
|
+
} catch (error) {
|
|
228
|
+
console.error("SSE stream error:", error);
|
|
229
|
+
} finally {
|
|
230
|
+
if (!isClosed) {
|
|
231
|
+
isClosed = true;
|
|
232
|
+
try {
|
|
233
|
+
controller.close();
|
|
234
|
+
} catch (error) {
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
cancel() {
|
|
240
|
+
isClosed = true;
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
return new Response(stream, {
|
|
244
|
+
headers: {
|
|
245
|
+
"Content-Type": "text/event-stream",
|
|
246
|
+
"Cache-Control": "no-cache",
|
|
247
|
+
Connection: "keep-alive"
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
function withHeartbeat(streamFn, heartbeatInterval = process.env.HEARTBEAT_INTERVAL ? parseInt(process.env.HEARTBEAT_INTERVAL) : 1500) {
|
|
252
|
+
return async (writer) => {
|
|
253
|
+
let heartbeatTimer = null;
|
|
254
|
+
const startHeartbeat = () => {
|
|
255
|
+
if (heartbeatTimer) {
|
|
256
|
+
clearInterval(heartbeatTimer);
|
|
257
|
+
}
|
|
258
|
+
heartbeatTimer = setInterval(async () => {
|
|
259
|
+
try {
|
|
260
|
+
await writer.writeSSE({ event: "ping", data: "{}" });
|
|
261
|
+
} catch (error) {
|
|
262
|
+
if (heartbeatTimer) {
|
|
263
|
+
clearInterval(heartbeatTimer);
|
|
264
|
+
heartbeatTimer = null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}, heartbeatInterval);
|
|
268
|
+
};
|
|
269
|
+
const stopHeartbeat = () => {
|
|
270
|
+
if (heartbeatTimer) {
|
|
271
|
+
clearInterval(heartbeatTimer);
|
|
272
|
+
heartbeatTimer = null;
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
const proxiedWriter = {
|
|
276
|
+
writeSSE: async (data) => {
|
|
277
|
+
stopHeartbeat();
|
|
278
|
+
await writer.writeSSE(data);
|
|
279
|
+
startHeartbeat();
|
|
280
|
+
},
|
|
281
|
+
close: () => {
|
|
282
|
+
stopHeartbeat();
|
|
283
|
+
writer.close();
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
startHeartbeat();
|
|
287
|
+
try {
|
|
288
|
+
await streamFn(proxiedWriter);
|
|
289
|
+
} finally {
|
|
290
|
+
stopHeartbeat();
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function searchAssistants(req, context) {
|
|
296
|
+
try {
|
|
297
|
+
const body = await req.json();
|
|
298
|
+
const payload = validate(AssistantsSearchSchema, body);
|
|
299
|
+
const data = await client.assistants.search(camelcaseKeys(payload));
|
|
300
|
+
return jsonResponse(data, 200, {
|
|
301
|
+
"X-Pagination-Total": "0"
|
|
302
|
+
});
|
|
303
|
+
} catch (error) {
|
|
304
|
+
return errorResponse(error);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
async function getAssistantGraph(req, context) {
|
|
308
|
+
try {
|
|
309
|
+
const url = new URL(req.url);
|
|
310
|
+
const pathParts = url.pathname.split("/").filter((p) => p);
|
|
311
|
+
const assistant_id = pathParts[1];
|
|
312
|
+
const queryParams = parseQueryParams(req.url);
|
|
313
|
+
const { xray } = validate(AssistantGraphQuerySchema, queryParams);
|
|
314
|
+
const data = await client.assistants.getGraph(assistant_id, {
|
|
315
|
+
xray: xray !== void 0 ? xray === "true" : void 0
|
|
316
|
+
});
|
|
317
|
+
return jsonResponse(data);
|
|
318
|
+
} catch (error) {
|
|
319
|
+
return errorResponse(error);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async function createThread(req, context) {
|
|
324
|
+
try {
|
|
325
|
+
const body = await req.json();
|
|
326
|
+
const payload = validate(ThreadCreatePayloadSchema, body);
|
|
327
|
+
const thread = await client.threads.create(camelcaseKeys(payload));
|
|
328
|
+
return jsonResponse(thread);
|
|
329
|
+
} catch (error) {
|
|
330
|
+
return errorResponse(error);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async function searchThreads(req, context) {
|
|
334
|
+
try {
|
|
335
|
+
const body = await req.json();
|
|
336
|
+
const payload = validate(ThreadSearchPayloadSchema, body);
|
|
337
|
+
const result = await client.threads.search(camelcaseKeys(payload));
|
|
338
|
+
return jsonResponse(result, 200, {
|
|
339
|
+
"X-Pagination-Total": result.length.toString()
|
|
340
|
+
});
|
|
341
|
+
} catch (error) {
|
|
342
|
+
return errorResponse(error);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
async function getThread(req, context) {
|
|
346
|
+
try {
|
|
347
|
+
const params = parsePathParams(req.url, "/threads/:thread_id");
|
|
348
|
+
const { thread_id } = validate(ThreadIdParamSchema, params);
|
|
349
|
+
const thread = await client.threads.get(thread_id);
|
|
350
|
+
return jsonResponse(thread);
|
|
351
|
+
} catch (error) {
|
|
352
|
+
return errorResponse(error);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
async function deleteThread(req, context) {
|
|
356
|
+
try {
|
|
357
|
+
const params = parsePathParams(req.url, "/threads/:thread_id");
|
|
358
|
+
const { thread_id } = validate(ThreadIdParamSchema, params);
|
|
359
|
+
await client.threads.delete(thread_id);
|
|
360
|
+
return new Response(null, { status: 204 });
|
|
361
|
+
} catch (error) {
|
|
362
|
+
return errorResponse(error);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function streamRun(req, context) {
|
|
367
|
+
try {
|
|
368
|
+
const params = parsePathParams(req.url, "/threads/:thread_id/runs/stream");
|
|
369
|
+
const { thread_id } = validate(ThreadIdParamSchema, params);
|
|
370
|
+
const body = await req.json();
|
|
371
|
+
const payload = validate(RunStreamPayloadSchema, body);
|
|
372
|
+
return createSSEStream(
|
|
373
|
+
withHeartbeat(async (writer) => {
|
|
374
|
+
payload.config = payload.config || {};
|
|
375
|
+
payload.config.configurable = payload.config.configurable || {};
|
|
376
|
+
const langgraphContext = context?.langgraph_context;
|
|
377
|
+
if (langgraphContext) {
|
|
378
|
+
Object.assign(payload.config.configurable, langgraphContext);
|
|
379
|
+
}
|
|
380
|
+
for await (const { event, data } of client.runs.stream(
|
|
381
|
+
thread_id,
|
|
382
|
+
payload.assistant_id,
|
|
383
|
+
camelcaseKeys(payload)
|
|
384
|
+
)) {
|
|
385
|
+
await writer.writeSSE({ data: serialiseAsDict(data) ?? "", event });
|
|
386
|
+
}
|
|
387
|
+
})
|
|
388
|
+
);
|
|
389
|
+
} catch (error) {
|
|
390
|
+
return errorResponse(error);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
async function joinRunStream(req, context) {
|
|
394
|
+
try {
|
|
395
|
+
const params = parsePathParams(req.url, "/threads/:thread_id/runs/:run_id/stream");
|
|
396
|
+
const { thread_id, run_id } = validate(RunIdParamSchema, params);
|
|
397
|
+
const queryParams = parseQueryParams(req.url);
|
|
398
|
+
const { cancel_on_disconnect, last_event_id, stream_mode } = validate(RunJoinStreamQuerySchema, queryParams);
|
|
399
|
+
return createSSEStream(
|
|
400
|
+
withHeartbeat(async (writer) => {
|
|
401
|
+
const controller = new AbortController();
|
|
402
|
+
if (cancel_on_disconnect) {
|
|
403
|
+
const cleanup = () => {
|
|
404
|
+
controller.abort("Client disconnected");
|
|
405
|
+
};
|
|
406
|
+
req.signal?.addEventListener("abort", cleanup);
|
|
407
|
+
}
|
|
408
|
+
try {
|
|
409
|
+
for await (const { event, data, id } of client.runs.joinStream(thread_id, run_id, {
|
|
410
|
+
signal: controller.signal,
|
|
411
|
+
cancelOnDisconnect: cancel_on_disconnect,
|
|
412
|
+
lastEventId: last_event_id,
|
|
413
|
+
streamMode: stream_mode ? [stream_mode] : void 0
|
|
414
|
+
})) {
|
|
415
|
+
await writer.writeSSE({
|
|
416
|
+
data: serialiseAsDict(data) ?? "",
|
|
417
|
+
event,
|
|
418
|
+
id
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
} catch (error) {
|
|
422
|
+
if (!(error instanceof Error) || !error.message.includes("user cancel")) {
|
|
423
|
+
console.error("Join stream error:", error);
|
|
424
|
+
await writer.writeSSE({
|
|
425
|
+
event: "error",
|
|
426
|
+
data: JSON.stringify({
|
|
427
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
428
|
+
})
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
})
|
|
433
|
+
);
|
|
434
|
+
} catch (error) {
|
|
435
|
+
return errorResponse(error);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
async function listRuns(req, context) {
|
|
439
|
+
try {
|
|
440
|
+
const params = parsePathParams(req.url, "/threads/:thread_id/runs");
|
|
441
|
+
const { thread_id } = validate(ThreadIdParamSchema, params);
|
|
442
|
+
const queryParams = parseQueryParams(req.url);
|
|
443
|
+
const { limit, offset, status } = validate(RunListQuerySchema, queryParams);
|
|
444
|
+
const runs = await client.runs.list(thread_id, { limit, offset, status });
|
|
445
|
+
return jsonResponse(runs);
|
|
446
|
+
} catch (error) {
|
|
447
|
+
return errorResponse(error);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
async function cancelRun(req, context) {
|
|
451
|
+
try {
|
|
452
|
+
const params = parsePathParams(req.url, "/threads/:thread_id/runs/:run_id/cancel");
|
|
453
|
+
const { thread_id, run_id } = validate(RunIdParamSchema, params);
|
|
454
|
+
const queryParams = parseQueryParams(req.url);
|
|
455
|
+
const { wait, action } = validate(RunCancelQuerySchema, queryParams);
|
|
456
|
+
const cancel = client.runs.cancel(thread_id, run_id, wait, action);
|
|
457
|
+
if (wait) {
|
|
458
|
+
await cancel;
|
|
459
|
+
}
|
|
460
|
+
return new Response(null, { status: wait ? 204 : 202 });
|
|
461
|
+
} catch (error) {
|
|
462
|
+
return errorResponse(error);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
async function updateThreadState(req, context) {
|
|
466
|
+
try {
|
|
467
|
+
const params = parsePathParams(req.url, "/threads/:thread_id/state");
|
|
468
|
+
const { thread_id } = validate(z.object({ thread_id: z.string().uuid() }), params);
|
|
469
|
+
const body = await req.json();
|
|
470
|
+
const payload = validate(ThreadStateUpdate, body);
|
|
471
|
+
const inserted = await client.threads.updateState(thread_id, payload);
|
|
472
|
+
return jsonResponse(inserted);
|
|
473
|
+
} catch (error) {
|
|
474
|
+
return errorResponse(error);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const routes = [
|
|
479
|
+
// Assistants
|
|
480
|
+
{
|
|
481
|
+
method: "POST",
|
|
482
|
+
pattern: /^\/assistants\/search$/,
|
|
483
|
+
handler: searchAssistants
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
method: "GET",
|
|
487
|
+
pattern: /^\/assistants\/[^/]+\/graph$/,
|
|
488
|
+
handler: getAssistantGraph
|
|
489
|
+
},
|
|
490
|
+
// Threads
|
|
491
|
+
{
|
|
492
|
+
method: "POST",
|
|
493
|
+
pattern: /^\/threads$/,
|
|
494
|
+
handler: createThread
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
method: "POST",
|
|
498
|
+
pattern: /^\/threads\/search$/,
|
|
499
|
+
handler: searchThreads
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
method: "GET",
|
|
503
|
+
pattern: /^\/threads\/[^/]+$/,
|
|
504
|
+
handler: getThread
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
method: "DELETE",
|
|
508
|
+
pattern: /^\/threads\/[^/]+$/,
|
|
509
|
+
handler: deleteThread
|
|
510
|
+
},
|
|
511
|
+
// Runs
|
|
512
|
+
{
|
|
513
|
+
method: "POST",
|
|
514
|
+
pattern: /^\/threads\/[^/]+\/runs\/stream$/,
|
|
515
|
+
handler: streamRun
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
method: "GET",
|
|
519
|
+
pattern: /^\/threads\/[^/]+\/runs\/[^/]+\/stream$/,
|
|
520
|
+
handler: joinRunStream
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
method: "GET",
|
|
524
|
+
pattern: /^\/threads\/[^/]+\/runs$/,
|
|
525
|
+
handler: listRuns
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
method: "POST",
|
|
529
|
+
pattern: /^\/threads\/[^/]+\/runs\/[^/]+\/cancel$/,
|
|
530
|
+
handler: cancelRun
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
method: "POST",
|
|
534
|
+
pattern: /^\/threads\/[^/]+\/state$/,
|
|
535
|
+
handler: updateThreadState
|
|
536
|
+
}
|
|
537
|
+
];
|
|
538
|
+
async function handleRequest(req, context = {}) {
|
|
539
|
+
try {
|
|
540
|
+
await LangGraphGlobal.initGlobal();
|
|
541
|
+
const url = new URL(req.url);
|
|
542
|
+
const pathname = url.pathname;
|
|
543
|
+
const method = req.method;
|
|
544
|
+
for (const route of routes) {
|
|
545
|
+
if (route.method === method && route.pattern.test(pathname)) {
|
|
546
|
+
return await route.handler(req, context);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return new Response("Not Found", { status: 404 });
|
|
550
|
+
} catch (error) {
|
|
551
|
+
console.error("Request error:", error);
|
|
552
|
+
return errorResponse(error);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
export { handleRequest as h };
|
|
557
|
+
//# sourceMappingURL=index-B-gojNlM.js.map
|