@langgraph-js/pure-graph 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/.prettierrc +11 -0
  2. package/README.md +104 -10
  3. package/bun.lock +209 -0
  4. package/dist/adapter/hono/assistants.js +3 -9
  5. package/dist/adapter/hono/endpoint.js +1 -2
  6. package/dist/adapter/hono/runs.js +6 -39
  7. package/dist/adapter/hono/threads.js +5 -46
  8. package/dist/adapter/nextjs/endpoint.d.ts +1 -0
  9. package/dist/adapter/nextjs/endpoint.js +2 -0
  10. package/dist/adapter/nextjs/index.d.ts +1 -0
  11. package/dist/adapter/nextjs/index.js +2 -0
  12. package/dist/adapter/nextjs/router.d.ts +5 -0
  13. package/dist/adapter/nextjs/router.js +168 -0
  14. package/dist/adapter/{hono → nextjs}/zod.d.ts +5 -5
  15. package/dist/adapter/{hono → nextjs}/zod.js +22 -5
  16. package/dist/adapter/zod.d.ts +577 -0
  17. package/dist/adapter/zod.js +119 -0
  18. package/dist/createEndpoint.d.ts +1 -2
  19. package/dist/createEndpoint.js +4 -3
  20. package/dist/global.d.ts +6 -4
  21. package/dist/global.js +10 -5
  22. package/dist/graph/stream.d.ts +1 -1
  23. package/dist/graph/stream.js +18 -10
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.js +1 -0
  26. package/dist/queue/stream_queue.d.ts +5 -3
  27. package/dist/queue/stream_queue.js +4 -2
  28. package/dist/storage/index.d.ts +9 -4
  29. package/dist/storage/index.js +38 -3
  30. package/dist/storage/redis/queue.d.ts +39 -0
  31. package/dist/storage/redis/queue.js +130 -0
  32. package/dist/storage/sqlite/DB.d.ts +3 -0
  33. package/dist/storage/sqlite/DB.js +14 -0
  34. package/dist/storage/sqlite/checkpoint.d.ts +18 -0
  35. package/dist/storage/sqlite/checkpoint.js +374 -0
  36. package/dist/storage/sqlite/threads.d.ts +43 -0
  37. package/dist/storage/sqlite/threads.js +266 -0
  38. package/dist/storage/sqlite/type.d.ts +15 -0
  39. package/dist/storage/sqlite/type.js +1 -0
  40. package/dist/utils/createEntrypointGraph.d.ts +14 -0
  41. package/dist/utils/createEntrypointGraph.js +11 -0
  42. package/dist/utils/getGraph.js +3 -3
  43. package/examples/nextjs/README.md +36 -0
  44. package/examples/nextjs/app/api/langgraph/[...path]/route.ts +10 -0
  45. package/examples/nextjs/app/favicon.ico +0 -0
  46. package/examples/nextjs/app/globals.css +26 -0
  47. package/examples/nextjs/app/layout.tsx +34 -0
  48. package/examples/nextjs/app/page.tsx +211 -0
  49. package/examples/nextjs/next.config.ts +26 -0
  50. package/examples/nextjs/package.json +24 -0
  51. package/examples/nextjs/postcss.config.mjs +5 -0
  52. package/examples/nextjs/tsconfig.json +27 -0
  53. package/package.json +9 -4
  54. package/packages/agent-graph/demo.json +35 -0
  55. package/packages/agent-graph/package.json +18 -0
  56. package/packages/agent-graph/src/index.ts +47 -0
  57. package/packages/agent-graph/src/tools/tavily.ts +9 -0
  58. package/packages/agent-graph/src/tools.ts +38 -0
  59. package/packages/agent-graph/src/types.ts +42 -0
  60. package/pnpm-workspace.yaml +4 -0
  61. package/src/adapter/hono/assistants.ts +16 -33
  62. package/src/adapter/hono/endpoint.ts +1 -2
  63. package/src/adapter/hono/runs.ts +15 -51
  64. package/src/adapter/hono/threads.ts +15 -70
  65. package/src/adapter/nextjs/endpoint.ts +2 -0
  66. package/src/adapter/nextjs/index.ts +2 -0
  67. package/src/adapter/nextjs/router.ts +193 -0
  68. package/src/adapter/{hono → nextjs}/zod.ts +22 -5
  69. package/src/adapter/zod.ts +135 -0
  70. package/src/createEndpoint.ts +12 -5
  71. package/src/e.d.ts +3 -0
  72. package/src/global.ts +11 -6
  73. package/src/graph/stream.ts +20 -10
  74. package/src/index.ts +1 -0
  75. package/src/queue/stream_queue.ts +6 -5
  76. package/src/storage/index.ts +42 -4
  77. package/src/storage/redis/queue.ts +148 -0
  78. package/src/storage/sqlite/DB.ts +16 -0
  79. package/src/storage/sqlite/checkpoint.ts +503 -0
  80. package/src/storage/sqlite/threads.ts +366 -0
  81. package/src/storage/sqlite/type.ts +12 -0
  82. package/src/utils/createEntrypointGraph.ts +20 -0
  83. package/src/utils/getGraph.ts +3 -3
  84. package/test/graph/entrypoint.ts +21 -0
  85. package/test/graph/index.ts +45 -6
  86. package/test/hono.ts +5 -0
  87. package/test/test.ts +0 -10
