@axiom-lattice/gateway 2.1.37 → 2.1.39
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 +8 -8
- package/CHANGELOG.md +19 -0
- package/dist/index.js +365 -145
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +412 -191
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/agent_service.test.ts +238 -0
- package/src/__tests__/workspace.test.ts +253 -0
- package/src/controllers/assistant.ts +48 -25
- package/src/controllers/database-configs.ts +17 -9
- package/src/controllers/mcp-configs.ts +9 -1
- package/src/controllers/memory.ts +3 -0
- package/src/controllers/metrics-configs.ts +81 -19
- package/src/controllers/models.ts +39 -0
- package/src/controllers/run.ts +36 -6
- package/src/controllers/sandbox.ts +6 -4
- package/src/controllers/schedules.ts +20 -0
- package/src/controllers/skills.ts +34 -11
- package/src/controllers/threads.ts +32 -12
- package/src/controllers/tools.ts +2 -273
- package/src/controllers/workspace.ts +153 -25
- package/src/index.ts +10 -0
- package/src/services/agent_service.ts +29 -10
- package/src/services/agent_task_consumer.ts +2 -2
- package/src/services/sandbox_service.ts +10 -9
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
CreateAssistantRequest,
|
|
7
7
|
} from "@axiom-lattice/protocols";
|
|
8
8
|
import { randomUUID } from "crypto";
|
|
9
|
-
import { AgentConfig,
|
|
9
|
+
import { AgentConfig, agentLatticeManager, eventBus } from "@axiom-lattice/core";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Assistant Controller
|
|
@@ -16,12 +16,28 @@ import { AgentConfig, getAllAgentConfigs, eventBus } from "@axiom-lattice/core";
|
|
|
16
16
|
* CUD operations only work on stored assistants
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Get tenant ID from authenticated user context
|
|
21
|
+
* Falls back to request headers for backward compatibility
|
|
22
|
+
*/
|
|
23
|
+
function getTenantId(request: FastifyRequest): string {
|
|
24
|
+
// First try to get from authenticated user context
|
|
25
|
+
const userTenantId = (request as any).user?.tenantId;
|
|
26
|
+
if (userTenantId) {
|
|
27
|
+
return userTenantId;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Fallback to request headers for backward compatibility
|
|
31
|
+
return (request.headers["x-tenant-id"] as string) || "default";
|
|
32
|
+
}
|
|
33
|
+
|
|
19
34
|
/**
|
|
20
35
|
* Convert AgentConfig to Assistant format
|
|
21
36
|
*/
|
|
22
37
|
function convertAgentConfigToAssistant(config: AgentConfig): Assistant {
|
|
23
38
|
return {
|
|
24
39
|
id: config.key,
|
|
40
|
+
tenantId: "default", // Code-configured agents belong to default tenant
|
|
25
41
|
name: config.name,
|
|
26
42
|
description: config.description,
|
|
27
43
|
graphDefinition: config, // Store the full config as graphDefinition
|
|
@@ -68,16 +84,18 @@ export async function getAssistantList(
|
|
|
68
84
|
request: FastifyRequest,
|
|
69
85
|
reply: FastifyReply
|
|
70
86
|
): Promise<AssistantListResponse> {
|
|
71
|
-
|
|
72
|
-
|
|
87
|
+
const tenantId = getTenantId(request);
|
|
88
|
+
|
|
89
|
+
// Get code-configured agents for the tenant
|
|
90
|
+
const agentConfigs = await agentLatticeManager.getAllAgentConfigsByTenant(tenantId);
|
|
73
91
|
const codeConfiguredAssistants = agentConfigs.map(
|
|
74
92
|
convertAgentConfigToAssistant
|
|
75
93
|
);
|
|
76
94
|
|
|
77
|
-
// Get stored assistants
|
|
95
|
+
// Get stored assistants for the tenant
|
|
78
96
|
const storeLattice = getStoreLattice("default", "assistant");
|
|
79
97
|
const assistantStore = storeLattice.store;
|
|
80
|
-
const storedAssistants = await assistantStore.getAllAssistants();
|
|
98
|
+
const storedAssistants = await assistantStore.getAllAssistants(tenantId);
|
|
81
99
|
|
|
82
100
|
// Merge both sources, stored assistants take precedence if ID conflicts
|
|
83
101
|
const assistantMap = new Map<string, Assistant>();
|
|
@@ -113,16 +131,16 @@ export async function getAssistant(
|
|
|
113
131
|
reply: FastifyReply
|
|
114
132
|
): Promise<AssistantResponse> {
|
|
115
133
|
const { id } = request.params;
|
|
134
|
+
const tenantId = getTenantId(request);
|
|
116
135
|
|
|
117
136
|
// First check stored assistants
|
|
118
137
|
const storeLattice = getStoreLattice("default", "assistant");
|
|
119
138
|
const assistantStore = storeLattice.store;
|
|
120
|
-
let assistant = await assistantStore.getAssistantById(id);
|
|
139
|
+
let assistant = await assistantStore.getAssistantById(tenantId, id);
|
|
121
140
|
|
|
122
|
-
// If not found in store, check code-configured agents
|
|
141
|
+
// If not found in store, check code-configured agents for the tenant
|
|
123
142
|
if (!assistant) {
|
|
124
|
-
const
|
|
125
|
-
const agentConfig = agentConfigs.find((config) => config.key === id);
|
|
143
|
+
const agentConfig = await agentLatticeManager.getAgentConfigWithTenant(tenantId, id);
|
|
126
144
|
if (agentConfig) {
|
|
127
145
|
assistant = convertAgentConfigToAssistant(agentConfig);
|
|
128
146
|
}
|
|
@@ -146,6 +164,7 @@ export async function getAssistant(
|
|
|
146
164
|
* Upsert assistant - create if not exists in store, update if exists
|
|
147
165
|
*/
|
|
148
166
|
async function upsertAssistant(
|
|
167
|
+
tenantId: string,
|
|
149
168
|
id: string,
|
|
150
169
|
data: CreateAssistantRequest | AssistantUpdateBody,
|
|
151
170
|
reply: FastifyReply,
|
|
@@ -154,18 +173,18 @@ async function upsertAssistant(
|
|
|
154
173
|
const storeLattice = getStoreLattice("default", "assistant");
|
|
155
174
|
const assistantStore = storeLattice.store;
|
|
156
175
|
|
|
157
|
-
const exists = await assistantStore.hasAssistant(id);
|
|
176
|
+
const exists = await assistantStore.hasAssistant(tenantId, id);
|
|
158
177
|
|
|
159
178
|
let assistant: Assistant | null;
|
|
160
179
|
if (exists) {
|
|
161
|
-
assistant = await assistantStore.updateAssistant(id, data);
|
|
180
|
+
assistant = await assistantStore.updateAssistant(tenantId, id, data);
|
|
162
181
|
if (!assistant) {
|
|
163
182
|
return reply.status(500).send({
|
|
164
183
|
success: false,
|
|
165
184
|
message: "Failed to update assistant",
|
|
166
185
|
});
|
|
167
186
|
}
|
|
168
|
-
eventBus.publish("assistant:updated", { id: assistant.id, name: assistant.name });
|
|
187
|
+
eventBus.publish("assistant:updated", { id: assistant.id, name: assistant.name, tenantId });
|
|
169
188
|
return {
|
|
170
189
|
success: true,
|
|
171
190
|
message: "Updated assistant",
|
|
@@ -183,8 +202,8 @@ async function upsertAssistant(
|
|
|
183
202
|
}
|
|
184
203
|
}
|
|
185
204
|
|
|
186
|
-
assistant = await assistantStore.createAssistant(id, data as CreateAssistantRequest);
|
|
187
|
-
eventBus.publish("assistant:created", { id: assistant.id, name: assistant.name });
|
|
205
|
+
assistant = await assistantStore.createAssistant(tenantId, id, data as CreateAssistantRequest);
|
|
206
|
+
eventBus.publish("assistant:created", { id: assistant.id, name: assistant.name, tenantId });
|
|
188
207
|
return reply.status(201).send({
|
|
189
208
|
success: true,
|
|
190
209
|
message: "Created assistant",
|
|
@@ -199,6 +218,7 @@ export async function createAssistant(
|
|
|
199
218
|
request: FastifyRequest<{ Body: CreateAssistantRequest & { id?: string } }>,
|
|
200
219
|
reply: FastifyReply
|
|
201
220
|
): Promise<AssistantResponse> {
|
|
221
|
+
const tenantId = getTenantId(request);
|
|
202
222
|
const data = request.body;
|
|
203
223
|
|
|
204
224
|
if (!data.name || !data.graphDefinition) {
|
|
@@ -209,7 +229,7 @@ export async function createAssistant(
|
|
|
209
229
|
}
|
|
210
230
|
|
|
211
231
|
const id = data.id ?? randomUUID();
|
|
212
|
-
return upsertAssistant(id, data, reply, true);
|
|
232
|
+
return upsertAssistant(tenantId, id, data, reply, true);
|
|
213
233
|
}
|
|
214
234
|
|
|
215
235
|
/**
|
|
@@ -222,10 +242,11 @@ export async function updateAssistant(
|
|
|
222
242
|
}>,
|
|
223
243
|
reply: FastifyReply
|
|
224
244
|
): Promise<AssistantResponse> {
|
|
245
|
+
const tenantId = getTenantId(request);
|
|
225
246
|
const { id } = request.params;
|
|
226
247
|
const updates = request.body;
|
|
227
248
|
|
|
228
|
-
return upsertAssistant(id, updates, reply, false);
|
|
249
|
+
return upsertAssistant(tenantId, id, updates, reply, false);
|
|
229
250
|
}
|
|
230
251
|
|
|
231
252
|
/**
|
|
@@ -237,31 +258,32 @@ export async function deleteAssistant(
|
|
|
237
258
|
request: FastifyRequest<{ Params: { id: string } }>,
|
|
238
259
|
reply: FastifyReply
|
|
239
260
|
): Promise<{ success: boolean; message: string }> {
|
|
261
|
+
const tenantId = getTenantId(request);
|
|
240
262
|
const { id } = request.params;
|
|
241
263
|
|
|
242
264
|
const storeLattice = getStoreLattice("default", "assistant");
|
|
243
265
|
const assistantStore = storeLattice.store;
|
|
244
266
|
|
|
245
|
-
const
|
|
246
|
-
const isCodeConfigured =
|
|
267
|
+
const agentConfig = await agentLatticeManager.getAgentConfigWithTenant(tenantId, id);
|
|
268
|
+
const isCodeConfigured = !!agentConfig;
|
|
247
269
|
|
|
248
270
|
if (isCodeConfigured) {
|
|
249
|
-
const exists = await assistantStore.hasAssistant(id);
|
|
271
|
+
const exists = await assistantStore.hasAssistant(tenantId, id);
|
|
250
272
|
if (!exists) {
|
|
251
273
|
return reply.status(404).send({
|
|
252
274
|
success: false,
|
|
253
275
|
message: "Assistant not found (code-configured assistants cannot be deleted from code, only from store)",
|
|
254
276
|
});
|
|
255
277
|
}
|
|
256
|
-
await assistantStore.deleteAssistant(id);
|
|
257
|
-
eventBus.publish("assistant:deleted", { id });
|
|
278
|
+
await assistantStore.deleteAssistant(tenantId, id);
|
|
279
|
+
eventBus.publish("assistant:deleted", { id, tenantId });
|
|
258
280
|
return {
|
|
259
281
|
success: true,
|
|
260
282
|
message: "Deleted assistant from store (code-configured registration remains)",
|
|
261
283
|
};
|
|
262
284
|
}
|
|
263
285
|
|
|
264
|
-
const exists = await assistantStore.hasAssistant(id);
|
|
286
|
+
const exists = await assistantStore.hasAssistant(tenantId, id);
|
|
265
287
|
if (!exists) {
|
|
266
288
|
return reply.status(404).send({
|
|
267
289
|
success: false,
|
|
@@ -269,7 +291,7 @@ export async function deleteAssistant(
|
|
|
269
291
|
});
|
|
270
292
|
}
|
|
271
293
|
|
|
272
|
-
const deleted = await assistantStore.deleteAssistant(id);
|
|
294
|
+
const deleted = await assistantStore.deleteAssistant(tenantId, id);
|
|
273
295
|
|
|
274
296
|
if (!deleted) {
|
|
275
297
|
return reply.status(500).send({
|
|
@@ -278,7 +300,7 @@ export async function deleteAssistant(
|
|
|
278
300
|
});
|
|
279
301
|
}
|
|
280
302
|
|
|
281
|
-
eventBus.publish("assistant:deleted", { id });
|
|
303
|
+
eventBus.publish("assistant:deleted", { id, tenantId });
|
|
282
304
|
|
|
283
305
|
return {
|
|
284
306
|
success: true,
|
|
@@ -297,9 +319,10 @@ export const getAgentGraph = async (
|
|
|
297
319
|
) => {
|
|
298
320
|
try {
|
|
299
321
|
const { assistantId } = request.params;
|
|
322
|
+
const tenant_id = getTenantId(request);
|
|
300
323
|
|
|
301
324
|
// Call drawing service to get image data
|
|
302
|
-
const imageData = await draw_graph(assistantId);
|
|
325
|
+
const imageData = await draw_graph(assistantId, tenant_id);
|
|
303
326
|
|
|
304
327
|
// Set response header and return image data
|
|
305
328
|
reply.header("Content-Type", "application/json").send({
|
|
@@ -18,9 +18,17 @@ import { randomUUID } from "crypto";
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* Get tenant ID from
|
|
21
|
+
* Get tenant ID from authenticated user context
|
|
22
|
+
* Falls back to request headers for backward compatibility
|
|
22
23
|
*/
|
|
23
24
|
function getTenantId(request: FastifyRequest): string {
|
|
25
|
+
// First try to get from authenticated user context
|
|
26
|
+
const userTenantId = (request as any).user?.tenantId;
|
|
27
|
+
if (userTenantId) {
|
|
28
|
+
return userTenantId;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Fallback to request headers for backward compatibility
|
|
24
32
|
return (request.headers["x-tenant-id"] as string) || "default";
|
|
25
33
|
}
|
|
26
34
|
|
|
@@ -165,7 +173,7 @@ export async function createDatabaseConfig(
|
|
|
165
173
|
|
|
166
174
|
// Auto-register to SqlDatabaseManager
|
|
167
175
|
try {
|
|
168
|
-
sqlDatabaseManager.registerDatabase(config.key, config.config);
|
|
176
|
+
sqlDatabaseManager.registerDatabase(tenantId, config.key, config.config);
|
|
169
177
|
} catch (error) {
|
|
170
178
|
console.warn("Failed to auto-register database:", error);
|
|
171
179
|
}
|
|
@@ -221,7 +229,7 @@ export async function updateDatabaseConfig(
|
|
|
221
229
|
// Re-register to SqlDatabaseManager if config changed
|
|
222
230
|
if (updates.config) {
|
|
223
231
|
try {
|
|
224
|
-
sqlDatabaseManager.registerDatabase(updated.key, updated.config);
|
|
232
|
+
sqlDatabaseManager.registerDatabase(tenantId, updated.key, updated.config);
|
|
225
233
|
} catch (error) {
|
|
226
234
|
console.warn("Failed to re-register database:", error);
|
|
227
235
|
}
|
|
@@ -290,8 +298,8 @@ export async function deleteDatabaseConfig(
|
|
|
290
298
|
|
|
291
299
|
// Remove from SqlDatabaseManager
|
|
292
300
|
try {
|
|
293
|
-
if (sqlDatabaseManager.hasDatabase(configKey)) {
|
|
294
|
-
await sqlDatabaseManager.removeDatabase(configKey);
|
|
301
|
+
if (sqlDatabaseManager.hasDatabase(tenantId, configKey)) {
|
|
302
|
+
await sqlDatabaseManager.removeDatabase(tenantId, configKey);
|
|
295
303
|
}
|
|
296
304
|
} catch (error) {
|
|
297
305
|
console.warn("Failed to remove from SqlDatabaseManager:", error);
|
|
@@ -335,10 +343,10 @@ export async function testDatabaseConnection(
|
|
|
335
343
|
|
|
336
344
|
// Register temporarily for testing
|
|
337
345
|
const testKey = `__test_${key}_${Date.now()}`;
|
|
338
|
-
sqlDatabaseManager.registerDatabase(testKey, config.config);
|
|
346
|
+
sqlDatabaseManager.registerDatabase(tenantId, testKey, config.config);
|
|
339
347
|
|
|
340
348
|
const startTime = Date.now();
|
|
341
|
-
const db = sqlDatabaseManager.getDatabase(testKey);
|
|
349
|
+
const db = sqlDatabaseManager.getDatabase(tenantId, testKey);
|
|
342
350
|
|
|
343
351
|
try {
|
|
344
352
|
// Try to connect and list tables
|
|
@@ -351,7 +359,7 @@ export async function testDatabaseConnection(
|
|
|
351
359
|
await db.disconnect();
|
|
352
360
|
|
|
353
361
|
// Cleanup
|
|
354
|
-
await sqlDatabaseManager.removeDatabase(testKey);
|
|
362
|
+
await sqlDatabaseManager.removeDatabase(tenantId, testKey);
|
|
355
363
|
|
|
356
364
|
return {
|
|
357
365
|
success: true,
|
|
@@ -365,7 +373,7 @@ export async function testDatabaseConnection(
|
|
|
365
373
|
// Cleanup on error
|
|
366
374
|
try {
|
|
367
375
|
await db.disconnect();
|
|
368
|
-
await sqlDatabaseManager.removeDatabase(testKey);
|
|
376
|
+
await sqlDatabaseManager.removeDatabase(tenantId, testKey);
|
|
369
377
|
} catch {}
|
|
370
378
|
|
|
371
379
|
return {
|
|
@@ -20,9 +20,17 @@ import type {
|
|
|
20
20
|
import { randomUUID } from "crypto";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Get tenant ID from
|
|
23
|
+
* Get tenant ID from authenticated user context
|
|
24
|
+
* Falls back to request headers for backward compatibility
|
|
24
25
|
*/
|
|
25
26
|
function getTenantId(request: FastifyRequest): string {
|
|
27
|
+
// First try to get from authenticated user context
|
|
28
|
+
const userTenantId = (request as any).user?.tenantId;
|
|
29
|
+
if (userTenantId) {
|
|
30
|
+
return userTenantId;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Fallback to request headers for backward compatibility
|
|
26
34
|
return (request.headers["x-tenant-id"] as string) || "default";
|
|
27
35
|
}
|
|
28
36
|
|
|
@@ -155,9 +155,12 @@ export const getAgentState = async (
|
|
|
155
155
|
return;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
const tenant_id = request.headers["x-tenant-id"] as string;
|
|
159
|
+
|
|
158
160
|
const result = await agent_state({
|
|
159
161
|
assistant_id: assistantId,
|
|
160
162
|
thread_id: thread_id,
|
|
163
|
+
tenant_id: tenant_id,
|
|
161
164
|
});
|
|
162
165
|
|
|
163
166
|
if (!result) {
|
|
@@ -22,9 +22,17 @@ import { randomUUID } from "crypto";
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* Get tenant ID from
|
|
25
|
+
* Get tenant ID from authenticated user context
|
|
26
|
+
* Falls back to request headers for backward compatibility
|
|
26
27
|
*/
|
|
27
28
|
function getTenantId(request: FastifyRequest): string {
|
|
29
|
+
// First try to get from authenticated user context
|
|
30
|
+
const userTenantId = (request as any).user?.tenantId;
|
|
31
|
+
if (userTenantId) {
|
|
32
|
+
return userTenantId;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Fallback to request headers for backward compatibility
|
|
28
36
|
return (request.headers["x-tenant-id"] as string) || "default";
|
|
29
37
|
}
|
|
30
38
|
|
|
@@ -124,8 +132,18 @@ interface DatasourceMetricsResponse {
|
|
|
124
132
|
searchKeywords: string[];
|
|
125
133
|
registered: boolean;
|
|
126
134
|
}>;
|
|
135
|
+
tables?: Array<{
|
|
136
|
+
tableName: string;
|
|
137
|
+
displayName: string;
|
|
138
|
+
docType: string;
|
|
139
|
+
docTypeEn: string;
|
|
140
|
+
mainTable?: string;
|
|
141
|
+
lineTable?: string;
|
|
142
|
+
columnCount: number;
|
|
143
|
+
shortDesc: string;
|
|
144
|
+
}>;
|
|
127
145
|
};
|
|
128
|
-
|
|
146
|
+
metricsDetails: Array<{
|
|
129
147
|
datasourceId: number;
|
|
130
148
|
metricName: string;
|
|
131
149
|
displayName: string;
|
|
@@ -176,6 +194,22 @@ interface DatasourceMetricsResponse {
|
|
|
176
194
|
humanReadableExplanation: string;
|
|
177
195
|
};
|
|
178
196
|
}>;
|
|
197
|
+
tablesDetails?: Array<{
|
|
198
|
+
tableName: string;
|
|
199
|
+
docType: string;
|
|
200
|
+
docTypeEn: string;
|
|
201
|
+
objTypeCode?: number;
|
|
202
|
+
mainTable?: string;
|
|
203
|
+
lineTable?: string;
|
|
204
|
+
selectSql: string;
|
|
205
|
+
columns: Array<{
|
|
206
|
+
name: string;
|
|
207
|
+
label: string;
|
|
208
|
+
description?: string;
|
|
209
|
+
type?: string;
|
|
210
|
+
example?: string;
|
|
211
|
+
} | null>;
|
|
212
|
+
}>;
|
|
179
213
|
};
|
|
180
214
|
}
|
|
181
215
|
|
|
@@ -326,7 +360,7 @@ export async function createMetricsServerConfig(
|
|
|
326
360
|
|
|
327
361
|
// Auto-register to MetricsServerManager
|
|
328
362
|
try {
|
|
329
|
-
metricsServerManager.registerServer(config.key, config.config);
|
|
363
|
+
metricsServerManager.registerServer(tenantId, config.key, config.config);
|
|
330
364
|
} catch (error) {
|
|
331
365
|
console.warn("Failed to auto-register metrics server:", error);
|
|
332
366
|
}
|
|
@@ -395,7 +429,7 @@ export async function updateMetricsServerConfig(
|
|
|
395
429
|
|
|
396
430
|
if (updates.config) {
|
|
397
431
|
try {
|
|
398
|
-
metricsServerManager.registerServer(updated.key, updated.config);
|
|
432
|
+
metricsServerManager.registerServer(tenantId, updated.key, updated.config);
|
|
399
433
|
} catch (error) {
|
|
400
434
|
console.warn("Failed to re-register metrics server:", error);
|
|
401
435
|
}
|
|
@@ -460,8 +494,8 @@ export async function deleteMetricsServerConfig(
|
|
|
460
494
|
|
|
461
495
|
// Remove from MetricsServerManager
|
|
462
496
|
try {
|
|
463
|
-
if (metricsServerManager.hasServer(configKey)) {
|
|
464
|
-
metricsServerManager.removeServer(configKey);
|
|
497
|
+
if (metricsServerManager.hasServer(tenantId, configKey)) {
|
|
498
|
+
metricsServerManager.removeServer(tenantId, configKey);
|
|
465
499
|
}
|
|
466
500
|
} catch (error) {
|
|
467
501
|
console.warn("Failed to remove from MetricsServerManager:", error);
|
|
@@ -505,14 +539,14 @@ export async function testMetricsServerConnection(
|
|
|
505
539
|
|
|
506
540
|
// Register temporarily for testing
|
|
507
541
|
const testKey = `__test_${key}_${Date.now()}`;
|
|
508
|
-
metricsServerManager.registerServer(testKey, config.config);
|
|
542
|
+
metricsServerManager.registerServer(tenantId, testKey, config.config);
|
|
509
543
|
|
|
510
544
|
try {
|
|
511
|
-
const client = metricsServerManager.getClient(testKey);
|
|
545
|
+
const client = metricsServerManager.getClient(tenantId, testKey);
|
|
512
546
|
const result = await client.testConnection();
|
|
513
547
|
|
|
514
548
|
// Cleanup
|
|
515
|
-
metricsServerManager.removeServer(testKey);
|
|
549
|
+
metricsServerManager.removeServer(tenantId, testKey);
|
|
516
550
|
|
|
517
551
|
return {
|
|
518
552
|
success: true,
|
|
@@ -522,7 +556,7 @@ export async function testMetricsServerConnection(
|
|
|
522
556
|
} catch (error) {
|
|
523
557
|
// Cleanup on error
|
|
524
558
|
try {
|
|
525
|
-
metricsServerManager.removeServer(testKey);
|
|
559
|
+
metricsServerManager.removeServer(tenantId, testKey);
|
|
526
560
|
} catch {}
|
|
527
561
|
|
|
528
562
|
return {
|
|
@@ -571,11 +605,11 @@ export async function listAvailableMetrics(
|
|
|
571
605
|
}
|
|
572
606
|
|
|
573
607
|
// Ensure server is registered
|
|
574
|
-
if (!metricsServerManager.hasServer(key)) {
|
|
575
|
-
metricsServerManager.registerServer(key, config.config);
|
|
608
|
+
if (!metricsServerManager.hasServer(tenantId, key)) {
|
|
609
|
+
metricsServerManager.registerServer(tenantId, key, config.config);
|
|
576
610
|
}
|
|
577
611
|
|
|
578
|
-
const client = metricsServerManager.getClient(key);
|
|
612
|
+
const client = metricsServerManager.getClient(tenantId, key);
|
|
579
613
|
const metrics = await client.listMetrics();
|
|
580
614
|
|
|
581
615
|
return {
|
|
@@ -637,11 +671,11 @@ export async function queryMetricsData(
|
|
|
637
671
|
}
|
|
638
672
|
|
|
639
673
|
// Ensure server is registered
|
|
640
|
-
if (!metricsServerManager.hasServer(key)) {
|
|
641
|
-
metricsServerManager.registerServer(key, config.config);
|
|
674
|
+
if (!metricsServerManager.hasServer(tenantId, key)) {
|
|
675
|
+
metricsServerManager.registerServer(tenantId, key, config.config);
|
|
642
676
|
}
|
|
643
677
|
|
|
644
|
-
const client = metricsServerManager.getClient(key);
|
|
678
|
+
const client = metricsServerManager.getClient(tenantId, key);
|
|
645
679
|
const result = await client.queryMetricData(metricName, {
|
|
646
680
|
startTime,
|
|
647
681
|
endTime,
|
|
@@ -822,14 +856,42 @@ export async function querySemanticMetrics(
|
|
|
822
856
|
const client = new SemanticMetricsClient(semanticConfig);
|
|
823
857
|
const result = await client.semanticQuery(body);
|
|
824
858
|
|
|
859
|
+
// Transform SemanticMetricsQueryResponse to response format
|
|
860
|
+
// The response contains results array with metric data
|
|
861
|
+
const allDataPoints: Array<{
|
|
862
|
+
timestamp?: number;
|
|
863
|
+
value: number;
|
|
864
|
+
metricName?: string;
|
|
865
|
+
labels?: Record<string, string>;
|
|
866
|
+
}> = [];
|
|
867
|
+
const metricNames: string[] = [];
|
|
868
|
+
|
|
869
|
+
for (const metricResult of result.results) {
|
|
870
|
+
metricNames.push(metricResult.metricName);
|
|
871
|
+
for (const row of metricResult.rows) {
|
|
872
|
+
allDataPoints.push({
|
|
873
|
+
timestamp: row.timestamp ? new Date(row.timestamp as string).getTime() : undefined,
|
|
874
|
+
value: typeof row.value === 'number' ? row.value : 0,
|
|
875
|
+
metricName: metricResult.metricName,
|
|
876
|
+
labels: Object.fromEntries(
|
|
877
|
+
Object.entries(row).filter(([k]) => k !== 'value' && k !== 'timestamp')
|
|
878
|
+
.map(([k, v]) => [k, String(v)])
|
|
879
|
+
),
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
825
884
|
return {
|
|
826
885
|
success: true,
|
|
827
886
|
message: "Semantic query executed successfully",
|
|
828
887
|
data: {
|
|
829
888
|
datasourceId: result.datasourceId,
|
|
830
|
-
metrics:
|
|
831
|
-
dataPoints:
|
|
832
|
-
metadata:
|
|
889
|
+
metrics: metricNames,
|
|
890
|
+
dataPoints: allDataPoints,
|
|
891
|
+
metadata: {
|
|
892
|
+
rowCount: allDataPoints.length,
|
|
893
|
+
queryTimeMs: result.totalExecutionTimeMs,
|
|
894
|
+
},
|
|
833
895
|
},
|
|
834
896
|
};
|
|
835
897
|
} catch (error) {
|
|
@@ -5,8 +5,41 @@ import type { LLMConfig } from "@axiom-lattice/protocols";
|
|
|
5
5
|
/**
|
|
6
6
|
* Models Controller
|
|
7
7
|
* Handles model lattice registration and management
|
|
8
|
+
* Models are tenant-isolated using key prefix: {tenantId}:{modelKey}
|
|
8
9
|
*/
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Get tenant ID from request headers or user context
|
|
13
|
+
*/
|
|
14
|
+
function getTenantId(request: FastifyRequest): string {
|
|
15
|
+
// First try to get from authenticated user context
|
|
16
|
+
const userTenantId = (request as any).user?.tenantId;
|
|
17
|
+
if (userTenantId) {
|
|
18
|
+
return userTenantId;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Fallback to request headers for backward compatibility
|
|
22
|
+
return (request.headers["x-tenant-id"] as string) || "default";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get tenant-scoped model key
|
|
27
|
+
*/
|
|
28
|
+
function getTenantModelKey(tenantId: string, modelKey: string): string {
|
|
29
|
+
return `${tenantId}:${modelKey}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Extract model key from tenant-scoped key
|
|
34
|
+
*/
|
|
35
|
+
function extractModelKey(tenantId: string, tenantModelKey: string): string {
|
|
36
|
+
const prefix = `${tenantId}:`;
|
|
37
|
+
if (tenantModelKey.startsWith(prefix)) {
|
|
38
|
+
return tenantModelKey.substring(prefix.length);
|
|
39
|
+
}
|
|
40
|
+
return tenantModelKey;
|
|
41
|
+
}
|
|
42
|
+
|
|
10
43
|
interface ModelConfig {
|
|
11
44
|
key: string;
|
|
12
45
|
model: string;
|
|
@@ -26,6 +59,12 @@ interface UpdateModelsRequest {
|
|
|
26
59
|
};
|
|
27
60
|
}
|
|
28
61
|
|
|
62
|
+
interface UpdateModelsRequest {
|
|
63
|
+
Body: {
|
|
64
|
+
models: ModelConfig[];
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
29
68
|
/**
|
|
30
69
|
* Get all registered models
|
|
31
70
|
*/
|
package/src/controllers/run.ts
CHANGED
|
@@ -42,6 +42,16 @@ export const createRun = async (
|
|
|
42
42
|
|
|
43
43
|
// 如果请求streaming,则agent_stream
|
|
44
44
|
if (streaming) {
|
|
45
|
+
// 先检查agent是否存在(在hijack之前)
|
|
46
|
+
const agentExists = await agentService.checkAgentExists(tenant_id, assistant_id);
|
|
47
|
+
if (!agentExists) {
|
|
48
|
+
reply.status(404).send({
|
|
49
|
+
success: false,
|
|
50
|
+
error: `Agent ${assistant_id} not found for tenant ${tenant_id}`,
|
|
51
|
+
});
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
45
55
|
// 开始运行
|
|
46
56
|
const stream = await agentService.agent_stream({
|
|
47
57
|
assistant_id: assistant_id,
|
|
@@ -67,11 +77,24 @@ export const createRun = async (
|
|
|
67
77
|
});
|
|
68
78
|
|
|
69
79
|
try {
|
|
80
|
+
let chunkCount = 0;
|
|
70
81
|
for await (const chunk of stream) {
|
|
71
|
-
|
|
82
|
+
chunkCount++;
|
|
83
|
+
const success = reply.raw.write(`data: ${JSON.stringify(chunk)}\n\n`);
|
|
84
|
+
if (!success) {
|
|
85
|
+
await new Promise(resolve => reply.raw.once('drain', resolve));
|
|
86
|
+
}
|
|
72
87
|
}
|
|
73
|
-
} catch (error) {
|
|
74
|
-
|
|
88
|
+
} catch (error: any) {
|
|
89
|
+
// Send error as SSE event before closing (following MessageChunk format)
|
|
90
|
+
const errorEvent = {
|
|
91
|
+
type: 'error',
|
|
92
|
+
data: {
|
|
93
|
+
id: v4(),
|
|
94
|
+
content: error.message || 'Stream processing error'
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
reply.raw.write(`data: ${JSON.stringify(errorEvent)}\n\n`);
|
|
75
98
|
} finally {
|
|
76
99
|
reply.raw.end();
|
|
77
100
|
}
|
|
@@ -138,11 +161,18 @@ export const resumeStream = async (
|
|
|
138
161
|
|
|
139
162
|
// Stream the chunks to the client
|
|
140
163
|
for await (const chunk of stream) {
|
|
141
|
-
// console.log("resume stream raw.write", chunk);
|
|
142
164
|
reply.raw.write(`data: ${JSON.stringify(chunk)}\n\n`);
|
|
143
165
|
}
|
|
144
|
-
} catch (error) {
|
|
145
|
-
|
|
166
|
+
} catch (error: any) {
|
|
167
|
+
// Send error as SSE event before closing (following MessageChunk format)
|
|
168
|
+
const errorEvent = {
|
|
169
|
+
type: 'error',
|
|
170
|
+
data: {
|
|
171
|
+
id: v4(),
|
|
172
|
+
content: error.message || 'Resume stream processing error'
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
reply.raw.write(`data: ${JSON.stringify(errorEvent)}\n\n`);
|
|
146
176
|
} finally {
|
|
147
177
|
reply.raw.end();
|
|
148
178
|
}
|
|
@@ -53,8 +53,9 @@ export function registerSandboxProxyRoutes(app: FastifyInstance): void {
|
|
|
53
53
|
async (request, reply) => {
|
|
54
54
|
console.log("[Sandbox Upload] Route matched:", request.url);
|
|
55
55
|
const { assistantId, threadId } = request.params;
|
|
56
|
+
const tenantId = (request.headers["x-tenant-id"] as string) || "default";
|
|
56
57
|
|
|
57
|
-
const isolatedLevel = sandboxService.getFilesystemIsolatedLevel(assistantId);
|
|
58
|
+
const isolatedLevel = sandboxService.getFilesystemIsolatedLevel(tenantId, assistantId);
|
|
58
59
|
if (!isolatedLevel) {
|
|
59
60
|
return reply.status(500).send({ error: "Assistant sandbox config not found" });
|
|
60
61
|
}
|
|
@@ -65,7 +66,7 @@ export function registerSandboxProxyRoutes(app: FastifyInstance): void {
|
|
|
65
66
|
isolatedLevel
|
|
66
67
|
);
|
|
67
68
|
|
|
68
|
-
const sandboxManager = getSandBoxManager(
|
|
69
|
+
const sandboxManager = getSandBoxManager()
|
|
69
70
|
const sandbox = await sandboxManager.createSandbox(sandboxName)
|
|
70
71
|
|
|
71
72
|
try {
|
|
@@ -116,12 +117,13 @@ export function registerSandboxProxyRoutes(app: FastifyInstance): void {
|
|
|
116
117
|
async (request, reply) => {
|
|
117
118
|
const { assistantId, threadId } = request.params;
|
|
118
119
|
const { path: filePath } = request.query;
|
|
120
|
+
const tenantId = (request.headers["x-tenant-id"] as string) || "default";
|
|
119
121
|
|
|
120
122
|
if (!filePath || typeof filePath !== "string") {
|
|
121
123
|
return reply.status(400).send({ error: "Query parameter 'path' is required" });
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
const isolatedLevel = sandboxService.getFilesystemIsolatedLevel(assistantId);
|
|
126
|
+
const isolatedLevel = sandboxService.getFilesystemIsolatedLevel(tenantId, assistantId);
|
|
125
127
|
if (!isolatedLevel) {
|
|
126
128
|
return reply.status(500).send({ error: "Assistant filesystem isolated level not found" });
|
|
127
129
|
}
|
|
@@ -132,7 +134,7 @@ export function registerSandboxProxyRoutes(app: FastifyInstance): void {
|
|
|
132
134
|
isolatedLevel
|
|
133
135
|
);
|
|
134
136
|
|
|
135
|
-
const sandboxManager = getSandBoxManager(
|
|
137
|
+
const sandboxManager = getSandBoxManager();
|
|
136
138
|
const sandbox = await sandboxManager.createSandbox(sandboxName);
|
|
137
139
|
|
|
138
140
|
try {
|