@axiom-lattice/gateway 1.0.11 → 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.
@@ -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 { getAgentClient, getAgentLattice } from "@axiom-lattice/core";
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,
@@ -40,7 +61,7 @@ export async function agent_invoke({
40
61
  run_id?: string;
41
62
  command?: CommandParams<any>;
42
63
  }) {
43
- const runnable_agent = getAgentLattice(assistant_id)?.client;
64
+ const runnable_agent = getAgentClient(assistant_id);
44
65
  const { files, message, ...rest } = input;
45
66
  const humanMessage = new HumanMessage(message || "");
46
67
  humanMessage.additional_kwargs = { files: files };
@@ -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
- return result;
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
- // data = {
144
- // messages: [
145
- // {
146
- // role: "ai",
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
+ };