@@ -0,0 +1,42 @@
1
+ export interface AgentTool {
2
+ name: string;
3
+ tool_type: string;
4
+ description?: string;
5
+ }
6
+
7
+ export interface MCPTool extends AgentTool {
8
+ tool_type: 'mcp';
9
+ type: string;
10
+ url: string;
11
+ headers?: Record<string, string>;
12
+ }
13
+ export interface BuiltinTool extends AgentTool {
14
+ tool_type: 'builtin';
15
+ }
16
+
17
+ export interface InnerTool extends AgentTool {
18
+ tool_type: 'inner';
19
+ }
20
+
21
+ export interface SubAgent {
22
+ protocolId: string;
23
+ protocol?: AgentProtocol;
24
+ }
25
+
26
+ export interface AgentProtocol {
27
+ id: string;
28
+ protocolVersion: string;
29
+ name: string;
30
+ description: string;
31
+ url: string;
32
+ iconUrl?: string;
33
+ version: string;
34
+ documentationUrl?: string;
35
+ llm: {
36
+ provider?: string;
37
+ model: string;
38
+ }[];
39
+ systemPrompt: string;
40
+ tools: (BuiltinTool | InnerTool | MCPTool)[];
41
+ subAgents: SubAgent[];
42
+ }
@@ -0,0 +1,4 @@
1
+ packages:
2
+ # 指定根目录直接子目录中的包
3
+ - "packages/*"
4
+ - "examples/*"
@@ -1,41 +1,24 @@
1
1
  import { zValidator } from '@hono/zod-validator';
2
2
  import { Hono } from 'hono';
3
- import { z } from 'zod';
4
3
  import { client } from './endpoint';
5
- import { MetadataSchema } from './zod';
4
+ import { AssistantsSearchSchema, AssistantGraphQuerySchema } from '../zod';
6
5
  const api = new Hono();
7
6
 
8
- api.post(
9
- '/assistants/search',
10
- zValidator(
11
- 'json',
12
- z.object({
13
- graph_id: z.string().optional(),
14
- metadata: MetadataSchema.optional(),
15
- limit: z.number().int().optional(),
16
- offset: z.number().int().optional(),
17
- }),
18
- ),
19
- async (c) => {
20
- // Search Assistants
21
- const payload = c.req.valid('json');
22
- let total = 0;
23
- const data = await client.assistants.search(payload);
24
- c.res.headers.set('X-Pagination-Total', total.toString());
25
- return c.json(data);
26
- },
27
- );
7
+ api.post('/assistants/search', zValidator('json', AssistantsSearchSchema), async (c) => {
8
+ // Search Assistants
9
+ const payload = c.req.valid('json');
10
+ let total = 0;
11
+ const data = await client.assistants.search(payload);
12
+ c.res.headers.set('X-Pagination-Total', total.toString());
13
+ return c.json(data);
14
+ });
28
15
 
