@axiom-lattice/gateway 2.1.11 → 2.1.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,189 @@
1
+ import { FastifyRequest, FastifyReply } from "fastify";
2
+ import { threadStore } from "../stores/thread_store";
3
+ import { Thread, CreateThreadRequest } from "../types";
4
+ import { randomUUID } from "crypto";
5
+
6
+ /**
7
+ * Thread Controller
8
+ * Handles thread-related CRUD operations for a specific assistant
9
+ * All operations are scoped to an assistant ID
10
+ */
11
+
12
+ /**
13
+ * Thread list response interface
14
+ */
15
+ interface ThreadListResponse {
16
+ success: boolean;
17
+ message: string;
18
+ data: {
19
+ records: Thread[];
20
+ total: number;
21
+ };
22
+ }
23
+
24
+ /**
25
+ * Thread response interface
26
+ */
27
+ interface ThreadResponse {
28
+ success: boolean;
29
+ message: string;
30
+ data?: Thread;
31
+ }
32
+
33
+ /**
34
+ * Thread update request body interface
35
+ */
36
+ interface ThreadUpdateBody {
37
+ metadata?: Record<string, any>;
38
+ }
39
+
40
+ /**
41
+ * Get list of all threads for a specific assistant
42
+ */
43
+ export async function getThreadList(
44
+ request: FastifyRequest<{ Params: { assistantId: string } }>,
45
+ reply: FastifyReply
46
+ ): Promise<ThreadListResponse> {
47
+ const { assistantId } = request.params;
48
+
49
+ const threads = threadStore.getThreadsByAssistantId(assistantId);
50
+
51
+ return {
52
+ success: true,
53
+ message: "Successfully retrieved thread list",
54
+ data: {
55
+ records: threads,
56
+ total: threads.length,
57
+ },
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Get a single thread by ID for a specific assistant
63
+ */
64
+ export async function getThread(
65
+ request: FastifyRequest<{
66
+ Params: { assistantId: string; threadId: string };
67
+ }>,
68
+ reply: FastifyReply
69
+ ): Promise<ThreadResponse> {
70
+ const { assistantId, threadId } = request.params;
71
+
72
+ const thread = threadStore.getThreadById(assistantId, threadId);
73
+
74
+ if (!thread) {
75
+ return reply.status(404).send({
76
+ success: false,
77
+ message: "Thread not found",
78
+ });
79
+ }
80
+
81
+ return {
82
+ success: true,
83
+ message: "Successfully retrieved thread",
84
+ data: thread,
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Create a new thread for an assistant
90
+ */
91
+ export async function createThread(
92
+ request: FastifyRequest<{
93
+ Params: { assistantId: string };
94
+ Body: CreateThreadRequest;
95
+ }>,
96
+ reply: FastifyReply
97
+ ): Promise<ThreadResponse> {
98
+ const { assistantId } = request.params;
99
+ const data = request.body;
100
+
101
+ // Generate thread ID if not provided
102
+ const threadId = randomUUID();
103
+
104
+ // Create thread
105
+ const newThread = threadStore.createThread(assistantId, threadId, data);
106
+
107
+ return reply.status(201).send({
108
+ success: true,
109
+ message: "Successfully created thread",
110
+ data: newThread,
111
+ });
112
+ }
113
+
114
+ /**
115
+ * Update an existing thread by ID
116
+ */
117
+ export async function updateThread(
118
+ request: FastifyRequest<{
119
+ Params: { assistantId: string; threadId: string };
120
+ Body: ThreadUpdateBody;
121
+ }>,
122
+ reply: FastifyReply
123
+ ): Promise<ThreadResponse> {
124
+ const { assistantId, threadId } = request.params;
125
+ const updates = request.body;
126
+
127
+ // Check if thread exists
128
+ if (!threadStore.hasThread(assistantId, threadId)) {
129
+ return reply.status(404).send({
130
+ success: false,
131
+ message: "Thread not found",
132
+ });
133
+ }
134
+
135
+ // Update thread
136
+ const updatedThread = threadStore.updateThread(
137
+ assistantId,
138
+ threadId,
139
+ updates
140
+ );
141
+
142
+ if (!updatedThread) {
143
+ return reply.status(500).send({
144
+ success: false,
145
+ message: "Failed to update thread",
146
+ });
147
+ }
148
+
149
+ return {
150
+ success: true,
151
+ message: "Successfully updated thread",
152
+ data: updatedThread,
153
+ };
154
+ }
155
+
156
+ /**
157
+ * Delete a thread by ID
158
+ */
159
+ export async function deleteThread(
160
+ request: FastifyRequest<{
161
+ Params: { assistantId: string; threadId: string };
162
+ }>,
163
+ reply: FastifyReply
164
+ ): Promise<{ success: boolean; message: string }> {
165
+ const { assistantId, threadId } = request.params;
166
+
167
+ // Check if thread exists
168
+ if (!threadStore.hasThread(assistantId, threadId)) {
169
+ return reply.status(404).send({
170
+ success: false,
171
+ message: "Thread not found",
172
+ });
173
+ }
174
+
175
+ // Delete the thread
176
+ const deleted = threadStore.deleteThread(assistantId, threadId);
177
+
178
+ if (!deleted) {
179
+ return reply.status(500).send({
180
+ success: false,
181
+ message: "Failed to delete thread",
182
+ });
183
+ }
184
+
185
+ return {
186
+ success: true,
187
+ message: "Successfully deleted thread",
188
+ };
189
+ }
@@ -1,9 +1,12 @@
1
1
  import { FastifyInstance } from "fastify";
2
- //import * as assistantController from "../controllers/assistant";
2
+ import * as assistantController from "../controllers/assistant";
3
3
  import * as runController from "../controllers/run";
4
4
  import * as memoryController from "../controllers/memory";
5
5
  import * as graphController from "../controllers/assistant";
6
6
  import * as agentTaskController from "../controllers/agent_task";
7
+ import * as threadsController from "../controllers/threads";
8
+ import * as configController from "../controllers/config";
9
+ import * as modelsController from "../controllers/models";
7
10
  import {
8
11
  createRunSchema,
9
12
  getAllMemoryItemsSchema,
@@ -15,6 +18,8 @@ import {
15
18
  getAgentGraphSchema,
16
19
  resumeStreamSchema,
17
20
  triggerAgentTaskSchema,
21
+ updateConfigSchema,
22
+ getConfigSchema,
18
23
  } from "../schemas";
19
24
 
20
25
  export const registerLatticeRoutes = (app: FastifyInstance): void => {
@@ -90,6 +95,26 @@ export const registerLatticeRoutes = (app: FastifyInstance): void => {
90
95
  memoryController.clearMemory
91
96
  );
92
97
 
98
+ // Assistant CRUD routes
99
+ app.get("/api/assistants", assistantController.getAssistantList);
100
+
101
+ app.get<{
102
+ Params: { id: string };
103
+ }>("/api/assistants/:id", assistantController.getAssistant);
104
+
105
+ app.post<{
106
+ Body: any;
107
+ }>("/api/assistants", assistantController.createAssistant);
108
+
109
+ app.put<{
110
+ Params: { id: string };
111
+ Body: any;
112
+ }>("/api/assistants/:id", assistantController.updateAssistant);
113
+
114
+ app.delete<{
115
+ Params: { id: string };
116
+ }>("/api/assistants/:id", assistantController.deleteAssistant);
117
+
93
118
  // 图表路由
94
119
  app.get<{
95
120
  Params: { assistantId: string };
@@ -107,4 +132,58 @@ export const registerLatticeRoutes = (app: FastifyInstance): void => {
107
132
  { schema: triggerAgentTaskSchema },
108
133
  agentTaskController.triggerAgentTask
109
134
  );
135
+
136
+ // Thread CRUD routes for a specific assistant
137
+ app.get<{
138
+ Params: { assistantId: string };
139
+ }>("/api/assistants/:assistantId/threads", threadsController.getThreadList);
140
+
141
+ app.get<{
142
+ Params: { assistantId: string; threadId: string };
143
+ }>(
144
+ "/api/assistants/:assistantId/threads/:threadId",
145
+ threadsController.getThread
146
+ );
147
+
148
+ app.post<{
149
+ Params: { assistantId: string };
150
+ Body: any;
151
+ }>("/api/assistants/:assistantId/threads", threadsController.createThread);
152
+
153
+ app.put<{
154
+ Params: { assistantId: string; threadId: string };
155
+ Body: any;
156
+ }>(
157
+ "/api/assistants/:assistantId/threads/:threadId",
158
+ threadsController.updateThread
159
+ );
160
+
161
+ app.delete<{
162
+ Params: { assistantId: string; threadId: string };
163
+ }>(
164
+ "/api/assistants/:assistantId/threads/:threadId",
165
+ threadsController.deleteThread
166
+ );
167
+
168
+ // Configuration routes
169
+ app.get(
170
+ "/api/config",
171
+ { schema: getConfigSchema },
172
+ configController.getConfig
173
+ );
174
+
175
+ app.put<{
176
+ Body: { config: Record<string, any> };
177
+ }>(
178
+ "/api/config",
179
+ { schema: updateConfigSchema },
180
+ configController.updateConfig
181
+ );
182
+
183
+ // Models routes
184
+ app.get("/api/models", modelsController.getModels);
185
+
186
+ app.put<{
187
+ Body: { models: any[] };
188
+ }>("/api/models", modelsController.updateModels);
110
189
  };
@@ -245,3 +245,77 @@ export const triggerAgentTaskSchema: FastifySchema = {
245
245
  },
246
246
  },
247
247
  };
248
+
249
+ // Configuration Schemas
250
+ export const updateConfigSchema: FastifySchema = {
251
+ description: "Update gateway configuration",
252
+ tags: ["Configuration"],
253
+ summary: "Update Configuration",
254
+ body: {
255
+ type: "object",
256
+ properties: {
257
+ config: {
258
+ type: "object",
259
+ description: "Configuration object to update",
260
+ properties: {
261
+ port: { type: "number", description: "Server port" },
262
+ queueServiceType: {
263
+ type: "string",
264
+ enum: ["memory", "redis"],
265
+ description: "Queue service type",
266
+ },
267
+ redisUrl: { type: "string", description: "Redis URL" },
268
+ redisPassword: { type: "string", description: "Redis password" },
269
+ queueName: { type: "string", description: "Queue name" },
270
+ },
271
+ },
272
+ },
273
+ required: ["config"],
274
+ },
275
+ response: {
276
+ 200: {
277
+ type: "object",
278
+ properties: {
279
+ success: { type: "boolean" },
280
+ message: { type: "string" },
281
+ data: { type: "object" },
282
+ },
283
+ },
284
+ 400: {
285
+ type: "object",
286
+ properties: {
287
+ success: { type: "boolean" },
288
+ error: { type: "string" },
289
+ },
290
+ },
291
+ 500: {
292
+ type: "object",
293
+ properties: {
294
+ success: { type: "boolean" },
295
+ error: { type: "string" },
296
+ },
297
+ },
298
+ },
299
+ };
300
+
301
+ export const getConfigSchema: FastifySchema = {
302
+ description: "Get current gateway configuration",
303
+ tags: ["Configuration"],
304
+ summary: "Get Configuration",
305
+ response: {
306
+ 200: {
307
+ type: "object",
308
+ properties: {
309
+ success: { type: "boolean" },
310
+ data: { type: "object" },
311
+ },
312
+ },
313
+ 500: {
314
+ type: "object",
315
+ properties: {
316
+ success: { type: "boolean" },
317
+ error: { type: "string" },
318
+ },
319
+ },
320
+ },
321
+ };
@@ -1,15 +1,36 @@
1
- import { createClient } from "@supabase/supabase-js";
2
- import { config } from "../config";
1
+ import { createClient, SupabaseClient } from "@supabase/supabase-js";
3
2
  // import { Database } from "@/types/database.types";
4
3
  // import { Database as PgmqPublic } from "@/types/pgmq_public.types";
5
- // 创建Supabase客户端
6
- const supabaseClient = createClient(config.supabase.url, config.supabase.key);
7
4
 
8
- export default supabaseClient;
5
+ /**
6
+ * Get Supabase client
7
+ * This function reads from environment variables directly
8
+ */
9
+ const getSupabaseClient = (): SupabaseClient => {
10
+ const url = process.env.SUPABASE_URL;
11
+ const key = process.env.SUPABASE_KEY;
12
+ if (!url || !key) {
13
+ throw new Error("Supabase URL and Key must be configured");
14
+ }
15
+ return createClient(url, key);
16
+ };
17
+
18
+ // Default export - creates client using current config
19
+ // Note: Since config uses getters, this will read the latest config values
20
+ export default getSupabaseClient();
9
21
 
22
+ /**
23
+ * Create a Supabase client with tenant headers
24
+ * This function reads from environment variables directly
25
+ */
10
26
  export const createSupabaseClient = (headers: Record<string, string>) => {
11
27
  const currentTenantId = headers["x-tenant-id"];
12
- return createClient(config.supabase.url, config.supabase.key, {
28
+ const url = process.env.SUPABASE_URL;
29
+ const key = process.env.SUPABASE_KEY;
30
+ if (!url || !key) {
31
+ throw new Error("Supabase URL and Key must be configured");
32
+ }
33
+ return createClient(url, key, {
13
34
  global: {
14
35
  fetch: async (input, init) => {
15
36
  const headers = new Headers(init?.headers);
@@ -20,7 +41,20 @@ export const createSupabaseClient = (headers: Record<string, string>) => {
20
41
  });
21
42
  };
22
43
 
23
- export const supabaseQueueClient = createClient(
24
- config.supabase.url,
25
- config.supabase.key
26
- );
44
+ /**
45
+ * Get Supabase queue client
46
+ * This function reads from environment variables directly
47
+ */
48
+ export const getSupabaseQueueClient = (): SupabaseClient => {
49
+ const url = process.env.SUPABASE_URL;
50
+ const key = process.env.SUPABASE_KEY;
51
+ if (!url || !key) {
52
+ throw new Error("Supabase URL and Key must be configured");
53
+ }
54
+ return createClient(url, key);
55
+ };
56
+
57
+ // For backward compatibility, export a client that reads config dynamically
58
+ // Note: This creates a new client each time, but Supabase clients are lightweight
59
+ // and this ensures we always use the latest config
60
+ export const supabaseQueueClient = getSupabaseQueueClient();
@@ -0,0 +1,82 @@
1
+ import { Assistant, CreateAssistantRequest } from "../types";
2
+
3
+ /**
4
+ * In-memory store for assistants
5
+ * Provides CRUD operations for assistant data
6
+ */
7
+ class AssistantStore {
8
+ private assistants: Map<string, Assistant> = new Map();
9
+
10
+ /**
11
+ * Get all assistants
12
+ */
13
+ getAllAssistants(): Assistant[] {
14
+ return Array.from(this.assistants.values());
15
+ }
16
+
17
+ /**
18
+ * Get assistant by ID
19
+ */
20
+ getAssistantById(id: string): Assistant | undefined {
21
+ return this.assistants.get(id);
22
+ }
23
+
24
+ /**
25
+ * Create a new assistant
26
+ */
27
+ createAssistant(
28
+ id: string,
29
+ data: CreateAssistantRequest
30
+ ): Assistant {
31
+ const now = new Date();
32
+ const assistant: Assistant = {
33
+ id,
34
+ name: data.name,
35
+ description: data.description,
36
+ graphDefinition: data.graphDefinition,
37
+ createdAt: now,
38
+ updatedAt: now,
39
+ };
40
+ this.assistants.set(id, assistant);
41
+ return assistant;
42
+ }
43
+
44
+ /**
45
+ * Update an existing assistant
46
+ */
47
+ updateAssistant(
48
+ id: string,
49
+ updates: Partial<CreateAssistantRequest>
50
+ ): Assistant | null {
51
+ const existing = this.assistants.get(id);
52
+ if (!existing) {
53
+ return null;
54
+ }
55
+
56
+ const updated: Assistant = {
57
+ ...existing,
58
+ ...updates,
59
+ updatedAt: new Date(),
60
+ };
61
+ this.assistants.set(id, updated);
62
+ return updated;
63
+ }
64
+
65
+ /**
66
+ * Delete an assistant by ID
67
+ */
68
+ deleteAssistant(id: string): boolean {
69
+ return this.assistants.delete(id);
70
+ }
71
+
72
+ /**
73
+ * Check if assistant exists
74
+ */
75
+ hasAssistant(id: string): boolean {
76
+ return this.assistants.has(id);
77
+ }
78
+ }
79
+
80
+ // Export singleton instance
81
+ export const assistantStore = new AssistantStore();
82
+
@@ -0,0 +1,115 @@
1
+ import { Thread, CreateThreadRequest } from "../types";
2
+
3
+ /**
4
+ * In-memory store for threads
5
+ * Provides CRUD operations for thread data
6
+ * Threads are organized by assistant ID
7
+ */
8
+ class ThreadStore {
9
+ // Map<assistantId, Map<threadId, Thread>>
10
+ private threads: Map<string, Map<string, Thread>> = new Map();
11
+
12
+ /**
13
+ * Get all threads for a specific assistant
14
+ */
15
+ getThreadsByAssistantId(assistantId: string): Thread[] {
16
+ const assistantThreads = this.threads.get(assistantId);
17
+ if (!assistantThreads) {
18
+ return [];
19
+ }
20
+ return Array.from(assistantThreads.values());
21
+ }
22
+
23
+ /**
24
+ * Get a thread by ID for a specific assistant
25
+ */
26
+ getThreadById(assistantId: string, threadId: string): Thread | undefined {
27
+ const assistantThreads = this.threads.get(assistantId);
28
+ if (!assistantThreads) {
29
+ return undefined;
30
+ }
31
+ return assistantThreads.get(threadId);
32
+ }
33
+
34
+ /**
35
+ * Create a new thread for an assistant
36
+ */
37
+ createThread(
38
+ assistantId: string,
39
+ threadId: string,
40
+ data: CreateThreadRequest
41
+ ): Thread {
42
+ const now = new Date();
43
+ const thread: Thread = {
44
+ id: threadId,
45
+ assistantId,
46
+ metadata: data.metadata || {},
47
+ createdAt: now,
48
+ updatedAt: now,
49
+ };
50
+
51
+ // Initialize assistant's thread map if it doesn't exist
52
+ if (!this.threads.has(assistantId)) {
53
+ this.threads.set(assistantId, new Map());
54
+ }
55
+
56
+ const assistantThreads = this.threads.get(assistantId)!;
57
+ assistantThreads.set(threadId, thread);
58
+ return thread;
59
+ }
60
+
61
+ /**
62
+ * Update an existing thread
63
+ */
64
+ updateThread(
65
+ assistantId: string,
66
+ threadId: string,
67
+ updates: Partial<CreateThreadRequest>
68
+ ): Thread | null {
69
+ const assistantThreads = this.threads.get(assistantId);
70
+ if (!assistantThreads) {
71
+ return null;
72
+ }
73
+
74
+ const existing = assistantThreads.get(threadId);
75
+ if (!existing) {
76
+ return null;
77
+ }
78
+
79
+ const updated: Thread = {
80
+ ...existing,
81
+ metadata: {
82
+ ...existing.metadata,
83
+ ...(updates.metadata || {}),
84
+ },
85
+ updatedAt: new Date(),
86
+ };
87
+ assistantThreads.set(threadId, updated);
88
+ return updated;
89
+ }
90
+
91
+ /**
92
+ * Delete a thread by ID
93
+ */
94
+ deleteThread(assistantId: string, threadId: string): boolean {
95
+ const assistantThreads = this.threads.get(assistantId);
96
+ if (!assistantThreads) {
97
+ return false;
98
+ }
99
+ return assistantThreads.delete(threadId);
100
+ }
101
+
102
+ /**
103
+ * Check if thread exists
104
+ */
105
+ hasThread(assistantId: string, threadId: string): boolean {
106
+ const assistantThreads = this.threads.get(assistantId);
107
+ if (!assistantThreads) {
108
+ return false;
109
+ }
110
+ return assistantThreads.has(threadId);
111
+ }
112
+ }
113
+
114
+ // Export singleton instance
115
+ export const threadStore = new ThreadStore();
@@ -121,3 +121,17 @@ export interface ServiceResponse<T = any> {
121
121
  data?: T;
122
122
  error?: string;
123
123
  }
124
+
125
+ // Thread type
126
+ export interface Thread {
127
+ id: string;
128
+ assistantId: string;
129
+ metadata?: Record<string, any>;
130
+ createdAt: Date;
131
+ updatedAt: Date;
132
+ }
133
+
134
+ // Create thread request type
135
+ export interface CreateThreadRequest {
136
+ metadata?: Record<string, any>;
137
+ }