@langgraph-js/pure-graph 1.4.1 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__vite-browser-external-DGN5jhtd.js +4 -0
- package/dist/__vite-browser-external-DGN5jhtd.js.map +1 -0
- package/dist/checkpoint-BHKV54sL.js +386 -0
- package/dist/checkpoint-BHKV54sL.js.map +1 -0
- package/dist/checkpoint-DxiUsHMy.js +13 -0
- package/dist/checkpoint-DxiUsHMy.js.map +1 -0
- package/dist/global.d.ts +1 -1
- package/dist/index-DcXE-SZb.js +1264 -0
- package/dist/index-DcXE-SZb.js.map +1 -0
- package/dist/index.js +2 -5
- package/dist/index.js.map +1 -0
- package/dist/queue-C6iEVbd2.js +120 -0
- package/dist/queue-C6iEVbd2.js.map +1 -0
- package/dist/storage/index.d.ts +1 -1
- package/dist/threads-BUgBiCiK.js +302 -0
- package/dist/threads-BUgBiCiK.js.map +1 -0
- package/package.json +6 -3
- package/dist/adapter/hono/assistants.js +0 -21
- package/dist/adapter/hono/endpoint.js +0 -2
- package/dist/adapter/hono/index.js +0 -11
- package/dist/adapter/hono/runs.js +0 -54
- package/dist/adapter/hono/threads.js +0 -30
- package/dist/adapter/nextjs/endpoint.js +0 -2
- package/dist/adapter/nextjs/index.js +0 -2
- package/dist/adapter/nextjs/router.js +0 -179
- package/dist/adapter/zod.js +0 -127
- package/dist/createEndpoint.js +0 -77
- package/dist/global.js +0 -13
- package/dist/graph/stream.js +0 -195
- package/dist/graph/stringify.js +0 -214
- package/dist/queue/JsonPlusSerializer.js +0 -138
- package/dist/queue/event_message.js +0 -27
- package/dist/queue/stream_queue.js +0 -177
- package/dist/storage/index.js +0 -64
- package/dist/storage/memory/checkpoint.js +0 -2
- package/dist/storage/memory/queue.js +0 -81
- package/dist/storage/memory/threads.js +0 -145
- package/dist/storage/pg/checkpoint.js +0 -9
- package/dist/storage/pg/threads.js +0 -303
- package/dist/storage/redis/queue.js +0 -130
- package/dist/storage/sqlite/DB.js +0 -14
- package/dist/storage/sqlite/checkpoint.js +0 -374
- package/dist/storage/sqlite/threads.js +0 -299
- package/dist/storage/sqlite/type.js +0 -1
- package/dist/threads/index.js +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/types.js +0 -1
- package/dist/utils/createEntrypointGraph.js +0 -11
- package/dist/utils/getGraph.js +0 -18
- package/dist/utils/getLangGraphCommand.js +0 -13
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/** @ts-ignore */
|
|
2
|
-
import { NextResponse } from 'next/server';
|
|
3
|
-
import { client } from './endpoint';
|
|
4
|
-
import { AssistantsSearchSchema, AssistantGraphQuerySchema, RunStreamPayloadSchema, RunListQuerySchema, RunCancelQuerySchema, ThreadCreatePayloadSchema, ThreadSearchPayloadSchema, ThreadStateUpdate, } from '../zod';
|
|
5
|
-
import { serialiseAsDict } from '../../graph/stream';
|
|
6
|
-
// Next.js App Router 的 SSE 响应实现
|
|
7
|
-
async function sseResponse(generator) {
|
|
8
|
-
const encoder = new TextEncoder();
|
|
9
|
-
const stream = new ReadableStream({
|
|
10
|
-
async start(controller) {
|
|
11
|
-
try {
|
|
12
|
-
for await (const { event, data } of generator) {
|
|
13
|
-
const line = `event: ${event}\n` + `data: ${serialiseAsDict(data, 0)}\n\n`;
|
|
14
|
-
controller.enqueue(encoder.encode(line));
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
catch (err) {
|
|
18
|
-
// ignore
|
|
19
|
-
}
|
|
20
|
-
finally {
|
|
21
|
-
controller.close();
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
return new Response(stream, {
|
|
26
|
-
headers: {
|
|
27
|
-
'Content-Type': 'text/event-stream; charset=utf-8',
|
|
28
|
-
'Cache-Control': 'no-cache, no-transform',
|
|
29
|
-
Connection: 'keep-alive',
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
// 统一路由处理器
|
|
34
|
-
export async function GET(req) {
|
|
35
|
-
const url = new URL(req.url);
|
|
36
|
-
const pathname = url.pathname;
|
|
37
|
-
// Assistants routes
|
|
38
|
-
if (pathname.match(/\/assistants\/[^/]+\/graph$/)) {
|
|
39
|
-
const match = pathname.match(/\/assistants\/([^/]+)\/graph$/);
|
|
40
|
-
if (match) {
|
|
41
|
-
const assistant_id = match[1];
|
|
42
|
-
const xrayParam = url.searchParams.get('xray') ?? undefined;
|
|
43
|
-
const queryParams = { xray: xrayParam };
|
|
44
|
-
const { xray } = AssistantGraphQuerySchema.parse(queryParams);
|
|
45
|
-
const data = await client.assistants.getGraph(assistant_id, {
|
|
46
|
-
xray: xray !== undefined ? xray === 'true' : undefined,
|
|
47
|
-
});
|
|
48
|
-
return NextResponse.json(data);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
// Threads routes
|
|
52
|
-
if (pathname.match(/\/threads\/[0-9a-fA-F-]{36}$/)) {
|
|
53
|
-
const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})$/);
|
|
54
|
-
if (match) {
|
|
55
|
-
const thread_id = match[1];
|
|
56
|
-
const data = await client.threads.get(thread_id);
|
|
57
|
-
return NextResponse.json(data);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
// Runs routes
|
|
61
|
-
if (pathname.match(/\/threads\/[0-9a-fA-F-]{36}\/runs$/)) {
|
|
62
|
-
const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})\/runs$/);
|
|
63
|
-
if (match) {
|
|
64
|
-
const thread_id = match[1];
|
|
65
|
-
const limit = url.searchParams.get('limit') ?? undefined;
|
|
66
|
-
const offset = url.searchParams.get('offset') ?? undefined;
|
|
67
|
-
const status = url.searchParams.get('status') ?? undefined;
|
|
68
|
-
const queryParams = { limit, offset, status };
|
|
69
|
-
const { limit: parsedLimit, offset: parsedOffset, status: parsedStatus, } = RunListQuerySchema.parse(queryParams);
|
|
70
|
-
const runs = await client.runs.list(thread_id, {
|
|
71
|
-
limit: parsedLimit,
|
|
72
|
-
offset: parsedOffset,
|
|
73
|
-
status: parsedStatus,
|
|
74
|
-
});
|
|
75
|
-
return Response.json(runs);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return new NextResponse('Not Found', { status: 404 });
|
|
79
|
-
}
|
|
80
|
-
export async function POST(req) {
|
|
81
|
-
const url = new URL(req.url);
|
|
82
|
-
const pathname = url.pathname;
|
|
83
|
-
// Assistants routes
|
|
84
|
-
if (pathname.endsWith('/assistants/search')) {
|
|
85
|
-
const body = await req.json();
|
|
86
|
-
const payload = AssistantsSearchSchema.parse(body);
|
|
87
|
-
const data = await client.assistants.search({
|
|
88
|
-
graphId: payload.graph_id,
|
|
89
|
-
metadata: payload.metadata,
|
|
90
|
-
limit: payload.limit,
|
|
91
|
-
offset: payload.offset,
|
|
92
|
-
});
|
|
93
|
-
return NextResponse.json(data, {
|
|
94
|
-
headers: { 'X-Pagination-Total': String(data.length) },
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
// Threads routes
|
|
98
|
-
if (pathname.endsWith('/threads')) {
|
|
99
|
-
const body = await req.json();
|
|
100
|
-
const payload = ThreadCreatePayloadSchema.parse(body);
|
|
101
|
-
const thread = await client.threads.create({
|
|
102
|
-
thread_id: payload.thread_id,
|
|
103
|
-
metadata: payload.metadata,
|
|
104
|
-
if_exists: payload.if_exists ?? undefined,
|
|
105
|
-
});
|
|
106
|
-
return NextResponse.json(thread);
|
|
107
|
-
}
|
|
108
|
-
if (pathname.endsWith('/threads/search')) {
|
|
109
|
-
const body = await req.json();
|
|
110
|
-
const payload = ThreadSearchPayloadSchema.parse(body);
|
|
111
|
-
const result = await client.threads.search({
|
|
112
|
-
metadata: payload.metadata,
|
|
113
|
-
status: payload.status,
|
|
114
|
-
limit: payload.limit,
|
|
115
|
-
offset: payload.offset,
|
|
116
|
-
sortBy: payload.sort_by ?? undefined,
|
|
117
|
-
sortOrder: payload.sort_order ?? undefined,
|
|
118
|
-
});
|
|
119
|
-
return NextResponse.json(result, {
|
|
120
|
-
headers: { 'X-Pagination-Total': String(result.length) },
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
// Threads state update
|
|
124
|
-
if (pathname.match(/\/threads\/[0-9a-fA-F-]{36}\/state$/)) {
|
|
125
|
-
const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})\/state$/);
|
|
126
|
-
if (match) {
|
|
127
|
-
const thread_id = match[1];
|
|
128
|
-
const body = await req.json();
|
|
129
|
-
const payload = ThreadStateUpdate.parse(body);
|
|
130
|
-
const result = await client.threads.updateState(thread_id, payload);
|
|
131
|
-
return NextResponse.json(result);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
// Runs routes - stream
|
|
135
|
-
if (pathname.match(/\/threads\/[0-9a-fA-F-]{36}\/runs\/stream$/)) {
|
|
136
|
-
const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})\/runs\/stream$/);
|
|
137
|
-
if (match) {
|
|
138
|
-
const thread_id = match[1];
|
|
139
|
-
const body = await req.json();
|
|
140
|
-
const payload = RunStreamPayloadSchema.parse(body);
|
|
141
|
-
const generator = client.runs.stream(thread_id, payload.assistant_id, payload);
|
|
142
|
-
return sseResponse(generator);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
// Runs routes - cancel
|
|
146
|
-
if (pathname.match(/\/threads\/[0-9a-fA-F-]{36}\/runs\/[0-9a-fA-F-]{36}\/cancel$/)) {
|
|
147
|
-
const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})\/runs\/([0-9a-fA-F-]{36})\/cancel$/);
|
|
148
|
-
if (match) {
|
|
149
|
-
const thread_id = match[1];
|
|
150
|
-
const run_id = match[2];
|
|
151
|
-
const waitParam = url.searchParams.get('wait') ?? undefined;
|
|
152
|
-
const actionParam = url.searchParams.get('action') ?? undefined;
|
|
153
|
-
const queryParams = {
|
|
154
|
-
wait: waitParam ? waitParam === 'true' : false,
|
|
155
|
-
action: actionParam ?? 'interrupt',
|
|
156
|
-
};
|
|
157
|
-
const { wait, action } = RunCancelQuerySchema.parse(queryParams);
|
|
158
|
-
const promise = client.runs.cancel(thread_id, run_id, wait, action);
|
|
159
|
-
if (wait)
|
|
160
|
-
await promise;
|
|
161
|
-
return new Response(null, { status: wait ? 204 : 202 });
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return new NextResponse('Not Found', { status: 404 });
|
|
165
|
-
}
|
|
166
|
-
export async function DELETE(req) {
|
|
167
|
-
const url = new URL(req.url);
|
|
168
|
-
const pathname = url.pathname;
|
|
169
|
-
// Threads routes
|
|
170
|
-
if (pathname.match(/\/threads\/[0-9a-fA-F-]{36}$/)) {
|
|
171
|
-
const match = pathname.match(/\/threads\/([0-9a-fA-F-]{36})$/);
|
|
172
|
-
if (match) {
|
|
173
|
-
const thread_id = match[1];
|
|
174
|
-
await client.threads.delete(thread_id);
|
|
175
|
-
return new NextResponse(null, { status: 204 });
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return new NextResponse('Not Found', { status: 404 });
|
|
179
|
-
}
|
package/dist/adapter/zod.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import z from 'zod';
|
|
2
|
-
export const AssistantConfigurable = z
|
|
3
|
-
.object({
|
|
4
|
-
thread_id: z.string().optional(),
|
|
5
|
-
thread_ts: z.string().optional(),
|
|
6
|
-
})
|
|
7
|
-
.catchall(z.unknown());
|
|
8
|
-
export const AssistantConfig = z
|
|
9
|
-
.object({
|
|
10
|
-
tags: z.array(z.string()).optional(),
|
|
11
|
-
recursion_limit: z.number().int().optional(),
|
|
12
|
-
configurable: AssistantConfigurable.optional(),
|
|
13
|
-
})
|
|
14
|
-
.catchall(z.unknown())
|
|
15
|
-
.describe('The configuration of an assistant.');
|
|
16
|
-
export const Assistant = z.object({
|
|
17
|
-
assistant_id: z.string().uuid(),
|
|
18
|
-
graph_id: z.string(),
|
|
19
|
-
config: AssistantConfig,
|
|
20
|
-
created_at: z.string(),
|
|
21
|
-
updated_at: z.string(),
|
|
22
|
-
metadata: z.object({}).catchall(z.any()),
|
|
23
|
-
});
|
|
24
|
-
export const MetadataSchema = z
|
|
25
|
-
.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.record(z.unknown()).nullable().optional(),
|
|
29
|
-
parents: z.record(z.string()).optional(),
|
|
30
|
-
})
|
|
31
|
-
.catchall(z.unknown());
|
|
32
|
-
export const SendSchema = z.object({
|
|
33
|
-
node: z.string(),
|
|
34
|
-
input: z.unknown().nullable(),
|
|
35
|
-
});
|
|
36
|
-
export const CommandSchema = z.object({
|
|
37
|
-
update: z
|
|
38
|
-
.union([z.record(z.unknown()), z.array(z.tuple([z.string(), z.unknown()]))])
|
|
39
|
-
.nullable()
|
|
40
|
-
.optional(),
|
|
41
|
-
resume: z.unknown().optional(),
|
|
42
|
-
goto: z.union([SendSchema, z.array(SendSchema), z.string(), z.array(z.string())]).optional(),
|
|
43
|
-
});
|
|
44
|
-
// 公共的查询参数验证 schema
|
|
45
|
-
export const PaginationQuerySchema = z.object({
|
|
46
|
-
limit: z.number().int().optional(),
|
|
47
|
-
offset: z.number().int().optional(),
|
|
48
|
-
});
|
|
49
|
-
export const ThreadIdParamSchema = z.object({
|
|
50
|
-
thread_id: z.string().uuid(),
|
|
51
|
-
});
|
|
52
|
-
export const RunIdParamSchema = z.object({
|
|
53
|
-
thread_id: z.string().uuid(),
|
|
54
|
-
run_id: z.string().uuid(),
|
|
55
|
-
});
|
|
56
|
-
// Assistants 相关的 schema
|
|
57
|
-
export const AssistantsSearchSchema = z.object({
|
|
58
|
-
graph_id: z.string().optional(),
|
|
59
|
-
metadata: MetadataSchema.optional(),
|
|
60
|
-
limit: z.number().int().optional(),
|
|
61
|
-
offset: z.number().int().optional(),
|
|
62
|
-
});
|
|
63
|
-
export const AssistantGraphQuerySchema = z.object({
|
|
64
|
-
xray: z.string().optional(),
|
|
65
|
-
});
|
|
66
|
-
// Runs 相关的 schema
|
|
67
|
-
export const RunStreamPayloadSchema = z
|
|
68
|
-
.object({
|
|
69
|
-
assistant_id: z.union([z.string().uuid(), z.string()]),
|
|
70
|
-
checkpoint_id: z.string().optional(),
|
|
71
|
-
input: z.any().optional(),
|
|
72
|
-
command: CommandSchema.optional(),
|
|
73
|
-
metadata: MetadataSchema.optional(),
|
|
74
|
-
config: AssistantConfig.optional(),
|
|
75
|
-
webhook: z.string().optional(),
|
|
76
|
-
interrupt_before: z.union([z.literal('*'), z.array(z.string())]).optional(),
|
|
77
|
-
interrupt_after: z.union([z.literal('*'), z.array(z.string())]).optional(),
|
|
78
|
-
on_disconnect: z.enum(['cancel', 'continue']).optional().default('continue'),
|
|
79
|
-
multitask_strategy: z.enum(['reject', 'rollback', 'interrupt', 'enqueue']).optional(),
|
|
80
|
-
stream_mode: z
|
|
81
|
-
.array(z.enum(['values', 'messages', 'messages-tuple', 'updates', 'events', 'debug', 'custom']))
|
|
82
|
-
.optional(),
|
|
83
|
-
stream_subgraphs: z.boolean().optional(),
|
|
84
|
-
stream_resumable: z.boolean().optional(),
|
|
85
|
-
after_seconds: z.number().optional(),
|
|
86
|
-
if_not_exists: z.enum(['create', 'reject']).optional(),
|
|
87
|
-
on_completion: z.enum(['complete', 'continue']).optional(),
|
|
88
|
-
feedback_keys: z.array(z.string()).optional(),
|
|
89
|
-
langsmith_tracer: z.unknown().optional(),
|
|
90
|
-
})
|
|
91
|
-
.describe('Payload for creating a stateful run.');
|
|
92
|
-
export const RunListQuerySchema = z.object({
|
|
93
|
-
limit: z.coerce.number().int().optional(),
|
|
94
|
-
offset: z.coerce.number().int().optional(),
|
|
95
|
-
status: z.enum(['pending', 'running', 'error', 'success', 'timeout', 'interrupted']).optional(),
|
|
96
|
-
});
|
|
97
|
-
export const RunCancelQuerySchema = z.object({
|
|
98
|
-
wait: z.coerce.boolean().optional().default(false),
|
|
99
|
-
action: z.enum(['interrupt', 'rollback']).optional().default('interrupt'),
|
|
100
|
-
});
|
|
101
|
-
// Threads 相关的 schema
|
|
102
|
-
export const ThreadCreatePayloadSchema = z
|
|
103
|
-
.object({
|
|
104
|
-
thread_id: z.string().uuid().describe('The ID of the thread. If not provided, an ID is generated.').optional(),
|
|
105
|
-
metadata: MetadataSchema.optional(),
|
|
106
|
-
if_exists: z.union([z.literal('raise'), z.literal('do_nothing')]).optional(),
|
|
107
|
-
})
|
|
108
|
-
.describe('Payload for creating a thread.');
|
|
109
|
-
export const ThreadSearchPayloadSchema = z
|
|
110
|
-
.object({
|
|
111
|
-
metadata: z.record(z.unknown()).describe('Metadata to search for.').optional(),
|
|
112
|
-
status: z.enum(['idle', 'busy', 'interrupted', 'error']).describe('Filter by thread status.').optional(),
|
|
113
|
-
values: z.record(z.unknown()).describe('Filter by thread values.').optional(),
|
|
114
|
-
limit: z.number().int().gte(1).lte(1000).describe('Maximum number to return.').optional(),
|
|
115
|
-
offset: z.number().int().gte(0).describe('Offset to start from.').optional(),
|
|
116
|
-
sort_by: z.enum(['thread_id', 'status', 'created_at', 'updated_at']).describe('Sort by field.').optional(),
|
|
117
|
-
sort_order: z.enum(['asc', 'desc']).describe('Sort order.').optional(),
|
|
118
|
-
})
|
|
119
|
-
.describe('Payload for listing threads.');
|
|
120
|
-
export const ThreadStateUpdate = z
|
|
121
|
-
.object({
|
|
122
|
-
values: z.union([z.record(z.string(), z.unknown()), z.array(z.record(z.string(), z.unknown()))]).nullish(),
|
|
123
|
-
// as_node: z.string().optional(),
|
|
124
|
-
// checkpoint_id: z.string().optional(),
|
|
125
|
-
// checkpoint: CheckpointSchema.nullish(),
|
|
126
|
-
})
|
|
127
|
-
.describe('Payload for adding state to a thread.');
|
package/dist/createEndpoint.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { streamState } from './graph/stream.js';
|
|
2
|
-
import { getGraph, GRAPHS } from './utils/getGraph.js';
|
|
3
|
-
import { LangGraphGlobal } from './global.js';
|
|
4
|
-
export { registerGraph } from './utils/getGraph.js';
|
|
5
|
-
export const AssistantEndpoint = {
|
|
6
|
-
async search(query) {
|
|
7
|
-
if (query?.graphId) {
|
|
8
|
-
return [
|
|
9
|
-
{
|
|
10
|
-
assistant_id: query.graphId,
|
|
11
|
-
graph_id: query.graphId,
|
|
12
|
-
config: {},
|
|
13
|
-
created_at: new Date().toISOString(),
|
|
14
|
-
updated_at: new Date().toISOString(),
|
|
15
|
-
metadata: {},
|
|
16
|
-
version: 1,
|
|
17
|
-
name: query.graphId,
|
|
18
|
-
description: '',
|
|
19
|
-
},
|
|
20
|
-
];
|
|
21
|
-
}
|
|
22
|
-
return Object.entries(GRAPHS).map(([graphId, _]) => ({
|
|
23
|
-
assistant_id: graphId,
|
|
24
|
-
graph_id: graphId,
|
|
25
|
-
config: {},
|
|
26
|
-
metadata: {},
|
|
27
|
-
version: 1,
|
|
28
|
-
name: graphId,
|
|
29
|
-
description: '',
|
|
30
|
-
created_at: new Date().toISOString(),
|
|
31
|
-
updated_at: new Date().toISOString(),
|
|
32
|
-
}));
|
|
33
|
-
},
|
|
34
|
-
async getGraph(assistantId, options) {
|
|
35
|
-
const config = {};
|
|
36
|
-
const graph = await getGraph(assistantId, config);
|
|
37
|
-
const drawable = await graph.getGraphAsync({
|
|
38
|
-
...config,
|
|
39
|
-
xray: options?.xray ?? undefined,
|
|
40
|
-
});
|
|
41
|
-
return drawable.toJSON();
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
export const createEndpoint = () => {
|
|
45
|
-
const threads = LangGraphGlobal.globalThreadsManager;
|
|
46
|
-
return {
|
|
47
|
-
assistants: AssistantEndpoint,
|
|
48
|
-
threads,
|
|
49
|
-
runs: {
|
|
50
|
-
list(threadId, options) {
|
|
51
|
-
return threads.listRuns(threadId, options);
|
|
52
|
-
},
|
|
53
|
-
async cancel(threadId, runId, wait, action) {
|
|
54
|
-
return LangGraphGlobal.globalMessageQueue.cancelQueue(runId);
|
|
55
|
-
},
|
|
56
|
-
async *stream(threadId, assistantId, payload) {
|
|
57
|
-
if (!payload.config) {
|
|
58
|
-
payload.config = {
|
|
59
|
-
configurable: {
|
|
60
|
-
graph_id: assistantId,
|
|
61
|
-
thread_id: threadId,
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
for await (const data of streamState(threads, threads.createRun(threadId, assistantId, payload), payload, {
|
|
66
|
-
attempt: 0,
|
|
67
|
-
getGraph,
|
|
68
|
-
})) {
|
|
69
|
-
yield data;
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
joinStream(threadId, runId, options) {
|
|
73
|
-
throw new Error('Function not implemented.');
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
};
|
package/dist/global.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { createCheckPointer, createMessageQueue, createThreadManager } from './storage/index.js';
|
|
2
|
-
const [globalMessageQueue, globalCheckPointer] = await Promise.all([createMessageQueue(), createCheckPointer()]);
|
|
3
|
-
console.debug('LG | checkpointer created');
|
|
4
|
-
const globalThreadsManager = await createThreadManager({
|
|
5
|
-
checkpointer: globalCheckPointer,
|
|
6
|
-
});
|
|
7
|
-
console.debug('LG | threads manager created');
|
|
8
|
-
console.debug('LG | global init done');
|
|
9
|
-
export class LangGraphGlobal {
|
|
10
|
-
static globalMessageQueue = globalMessageQueue;
|
|
11
|
-
static globalCheckPointer = globalCheckPointer;
|
|
12
|
-
static globalThreadsManager = globalThreadsManager;
|
|
13
|
-
}
|
package/dist/graph/stream.js
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { isBaseMessage } from '@langchain/core/messages';
|
|
2
|
-
import { getLangGraphCommand } from '../utils/getLangGraphCommand.js';
|
|
3
|
-
import { LangGraphGlobal } from '../global.js';
|
|
4
|
-
import { EventMessage, StreamErrorEventMessage, StreamEndEventMessage } from '../queue/event_message.js';
|
|
5
|
-
export async function streamStateWithQueue(threads, run, queue, payload, options) {
|
|
6
|
-
const kwargs = payload;
|
|
7
|
-
const graphId = kwargs.config?.configurable?.graph_id;
|
|
8
|
-
if (!graphId || typeof graphId !== 'string') {
|
|
9
|
-
throw new Error('Invalid or missing graph_id');
|
|
10
|
-
}
|
|
11
|
-
const graph = await options.getGraph(graphId, payload.config, {
|
|
12
|
-
checkpointer: payload.temporary ? null : undefined,
|
|
13
|
-
});
|
|
14
|
-
const userStreamMode = payload.stream_mode ?? [];
|
|
15
|
-
const libStreamMode = new Set([
|
|
16
|
-
'values',
|
|
17
|
-
...userStreamMode.filter((mode) => mode !== 'events' && mode !== 'messages-tuple'),
|
|
18
|
-
]);
|
|
19
|
-
if (userStreamMode.includes('messages-tuple')) {
|
|
20
|
-
libStreamMode.add('messages');
|
|
21
|
-
}
|
|
22
|
-
if (userStreamMode.includes('messages')) {
|
|
23
|
-
libStreamMode.add('values');
|
|
24
|
-
}
|
|
25
|
-
await queue.push(new EventMessage('metadata', {
|
|
26
|
-
run_id: run.run_id,
|
|
27
|
-
attempt: options.attempt,
|
|
28
|
-
graph_id: graphId,
|
|
29
|
-
}));
|
|
30
|
-
const metadata = {
|
|
31
|
-
...payload.config?.metadata,
|
|
32
|
-
run_attempt: options.attempt,
|
|
33
|
-
};
|
|
34
|
-
const events = graph.streamEvents(payload.command != null ? getLangGraphCommand(payload.command) : payload.input ?? null, {
|
|
35
|
-
version: 'v2',
|
|
36
|
-
interruptAfter: payload.interrupt_after,
|
|
37
|
-
interruptBefore: payload.interrupt_before,
|
|
38
|
-
tags: payload.config?.tags,
|
|
39
|
-
configurable: payload.config?.configurable,
|
|
40
|
-
recursionLimit: payload.config?.recursionLimit,
|
|
41
|
-
subgraphs: payload.stream_subgraphs,
|
|
42
|
-
metadata,
|
|
43
|
-
runId: run.run_id,
|
|
44
|
-
streamMode: [...libStreamMode],
|
|
45
|
-
signal: queue.cancelSignal.signal,
|
|
46
|
-
});
|
|
47
|
-
const messages = {};
|
|
48
|
-
const completedIds = new Set();
|
|
49
|
-
try {
|
|
50
|
-
for await (const event of events) {
|
|
51
|
-
// console.log(event);
|
|
52
|
-
if (event.tags?.includes('langsmith:hidden'))
|
|
53
|
-
continue;
|
|
54
|
-
if (event.event === 'on_chain_stream' && event.run_id === run.run_id) {
|
|
55
|
-
const [ns, mode, chunk] = (payload.stream_subgraphs ? event.data.chunk : [null, ...event.data.chunk]);
|
|
56
|
-
// Listen for debug events and capture checkpoint
|
|
57
|
-
let data = chunk;
|
|
58
|
-
if (mode === 'messages') {
|
|
59
|
-
if (userStreamMode.includes('messages-tuple')) {
|
|
60
|
-
await queue.push(new EventMessage('messages', data));
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
else if (userStreamMode.includes(mode)) {
|
|
64
|
-
if (payload.stream_subgraphs && ns?.length) {
|
|
65
|
-
await queue.push(new EventMessage(`${mode}|${ns.join('|')}`, data));
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
await queue.push(new EventMessage(mode, data));
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (mode === 'values') {
|
|
72
|
-
await threads.set(run.thread_id, {
|
|
73
|
-
values: data ? JSON.parse(serialiseAsDict(data)) : '',
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
else if (userStreamMode.includes('events')) {
|
|
78
|
-
await queue.push(new EventMessage('events', event));
|
|
79
|
-
}
|
|
80
|
-
// TODO: we still rely on old messages mode based of streamMode=values
|
|
81
|
-
// In order to fully switch to library messages mode, we need to do ensure that
|
|
82
|
-
// `StreamMessagesHandler` sends the final message, which requires the following:
|
|
83
|
-
// - handleLLMEnd does not send the final message b/c handleLLMNewToken sets the this.emittedChatModelRunIds[runId] flag. Python does not do that
|
|
84
|
-
// - handleLLMEnd receives the final message as BaseMessageChunk rather than BaseMessage, which from the outside will become indistinguishable.
|
|
85
|
-
// - handleLLMEnd should not dedupe the message
|
|
86
|
-
// - Don't think there's an utility that would convert a BaseMessageChunk to a BaseMessage?
|
|
87
|
-
if (userStreamMode.includes('messages')) {
|
|
88
|
-
if (event.event === 'on_chain_stream' && event.run_id === run.run_id) {
|
|
89
|
-
const newMessages = [];
|
|
90
|
-
const [_, chunk] = event.data.chunk;
|
|
91
|
-
let chunkMessages = [];
|
|
92
|
-
if (typeof chunk === 'object' && chunk != null && 'messages' in chunk && !isBaseMessage(chunk)) {
|
|
93
|
-
chunkMessages = chunk?.messages;
|
|
94
|
-
}
|
|
95
|
-
if (!Array.isArray(chunkMessages)) {
|
|
96
|
-
chunkMessages = [chunkMessages];
|
|
97
|
-
}
|
|
98
|
-
for (const message of chunkMessages) {
|
|
99
|
-
if (!message.id || completedIds.has(message.id))
|
|
100
|
-
continue;
|
|
101
|
-
completedIds.add(message.id);
|
|
102
|
-
newMessages.push(message);
|
|
103
|
-
}
|
|
104
|
-
if (newMessages.length > 0) {
|
|
105
|
-
await queue.push(new EventMessage('messages/complete', newMessages));
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
else if (event.event === 'on_chat_model_stream' && !event.tags?.includes('nostream')) {
|
|
109
|
-
const message = event.data.chunk;
|
|
110
|
-
if (!message.id)
|
|
111
|
-
continue;
|
|
112
|
-
if (messages[message.id] == null) {
|
|
113
|
-
messages[message.id] = message;
|
|
114
|
-
await queue.push(new EventMessage('messages/metadata', {
|
|
115
|
-
[message.id]: { metadata: event.metadata },
|
|
116
|
-
}));
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
messages[message.id] = messages[message.id].concat(message);
|
|
120
|
-
}
|
|
121
|
-
await queue.push(new EventMessage('messages/partial', [messages[message.id]]));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
finally {
|
|
127
|
-
// 发送流结束信号
|
|
128
|
-
await queue.push(new StreamEndEventMessage());
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* 从队列创建数据流生成器
|
|
133
|
-
* @param queueId 队列 ID
|
|
134
|
-
* @param signal 中止信号
|
|
135
|
-
* @returns 数据流生成器
|
|
136
|
-
*/
|
|
137
|
-
export async function* createStreamFromQueue(queueId) {
|
|
138
|
-
const queue = LangGraphGlobal.globalMessageQueue.getQueue(queueId);
|
|
139
|
-
return queue.onDataReceive();
|
|
140
|
-
}
|
|
141
|
-
export const serialiseAsDict = (obj, indent = 2) => {
|
|
142
|
-
return JSON.stringify(obj, function (key, value) {
|
|
143
|
-
const rawValue = this[key];
|
|
144
|
-
if (rawValue != null &&
|
|
145
|
-
typeof rawValue === 'object' &&
|
|
146
|
-
'toDict' in rawValue &&
|
|
147
|
-
typeof rawValue.toDict === 'function') {
|
|
148
|
-
// TODO: we need to upstream this to LangChainJS
|
|
149
|
-
const { type, data } = rawValue.toDict();
|
|
150
|
-
return { ...data, type };
|
|
151
|
-
}
|
|
152
|
-
return value;
|
|
153
|
-
}, indent);
|
|
154
|
-
};
|
|
155
|
-
/**
|
|
156
|
-
* 兼容性函数:保持原有 API,同时使用队列模式
|
|
157
|
-
* @param run 运行配置
|
|
158
|
-
* @param options 选项
|
|
159
|
-
* @returns 数据流生成器
|
|
160
|
-
*/
|
|
161
|
-
export async function* streamState(threads, run, payload, options) {
|
|
162
|
-
run = await run;
|
|
163
|
-
// 生成唯一的队列 ID
|
|
164
|
-
const queueId = run.run_id;
|
|
165
|
-
const threadId = run.thread_id;
|
|
166
|
-
try {
|
|
167
|
-
// 启动队列推送任务(在后台异步执行)
|
|
168
|
-
await threads.set(threadId, { status: 'busy' });
|
|
169
|
-
await threads.updateRun(run.run_id, { status: 'running' });
|
|
170
|
-
const queue = LangGraphGlobal.globalMessageQueue.createQueue(queueId);
|
|
171
|
-
const state = queue.onDataReceive();
|
|
172
|
-
streamStateWithQueue(threads, run, queue, payload, options).catch((error) => {
|
|
173
|
-
console.error('Queue task error:', error);
|
|
174
|
-
// 如果生产者出错,向队列推送错误信号
|
|
175
|
-
LangGraphGlobal.globalMessageQueue.pushToQueue(queueId, new StreamErrorEventMessage(error));
|
|
176
|
-
// TODO 不知道这里需不需要错误处理
|
|
177
|
-
});
|
|
178
|
-
for await (const data of state) {
|
|
179
|
-
yield data;
|
|
180
|
-
}
|
|
181
|
-
await threads.updateRun(run.run_id, { status: 'success' });
|
|
182
|
-
}
|
|
183
|
-
catch (error) {
|
|
184
|
-
// 如果发生错误,确保清理资源
|
|
185
|
-
console.error('Stream error:', error);
|
|
186
|
-
await threads.updateRun(run.run_id, { status: 'error' });
|
|
187
|
-
await threads.set(threadId, { status: 'error' });
|
|
188
|
-
// throw error;
|
|
189
|
-
}
|
|
190
|
-
finally {
|
|
191
|
-
// 在完成后清理队列
|
|
192
|
-
await threads.set(threadId, { status: 'idle' });
|
|
193
|
-
LangGraphGlobal.globalMessageQueue.removeQueue(queueId);
|
|
194
|
-
}
|
|
195
|
-
}
|