29
- api.get(
30
- '/assistants/:assistant_id/graph',
31
- zValidator('query', z.object({ xray: z.string().optional() })),
32
- async (c) => {
33
- const xray = c.req.valid('query').xray;
34
- const data = await client.assistants.getGraph(c.req.param('assistant_id'), {
35
- xray: xray !== undefined ? xray === 'true' : undefined,
36
- });
37
- return c.json(data);
38
- },
39
- );
16
+ api.get('/assistants/:assistant_id/graph', zValidator('query', AssistantGraphQuerySchema), async (c) => {
17
+ const xray = c.req.valid('query').xray;
18
+ const data = await client.assistants.getGraph(c.req.param('assistant_id'), {
19
+ xray: xray !== undefined ? xray === 'true' : undefined,
20
+ });
21
+ return c.json(data);
22
+ });
40
23
 
41
24
  export default api;
@@ -1,4 +1,3 @@
1
1
  import { createEndpoint } from '../../createEndpoint.js';
2
- import { MemoryThreadsManager } from '../../storage/memory/threads.js';
3
2
 
4
- export const client = createEndpoint(new MemoryThreadsManager());
3
+ export const client = createEndpoint();
@@ -2,9 +2,14 @@ import { zValidator } from '@hono/zod-validator';
2
2
  import { Hono } from 'hono';
3
3
 
4
4
  import { streamSSE } from 'hono/streaming';
5
- import { z } from 'zod';
6
5
  import { client } from './endpoint';
7
- import { AssistantConfig, CommandSchema, MetadataSchema } from './zod';
6
+ import {
7
+ ThreadIdParamSchema,
8
+ RunIdParamSchema,
9
+ RunStreamPayloadSchema,
10
+ RunListQuerySchema,
11
+ RunCancelQuerySchema,
12
+ } from '../zod';
8
13
  import { serialiseAsDict } from '../../graph/stream';
9
14
 
10
15
  const api = new Hono();
@@ -12,36 +17,8 @@ const api = new Hono();
12
17
  // 最常用的对话接口
