@axiom-lattice/gateway 1.0.12 → 1.0.13
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/.turbo/turbo-build.log +10 -10
- package/RESUME_STREAM_CONTENT_BASED.md +325 -0
- package/RESUME_STREAM_README.md +388 -0
- package/dist/index.d.mts +47 -1
- package/dist/index.d.ts +47 -1
- package/dist/index.js +323 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +330 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
- package/src/controllers/run.ts +62 -1
- package/src/index.ts +4 -4
- package/src/routes/index.ts +36 -3
- package/src/schemas/index.ts +194 -0
- package/src/services/agent_service.ts +128 -14
- package/src/swagger.ts +77 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { FastifySchema } from "fastify";
|
|
2
|
+
|
|
3
|
+
// Create Run Schemas
|
|
4
|
+
export const createRunSchema: FastifySchema = {
|
|
5
|
+
description: "Create a new agent run",
|
|
6
|
+
tags: ["Runs"],
|
|
7
|
+
summary: "Create Agent Run",
|
|
8
|
+
body: {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {
|
|
11
|
+
thread_id: { type: "string", description: "Thread ID" },
|
|
12
|
+
assistant_id: { type: "string", description: "Assistant ID" },
|
|
13
|
+
message: { type: "string", description: "Message data for the run" },
|
|
14
|
+
command: {
|
|
15
|
+
type: "object",
|
|
16
|
+
description: "Command data for the run",
|
|
17
|
+
nullable: true,
|
|
18
|
+
},
|
|
19
|
+
streaming: {
|
|
20
|
+
type: "boolean",
|
|
21
|
+
description: "Whether to stream the response",
|
|
22
|
+
nullable: true,
|
|
23
|
+
},
|
|
24
|
+
background: {
|
|
25
|
+
type: "boolean",
|
|
26
|
+
description: "Whether to run in background",
|
|
27
|
+
nullable: true,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ["thread_id", "assistant_id", "message"],
|
|
31
|
+
},
|
|
32
|
+
response: {
|
|
33
|
+
200: {},
|
|
34
|
+
400: {},
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Memory Schemas
|
|
39
|
+
export const getAllMemoryItemsSchema: FastifySchema = {
|
|
40
|
+
description: "Get all memory items for an assistant thread",
|
|
41
|
+
tags: ["Memory"],
|
|
42
|
+
summary: "Get All Memory Items",
|
|
43
|
+
params: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
assistantId: { type: "string", description: "Assistant ID" },
|
|
47
|
+
thread_id: { type: "string", description: "Thread ID" },
|
|
48
|
+
},
|
|
49
|
+
required: ["assistantId", "thread_id"],
|
|
50
|
+
},
|
|
51
|
+
response: {
|
|
52
|
+
200: {},
|
|
53
|
+
400: {},
|
|
54
|
+
500: {},
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const getAgentStateSchema: FastifySchema = {
|
|
59
|
+
description: "Get agent state for an assistant thread",
|
|
60
|
+
tags: ["Memory"],
|
|
61
|
+
summary: "Get Agent State",
|
|
62
|
+
params: {
|
|
63
|
+
type: "object",
|
|
64
|
+
properties: {
|
|
65
|
+
assistantId: { type: "string", description: "Assistant ID" },
|
|
66
|
+
thread_id: { type: "string", description: "Thread ID" },
|
|
67
|
+
},
|
|
68
|
+
required: ["assistantId", "thread_id"],
|
|
69
|
+
},
|
|
70
|
+
response: {
|
|
71
|
+
200: {},
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const getMemoryItemSchema: FastifySchema = {
|
|
76
|
+
description: "Get a specific memory item by key",
|
|
77
|
+
tags: ["Memory"],
|
|
78
|
+
summary: "Get Memory Item",
|
|
79
|
+
params: {
|
|
80
|
+
type: "object",
|
|
81
|
+
properties: {
|
|
82
|
+
assistantId: { type: "string", description: "Assistant ID" },
|
|
83
|
+
key: { type: "string", description: "Memory key" },
|
|
84
|
+
},
|
|
85
|
+
required: ["assistantId", "key"],
|
|
86
|
+
},
|
|
87
|
+
response: {
|
|
88
|
+
200: {},
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const setMemoryItemSchema: FastifySchema = {
|
|
93
|
+
description: "Set or update a memory item",
|
|
94
|
+
tags: ["Memory"],
|
|
95
|
+
summary: "Set Memory Item",
|
|
96
|
+
params: {
|
|
97
|
+
type: "object",
|
|
98
|
+
properties: {
|
|
99
|
+
assistantId: { type: "string", description: "Assistant ID" },
|
|
100
|
+
key: { type: "string", description: "Memory key" },
|
|
101
|
+
},
|
|
102
|
+
required: ["assistantId", "key"],
|
|
103
|
+
},
|
|
104
|
+
body: {
|
|
105
|
+
type: "object",
|
|
106
|
+
description: "Memory item data",
|
|
107
|
+
},
|
|
108
|
+
response: {
|
|
109
|
+
200: {},
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const deleteMemoryItemSchema: FastifySchema = {
|
|
114
|
+
description: "Delete a specific memory item",
|
|
115
|
+
tags: ["Memory"],
|
|
116
|
+
summary: "Delete Memory Item",
|
|
117
|
+
params: {
|
|
118
|
+
type: "object",
|
|
119
|
+
properties: {
|
|
120
|
+
assistantId: { type: "string", description: "Assistant ID" },
|
|
121
|
+
key: { type: "string", description: "Memory key" },
|
|
122
|
+
},
|
|
123
|
+
required: ["assistantId", "key"],
|
|
124
|
+
},
|
|
125
|
+
response: {
|
|
126
|
+
200: {},
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const clearMemorySchema: FastifySchema = {
|
|
131
|
+
description: "Clear all memory items for an assistant",
|
|
132
|
+
tags: ["Memory"],
|
|
133
|
+
summary: "Clear Memory",
|
|
134
|
+
params: {
|
|
135
|
+
type: "object",
|
|
136
|
+
properties: {
|
|
137
|
+
assistantId: { type: "string", description: "Assistant ID" },
|
|
138
|
+
},
|
|
139
|
+
required: ["assistantId"],
|
|
140
|
+
},
|
|
141
|
+
response: {
|
|
142
|
+
200: {},
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// Graph Schema
|
|
147
|
+
export const getAgentGraphSchema: FastifySchema = {
|
|
148
|
+
description: "Get agent graph visualization",
|
|
149
|
+
tags: ["Graph"],
|
|
150
|
+
summary: "Get Agent Graph",
|
|
151
|
+
params: {
|
|
152
|
+
type: "object",
|
|
153
|
+
properties: {
|
|
154
|
+
assistantId: { type: "string", description: "Assistant ID" },
|
|
155
|
+
},
|
|
156
|
+
required: ["assistantId"],
|
|
157
|
+
},
|
|
158
|
+
response: {
|
|
159
|
+
200: {},
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Resume Stream Schema
|
|
164
|
+
export const resumeStreamSchema: FastifySchema = {
|
|
165
|
+
description: "Resume streaming from a known position",
|
|
166
|
+
tags: ["Streaming"],
|
|
167
|
+
summary: "Resume Stream",
|
|
168
|
+
body: {
|
|
169
|
+
type: "object",
|
|
170
|
+
properties: {
|
|
171
|
+
thread_id: { type: "string", description: "Thread ID" },
|
|
172
|
+
message_id: {
|
|
173
|
+
type: "string",
|
|
174
|
+
description: "Message ID (usually run_id)",
|
|
175
|
+
},
|
|
176
|
+
known_content: {
|
|
177
|
+
type: "string",
|
|
178
|
+
description: "Content already received",
|
|
179
|
+
},
|
|
180
|
+
poll_interval: {
|
|
181
|
+
type: "number",
|
|
182
|
+
description: "Polling interval in milliseconds",
|
|
183
|
+
nullable: true,
|
|
184
|
+
default: 100,
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
required: ["thread_id", "message_id"],
|
|
188
|
+
},
|
|
189
|
+
response: {
|
|
190
|
+
200: {},
|
|
191
|
+
400: {},
|
|
192
|
+
500: {},
|
|
193
|
+
},
|
|
194
|
+
};
|
|
@@ -10,7 +10,14 @@ import {
|
|
|
10
10
|
import { Command, CommandParams } from "@langchain/langgraph";
|
|
11
11
|
import { v4 } from "uuid";
|
|
12
12
|
// 修改导入路径,使用 lattice_core 包
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
getAgentClient,
|
|
15
|
+
getAgentLattice,
|
|
16
|
+
InMemoryChunkBuffer,
|
|
17
|
+
registerChunkBuffer,
|
|
18
|
+
getChunkBuffer,
|
|
19
|
+
hasChunkBuffer,
|
|
20
|
+
} from "@axiom-lattice/core";
|
|
14
21
|
|
|
15
22
|
function isAIMessageChunk(msg: any): msg is AIMessageChunk {
|
|
16
23
|
return msg && msg.constructor.name === "AIMessageChunk";
|
|
@@ -25,6 +32,20 @@ function isToolMessage(msg: any): msg is ToolMessage {
|
|
|
25
32
|
return msg && msg.constructor.name === "ToolMessage";
|
|
26
33
|
}
|
|
27
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Get or create the global ChunkBuffer instance
|
|
37
|
+
*/
|
|
38
|
+
function getOrCreateChunkBuffer(): InMemoryChunkBuffer {
|
|
39
|
+
if (!hasChunkBuffer("default")) {
|
|
40
|
+
const buffer = new InMemoryChunkBuffer({
|
|
41
|
+
ttl: 60 * 60 * 1000, // 1 hour TTL
|
|
42
|
+
cleanupInterval: 5 * 60 * 1000, // Clean every 5 minutes
|
|
43
|
+
});
|
|
44
|
+
registerChunkBuffer("default", buffer);
|
|
45
|
+
}
|
|
46
|
+
return getChunkBuffer("default") as InMemoryChunkBuffer;
|
|
47
|
+
}
|
|
48
|
+
|
|
28
49
|
export async function agent_invoke({
|
|
29
50
|
input,
|
|
30
51
|
thread_id,
|
|
@@ -57,15 +78,22 @@ export async function agent_invoke({
|
|
|
57
78
|
configurable: {
|
|
58
79
|
thread_id: thread_id,
|
|
59
80
|
run_id: run_id || v4(),
|
|
60
|
-
recursionLimit: 200,
|
|
61
81
|
"x-tenant-id": tenant_id,
|
|
62
82
|
"x-request-id": run_id,
|
|
63
83
|
"x-thread-id": thread_id,
|
|
64
84
|
},
|
|
85
|
+
recursionLimit: 200,
|
|
65
86
|
}
|
|
66
87
|
);
|
|
67
88
|
|
|
68
|
-
|
|
89
|
+
const data = result.messages.map((message: BaseMessage) => {
|
|
90
|
+
const { type, data } = message.toDict();
|
|
91
|
+
return {
|
|
92
|
+
...data,
|
|
93
|
+
role: type,
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
return { messages: data };
|
|
69
97
|
}
|
|
70
98
|
|
|
71
99
|
export async function agent_stream({
|
|
@@ -92,6 +120,9 @@ export async function agent_stream({
|
|
|
92
120
|
messages = [humanMessage];
|
|
93
121
|
}
|
|
94
122
|
|
|
123
|
+
// Get ChunkBuffer instance
|
|
124
|
+
const chunkBuffer = getOrCreateChunkBuffer();
|
|
125
|
+
|
|
95
126
|
try {
|
|
96
127
|
if (!runnable_agent) {
|
|
97
128
|
throw new Error(`Agent ${assistant_id} not found`);
|
|
@@ -115,15 +146,18 @@ export async function agent_stream({
|
|
|
115
146
|
},
|
|
116
147
|
streamMode: ["updates", "messages"],
|
|
117
148
|
subgraphs: false,
|
|
149
|
+
recursionLimit: 200,
|
|
118
150
|
}
|
|
119
151
|
);
|
|
120
152
|
|
|
121
|
-
// 创建一个可迭代的 ReadableStream
|
|
153
|
+
// 创建一个可迭代的 ReadableStream with ChunkBuffer integration
|
|
122
154
|
return {
|
|
123
155
|
[Symbol.asyncIterator]: async function* () {
|
|
124
156
|
try {
|
|
125
157
|
for await (const chunk of agentStream) {
|
|
126
158
|
let data;
|
|
159
|
+
let chunkContent = "";
|
|
160
|
+
|
|
127
161
|
if (chunk[0] === "updates") {
|
|
128
162
|
const update = chunk[1];
|
|
129
163
|
const values = Object.values(update);
|
|
@@ -138,31 +172,35 @@ export async function agent_stream({
|
|
|
138
172
|
}
|
|
139
173
|
|
|
140
174
|
if ((chunk?.[1] as any)?.__interrupt__) {
|
|
141
|
-
data = chunk?.[1]?.[0]?.toDict();
|
|
175
|
+
//data = chunk?.[1]?.[0]?.toDict();
|
|
142
176
|
// 原有的中断消息处理
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// content: (chunk?.[1] as any)?.__interrupt__[0].value,
|
|
148
|
-
// type: "action",
|
|
149
|
-
// },
|
|
150
|
-
// ],
|
|
151
|
-
// };
|
|
177
|
+
data = {
|
|
178
|
+
type: "ai",
|
|
179
|
+
data: { content: (chunk?.[1] as any)?.__interrupt__[0].value },
|
|
180
|
+
};
|
|
152
181
|
}
|
|
153
182
|
|
|
154
183
|
if (data) {
|
|
155
184
|
//console.log(data);
|
|
185
|
+
await chunkBuffer.addChunk(thread_id, data);
|
|
186
|
+
|
|
156
187
|
yield data;
|
|
157
188
|
}
|
|
158
189
|
}
|
|
190
|
+
|
|
191
|
+
// Mark thread as completed when streaming finishes successfully
|
|
192
|
+
await chunkBuffer.completeThread(thread_id);
|
|
159
193
|
} catch (error) {
|
|
160
194
|
console.error("Stream error:", error);
|
|
195
|
+
// Mark thread as aborted on error
|
|
196
|
+
await chunkBuffer.abortThread(thread_id);
|
|
161
197
|
throw error;
|
|
162
198
|
}
|
|
163
199
|
},
|
|
164
200
|
};
|
|
165
201
|
} catch (error) {
|
|
202
|
+
// Mark thread as aborted on initialization error
|
|
203
|
+
await chunkBuffer.abortThread(thread_id);
|
|
166
204
|
throw error;
|
|
167
205
|
}
|
|
168
206
|
}
|
|
@@ -241,3 +279,79 @@ export async function draw_graph(assistant_id: string) {
|
|
|
241
279
|
const image = await drawableGraph.drawMermaid();
|
|
242
280
|
return image;
|
|
243
281
|
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Get accumulated content for a thread from ChunkBuffer
|
|
285
|
+
*/
|
|
286
|
+
export async function get_accumulated_content(thread_id: string) {
|
|
287
|
+
const chunkBuffer = getOrCreateChunkBuffer();
|
|
288
|
+
return await chunkBuffer.getAccumulatedContent(thread_id);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Get thread status and metadata from ChunkBuffer
|
|
293
|
+
*/
|
|
294
|
+
export async function get_thread_status(thread_id: string) {
|
|
295
|
+
const chunkBuffer = getOrCreateChunkBuffer();
|
|
296
|
+
const threadBuffer = await chunkBuffer.getThreadBuffer(thread_id);
|
|
297
|
+
return {
|
|
298
|
+
exists: !!threadBuffer,
|
|
299
|
+
status: threadBuffer?.status,
|
|
300
|
+
chunkCount: threadBuffer?.chunks.length || 0,
|
|
301
|
+
createdAt: threadBuffer?.createdAt,
|
|
302
|
+
updatedAt: threadBuffer?.updatedAt,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Get all active streaming threads
|
|
308
|
+
*/
|
|
309
|
+
export async function get_active_threads() {
|
|
310
|
+
const chunkBuffer = getOrCreateChunkBuffer();
|
|
311
|
+
return await chunkBuffer.getActiveThreads();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Get ChunkBuffer statistics
|
|
316
|
+
*/
|
|
317
|
+
export async function get_buffer_stats() {
|
|
318
|
+
const chunkBuffer = getOrCreateChunkBuffer();
|
|
319
|
+
return chunkBuffer.getStats();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Clear specific thread buffer
|
|
324
|
+
*/
|
|
325
|
+
export async function clear_thread_buffer(thread_id: string) {
|
|
326
|
+
const chunkBuffer = getOrCreateChunkBuffer();
|
|
327
|
+
await chunkBuffer.clearThread(thread_id);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Resume streaming from a known position
|
|
332
|
+
* Creates an async iterator that yields new chunks as they arrive
|
|
333
|
+
*
|
|
334
|
+
* @param thread_id - Thread identifier
|
|
335
|
+
* @param message_id - Message identifier (usually run_id)
|
|
336
|
+
* @param known_content - Content already received (used to find resume position)
|
|
337
|
+
* @param poll_interval - Polling interval in milliseconds (default: 100ms)
|
|
338
|
+
*/
|
|
339
|
+
export async function* resume_stream({
|
|
340
|
+
thread_id,
|
|
341
|
+
message_id,
|
|
342
|
+
known_content,
|
|
343
|
+
poll_interval = 100,
|
|
344
|
+
}: {
|
|
345
|
+
thread_id: string;
|
|
346
|
+
message_id: string;
|
|
347
|
+
known_content: string;
|
|
348
|
+
poll_interval?: number;
|
|
349
|
+
}) {
|
|
350
|
+
const chunkBuffer = getOrCreateChunkBuffer();
|
|
351
|
+
|
|
352
|
+
return chunkBuffer.getNewChunksSinceContent(
|
|
353
|
+
thread_id,
|
|
354
|
+
message_id,
|
|
355
|
+
known_content
|
|
356
|
+
);
|
|
357
|
+
}
|
package/src/swagger.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { FastifyInstance } from "fastify";
|
|
2
|
+
import swagger from "@fastify/swagger";
|
|
3
|
+
import swaggerUi from "@fastify/swagger-ui";
|
|
4
|
+
// Example usage:
|
|
5
|
+
// configureSwagger(app)
|
|
6
|
+
// configureSwagger(app, { openapi: { info: { version: "2.0.0" } } })
|
|
7
|
+
// configureSwagger(app, undefined, { routePrefix: "/docs" })
|
|
8
|
+
|
|
9
|
+
// Default swagger configuration
|
|
10
|
+
export const defaultSwaggerConfig = {
|
|
11
|
+
openapi: {
|
|
12
|
+
openapi: "3.0.0",
|
|
13
|
+
info: {
|
|
14
|
+
title: "Axiom Lattice Gateway API",
|
|
15
|
+
description: "API Gateway for LangGraph agent-based applications",
|
|
16
|
+
version: "1.0.0",
|
|
17
|
+
contact: {
|
|
18
|
+
name: "Axiom Lattice Team",
|
|
19
|
+
email: "support@axiom-lattice.com",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
servers: [
|
|
23
|
+
{
|
|
24
|
+
url: "http://localhost:4001",
|
|
25
|
+
description: "Development environment",
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
components: {
|
|
29
|
+
securitySchemes: {
|
|
30
|
+
bearerAuth: {
|
|
31
|
+
type: "http" as const,
|
|
32
|
+
scheme: "bearer" as const,
|
|
33
|
+
bearerFormat: "JWT",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
security: [
|
|
38
|
+
{
|
|
39
|
+
bearerAuth: [],
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
tags: [
|
|
43
|
+
{ name: "Runs", description: "Agent run management" },
|
|
44
|
+
{ name: "Memory", description: "Agent memory management" },
|
|
45
|
+
{ name: "Graph", description: "Agent graph visualization" },
|
|
46
|
+
{ name: "Health", description: "System health checks" },
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Default swagger UI configuration
|
|
52
|
+
export const defaultSwaggerUiConfig = {
|
|
53
|
+
routePrefix: "/api-docs",
|
|
54
|
+
uiConfig: {
|
|
55
|
+
docExpansion: "full" as const,
|
|
56
|
+
deepLinking: false,
|
|
57
|
+
},
|
|
58
|
+
staticCSP: true,
|
|
59
|
+
transformStaticCSP: (header: string) => header,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Configure Swagger with optional custom configuration
|
|
63
|
+
export const configureSwagger = async (
|
|
64
|
+
app: FastifyInstance,
|
|
65
|
+
customSwaggerConfig?: Partial<typeof defaultSwaggerConfig>,
|
|
66
|
+
customSwaggerUiConfig?: Partial<typeof defaultSwaggerUiConfig>
|
|
67
|
+
) => {
|
|
68
|
+
// Merge default config with custom config
|
|
69
|
+
const swaggerConfig = { ...defaultSwaggerConfig, ...customSwaggerConfig };
|
|
70
|
+
const swaggerUiConfig = {
|
|
71
|
+
...defaultSwaggerUiConfig,
|
|
72
|
+
...customSwaggerUiConfig,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
await app.register(swagger, swaggerConfig);
|
|
76
|
+
await app.register(swaggerUi, swaggerUiConfig);
|
|
77
|
+
};
|