13
18
  api.post(
14
19
  '/threads/:thread_id/runs/stream',
15
- zValidator('param', z.object({ thread_id: z.string().uuid() })),
16
- zValidator(
17
- 'json',
18
- z
19
- .object({
20
- assistant_id: z.union([z.string().uuid(), z.string()]),
21
- checkpoint_id: z.string().optional(),
22
- // checkpoint: CheckpointSchema.optional(),
23
- input: z.any().optional(),
24
- command: CommandSchema.optional(),
25
- metadata: MetadataSchema.optional(),
26
- config: AssistantConfig.optional(),
27
- webhook: z.string().optional(),
28
- interrupt_before: z.union([z.literal('*'), z.array(z.string())]).optional(),
29
- interrupt_after: z.union([z.literal('*'), z.array(z.string())]).optional(),
30
- on_disconnect: z.enum(['cancel', 'continue']).optional().default('continue'),
31
- multitask_strategy: z.enum(['reject', 'rollback', 'interrupt', 'enqueue']).optional(),
32
- stream_mode: z
33
- .array(z.enum(['values', 'messages', 'messages-tuple', 'updates', 'events', 'debug', 'custom']))
34
- .optional(),
35
- stream_subgraphs: z.boolean().optional(),
36
- stream_resumable: z.boolean().optional(),
37
- after_seconds: z.number().optional(),
38
- if_not_exists: z.enum(['create', 'reject']).optional(),
39
- on_completion: z.enum(['complete', 'continue']).optional(),
40
- feedback_keys: z.array(z.string()).optional(),
41
- langsmith_tracer: z.unknown().optional(),
42
- })
43
- .describe('Payload for creating a stateful run.'),
44
- ),
20
+ zValidator('param', ThreadIdParamSchema),
21
+ zValidator('json', RunStreamPayloadSchema),
45
22
  async (c) => {
46
23
  // Stream Run
47
24
  const { thread_id } = c.req.valid('param');
@@ -51,7 +28,7 @@ api.post(
51
28
  return streamSSE(c, async (stream) => {
52
29
  /** @ts-ignore zod v3 的问题,与 ts 类型不一致 */
53
30
  for await (const { event, data } of client.runs.stream(thread_id, payload.assistant_id, payload)) {
54
- await stream.writeSSE({ data: serialiseAsDict(data), event });
31
+ await stream.writeSSE({ data: serialiseAsDict(data) ?? '', event });
55
32
  }
56
33
  });
57
34
  },
@@ -59,33 +36,20 @@ api.post(
59
36
 
60
37
  api.get(
61
38
  '/threads/:thread_id/runs',
62
- zValidator('param', z.object({ thread_id: z.string().uuid() })),
63
- zValidator(
64
- 'query',
65
- z.object({
66
- limit: z.string().optional(),
67
- offset: z.string().optional(),
68
- status: z.enum(['pending', 'running', 'error', 'success', 'timeout', 'interrupted']).optional(),
69
- }),
70
- ),
39
+ zValidator('param', ThreadIdParamSchema),
40
+ zValidator('query', RunListQuerySchema),
71
41
  async (c) => {
72
42
  const { thread_id } = c.req.valid('param');
73
43
  const { limit, offset, status } = c.req.valid('query');
74
- const runs = await client.runs.list(thread_id, { limit: Number(limit), offset: Number(offset), status });
44
+ const runs = await client.runs.list(thread_id, { limit, offset, status });
75
45
  return c.json(runs);
76
46
  },
77
47
  );
78
48
 
79
49
  api.post(
80
50
  '/threads/:thread_id/runs/:run_id/cancel',
81
- zValidator('param', z.object({ thread_id: z.string().uuid(), run_id: z.string().uuid() })),
82
- zValidator(
83
- 'query',
84
- z.object({
85
- wait: z.coerce.boolean().optional().default(false),
86
- action: z.enum(['interrupt', 'rollback']).optional().default('interrupt'),
87
- }),
88
- ),
51
+ zValidator('param', RunIdParamSchema),
52
+ zValidator('query', RunCancelQuerySchema),
89
53
  async (c) => {
90
54
  // Cancel Run Http
91
55
  const { thread_id, run_id } = c.req.valid('param');
@@ -1,88 +1,33 @@
1
1
  import { zValidator } from '@hono/zod-validator';
2
2
  import { Hono } from 'hono';
3
- import { z } from 'zod';
4
3
  import { client } from './endpoint';
5
- import { CommandSchema, MetadataSchema } from './zod';
4
+ import { ThreadIdParamSchema, ThreadCreatePayloadSchema, ThreadSearchPayloadSchema } from '../zod';
6
5
 
7
6
  const api = new Hono();
8
7
 
9
8
  // Threads Routes
10
- api.post(
11
- '/threads',
12
- zValidator(
13
- 'json',
14
- z
15
- .object({
16
- // supersteps: z
17
- // .array(
18
- // z.object({
19
- // updates: z.array(
20
- // z.object({
21
- // values: z.unknown(),
22
- // command: CommandSchema.optional(),
23
- // as_node: z.string(),
24
- // }),
25
- // ),
26
- // }),
27
- // )
28
- // .describe('The supersteps to apply to the thread.')
29
- // .optional(),
30
- thread_id: z
31
- .string()
32
- .uuid()
33
- .describe('The ID of the thread. If not provided, an ID is generated.')
34
- .optional(),
35
- metadata: MetadataSchema.optional(),
36
- if_exists: z.union([z.literal('raise'), z.literal('do_nothing')]).optional(),
37
- })
38
- .describe('Payload for creating a thread.'),
39
- ),
40
- async (c) => {
41
- const payload = c.req.valid('json');
42
- const thread = await client.threads.create(payload);
9
+ api.post('/threads', zValidator('json', ThreadCreatePayloadSchema), async (c) => {
10
+ const payload = c.req.valid('json');
11
+ const thread = await client.threads.create(payload);
43
12
 
44
- return c.json(thread);
45
- },
46
- );
13
+ return c.json(thread);
14
+ });
47
15
 
48
- api.post(
49
- '/threads/search',
50
- zValidator(
51
- 'json',
52
- z
53
- .object({
54
- metadata: z.record(z.unknown()).describe('Metadata to search for.').optional(),
55
- status: z
56
- .enum(['idle', 'busy', 'interrupted', 'error'])
57
- .describe('Filter by thread status.')
58
- .optional(),
59
- values: z.record(z.unknown()).describe('Filter by thread values.').optional(),
60
- limit: z.number().int().gte(1).lte(1000).describe('Maximum number to return.').optional(),
61
- offset: z.number().int().gte(0).describe('Offset to start from.').optional(),
62
- sort_by: z
63
- .enum(['thread_id', 'status', 'created_at', 'updated_at'])
64
- .describe('Sort by field.')
65
- .optional(),
66
- sort_order: z.enum(['asc', 'desc']).describe('Sort order.').optional(),
67
- })
68
- .describe('Payload for listing threads.'),
69
- ),
70
- async (c) => {
71
- // Search Threads
72
- const payload = c.req.valid('json');
73
- const result = await client.threads.search(payload as any);
74
- c.res.headers.set('X-Pagination-Total', result.length.toString());
75
- return c.json(result);
76
- },
77
- );
16
+ api.post('/threads/search', zValidator('json', ThreadSearchPayloadSchema), async (c) => {
17
+ // Search Threads
18
+ const payload = c.req.valid('json');
19
+ const result = await client.threads.search(payload as any);
20
+ c.res.headers.set('X-Pagination-Total', result.length.toString());
21
+ return c.json(result);
22
+ });
78
23
 
79
- api.get('/threads/:thread_id', zValidator('param', z.object({ thread_id: z.string().uuid() })), async (c) => {
24
+ api.get('/threads/:thread_id', zValidator('param', ThreadIdParamSchema), async (c) => {
80
25
  // Get Thread
81
26
  const { thread_id } = c.req.valid('param');
82
27
  return c.json(await client.threads.get(thread_id));
83
28
  });
84
29
 
85
- api.delete('/threads/:thread_id', zValidator('param', z.object({ thread_id: z.string().uuid() })), async (c) => {
30
+ api.delete('/threads/:thread_id', zValidator('param', ThreadIdParamSchema), async (c) => {
86
31
  // Delete Thread
87
32
  const { thread_id } = c.req.valid('param');
88
33
  await client.threads.delete(thread_id);
@@ -0,0 +1,2 @@
1
+ import { createEndpoint } from '../../createEndpoint.js';
2
+ export const client = createEndpoint();
@@ -0,0 +1,2 @@
1
+ // 统一路由处理器
2
+ export { GET, POST, DELETE } from "./router";
@@ -0,0 +1,193 @@
1
+ /** @ts-ignore */
2
+ import { NextRequest, NextResponse } from 'next/server';
3
+ import { client } from './endpoint';
4
+ import {
5
+ AssistantsSearchSchema,
6
+ AssistantGraphQuerySchema,
7
+ RunStreamPayloadSchema,
8
+ RunListQuerySchema,
9
+ RunCancelQuerySchema,
10
+ ThreadCreatePayloadSchema,
11
+ ThreadSearchPayloadSchema,
12
+ } from '../zod';
13
+ import { serialiseAsDict } from '../../graph/stream';
14
+
15
+ // Next.js App Router 的 SSE 响应实现
16
+ async function sseResponse(generator: AsyncGenerator<{ event: string; data: unknown }>): Promise<Response> {
17
+ const encoder = new TextEncoder();
18
+ const stream = new ReadableStream({
19
+ async start(controller) {
20
+ try {
21
+ for await (const { event, data } of generator) {
22
+ const line = `event: ${event}\n` + `data: ${serialiseAsDict(data, 0)}\n\n`;
23
+ controller.enqueue(encoder.encode(line));
24
+ }
25
+ } catch (err) {
26
+ // ignore
27
+ } finally {
28
+ controller.close();
29
+ }
30
+ },
31
+ });
32
+ return new Response(stream, {
33
+ headers: {
34
+ 'Content-Type': 'text/event-stream; charset=utf-8',
35
+ 'Cache-Control': 'no-cache, no-transform',
36
+ Connection: 'keep-alive',
37
+ },
38
+ });
39
+ }
40
+
41
+ // 统一路由处理器
42
+ export async function GET(req: NextRequest) {
43
+ const url = new URL(req.url);
44
+ const pathname = url.pathname;
45
+
46
+ // Assistants routes
47
+ if (pathname.match(/\/assistants\/[^/]+\/graph$/)) {
48
+ const match = pathname.match(/\/assistants\/([^/]+)\/graph$/);
49
+ if (match) {
50
+ const assistant_id = match[1];
51
+ const xrayParam = url.searchParams.get('xray');
52
+ const queryParams = { xray: xrayParam };
53
+ const { xray } = AssistantGraphQuerySchema.parse(queryParams);
54
+ const data = await client.assistants.getGraph(assistant_id, {
55
+ xray: xray !== undefined ? xray === 'true' : undefined,
56
+ });
57
+ return NextResponse.json(data);
58
+ }
59
+ }
60
+
61
+ // Threads routes
62
+ if (pathname.match(/\/threads\/[0-9a-fA-F-]{36}$/)) {
63
+ const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})$/);
64
+ if (match) {
65
+ const thread_id = match[1];
66
+ const data = await client.threads.get(thread_id);
67
+ return NextResponse.json(data);
68
+ }
69
+ }
70
+
71
+ // Runs routes
72
+ if (pathname.match(/\/threads\/[0-9a-fA-F-]{36}\/runs$/)) {
73
+ const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})\/runs$/);
74
+ if (match) {
75
+ const thread_id = match[1];
76
+ const limit = url.searchParams.get('limit');
77
+ const offset = url.searchParams.get('offset');
78
+ const status = url.searchParams.get('status');
79
+ const queryParams = { limit, offset, status };
80
+ const {
81
+ limit: parsedLimit,
82
+ offset: parsedOffset,
83
+ status: parsedStatus,
84
+ } = RunListQuerySchema.parse(queryParams);
85
+ const runs = await client.runs.list(thread_id, {
86
+ limit: parsedLimit,
87
+ offset: parsedOffset,
88
+ status: parsedStatus,
89
+ });
90
+ return Response.json(runs);
91
+ }
92
+ }
93
+
94
+ return new NextResponse('Not Found', { status: 404 });
95
+ }
96
+
97
+ export async function POST(req: NextRequest) {
98
+ const url = new URL(req.url);
99
+ const pathname = url.pathname;
100
+
101
+ // Assistants routes
102
+ if (pathname.endsWith('/assistants/search')) {
103
+ const body = await req.json();
104
+ const payload = AssistantsSearchSchema.parse(body);
105
+ const data = await client.assistants.search({
106
+ graphId: payload.graph_id,
107
+ metadata: payload.metadata as any,
108
+ limit: payload.limit,
109
+ offset: payload.offset,
110
+ } as any);
111
+ return NextResponse.json(data, {
112
+ headers: { 'X-Pagination-Total': String(data.length) },
113
+ });
114
+ }
115
+
116
+ // Threads routes
117
+ if (pathname.endsWith('/threads')) {
118
+ const body = await req.json();
119
+ const payload = ThreadCreatePayloadSchema.parse(body);
120
+ const thread = await client.threads.create({
121
+ thread_id: payload.thread_id,
122
+ metadata: payload.metadata as any,
123
+ if_exists: (payload.if_exists as any) ?? undefined,
124
+ });
125
+ return NextResponse.json(thread);
126
+ }
127
+
128
+ if (pathname.endsWith('/threads/search')) {
129
+ const body = await req.json();
130
+ const payload = ThreadSearchPayloadSchema.parse(body);
131
+ const result = await client.threads.search({
132
+ metadata: payload.metadata as any,
133
+ status: payload.status as any,
134
+ limit: payload.limit,
135
+ offset: payload.offset,
136
+ sortBy: (payload.sort_by as any) ?? undefined,
137
+ sortOrder: (payload.sort_order as any) ?? undefined,
138
+ });
139
+ return NextResponse.json(result, {
140
+ headers: { 'X-Pagination-Total': String(result.length) },
141
+ });
142
+ }
143
+
144
+ // Runs routes - stream
145
+ if (pathname.match(/\/threads\/[0-9a-fA-F-]{36}\/runs\/stream$/)) {
146
+ const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})\/runs\/stream$/);
147
+ if (match) {
148
+ const thread_id = match[1];
149
+ const body = await req.json();
150
+ const payload = RunStreamPayloadSchema.parse(body);
151
+ const generator = client.runs.stream(thread_id, payload.assistant_id as string, payload as any);
152
+ return sseResponse(generator as any);
153
+ }
154
+ }
155
+
156
+ // Runs routes - cancel
157
+ if (pathname.match(/\/threads\/[0-9a-fA-F-]{36}\/runs\/[0-9a-fA-F-]{36}\/cancel$/)) {
158
+ const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})\/runs\/([0-9a-fA-F-]{36})\/cancel$/);
159
+ if (match) {
160
+ const thread_id = match[1];
161
+ const run_id = match[2];
162
+ const waitParam = url.searchParams.get('wait');
163
+ const actionParam = url.searchParams.get('action');
164
+ const queryParams = {
165
+ wait: waitParam ? waitParam === 'true' : false,
166
+ action: actionParam ?? 'interrupt',
167
+ };
168
+ const { wait, action } = RunCancelQuerySchema.parse(queryParams);
169
+ const promise = client.runs.cancel(thread_id, run_id, wait, action);
170
+ if (wait) await promise;
171
+ return new Response(null, { status: wait ? 204 : 202 });
172
+ }
173
+ }
174
+
175
+ return new NextResponse('Not Found', { status: 404 });
176
+ }
177
+
178
+ export async function DELETE(req: NextRequest) {
179
+ const url = new URL(req.url);
180
+ const pathname = url.pathname;
181
+
182
+ // Threads routes
183
+ if (pathname.match(/\/threads\/[0-9a-fA-F-]{36}$/)) {
184
+ const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})$/);
185
+ if (match) {
186
+ const thread_id = match[1];
187
+ await client.threads.delete(thread_id);
188
+ return new NextResponse(null, { status: 204 });
189
+ }
190
+ }
191
+
192
+ return new NextResponse('Not Found', { status: 404 });
193
+ }
@@ -1,4 +1,4 @@
1
- import z from 'zod';
1
+ import z from "zod";
2
2
 
3
3
  export const AssistantConfigurable = z
4
4
  .object({
@@ -14,7 +14,7 @@ export const AssistantConfig = z
14
14
  configurable: AssistantConfigurable.optional(),
15
15
  })
16
16
  .catchall(z.unknown())
17
- .describe('The configuration of an assistant.');
17
+ .describe("The configuration of an assistant.");
18
18
 
19
19
  export const Assistant = z.object({
20
20
  assistant_id: z.string().uuid(),
@@ -27,7 +27,14 @@ export const Assistant = z.object({
27
27
 
28
28
  export const MetadataSchema = z
29
29
  .object({
30
- source: z.union([z.literal('input'), z.literal('loop'), z.literal('update'), z.string()]).optional(),
30
+ source: z
31
+ .union([
32
+ z.literal("input"),
33
+ z.literal("loop"),
34
+ z.literal("update"),
35
+ z.string(),
36
+ ])
37
+ .optional(),
31
38
  step: z.number().optional(),
32
39
  writes: z.record(z.unknown()).nullable().optional(),
33
40
  parents: z.record(z.string()).optional(),
@@ -41,9 +48,19 @@ export const SendSchema = z.object({
41
48
 
42
49
  export const CommandSchema = z.object({
43
50
  update: z
44
- .union([z.record(z.unknown()), z.array(z.tuple([z.string(), z.unknown()]))])
51
+ .union([
52
+ z.record(z.unknown()),
53
+ z.array(z.tuple([z.string(), z.unknown()])),
54
+ ])
45
55
  .nullable()
46
56
  .optional(),
47
57
  resume: z.unknown().optional(),
48
- goto: z.union([SendSchema, z.array(SendSchema), z.string(), z.array(z.string())]).optional(),
58
+ goto: z
59
+ .union([
60
+ SendSchema,
61
+ z.array(SendSchema),
62
+ z.string(),
63
+ z.array(z.string()),
64
+ ])
65
+ .optional(),
49
66
  });