@axiom-lattice/gateway 2.1.63 → 2.1.64
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 +12 -12
- package/CHANGELOG.md +8 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +521 -162
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +449 -89
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/controllers/workflow-tracking.ts +385 -0
- package/src/index.ts +18 -0
- package/src/routes/index.ts +46 -0
- package/src/services/agent_task_consumer.ts +40 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axiom-lattice/gateway",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.64",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "dist/index.mjs",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"pg": "^8.11.0",
|
|
40
40
|
"redis": "^5.0.1",
|
|
41
41
|
"uuid": "^9.0.1",
|
|
42
|
-
"@axiom-lattice/core": "2.1.
|
|
43
|
-
"@axiom-lattice/pg-stores": "1.0.
|
|
42
|
+
"@axiom-lattice/core": "2.1.58",
|
|
43
|
+
"@axiom-lattice/pg-stores": "1.0.48",
|
|
44
44
|
"@axiom-lattice/protocols": "2.1.30",
|
|
45
45
|
"@axiom-lattice/queue-redis": "1.0.29"
|
|
46
46
|
},
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
+
import { getStoreLattice, agentInstanceManager } from "@axiom-lattice/core";
|
|
3
|
+
import type { WorkflowTrackingStore, WorkflowRun, RunStep } from "@axiom-lattice/protocols";
|
|
4
|
+
|
|
5
|
+
function getTenantId(request: FastifyRequest): string {
|
|
6
|
+
const userTenantId = (request as any).user?.tenantId;
|
|
7
|
+
if (userTenantId) return userTenantId;
|
|
8
|
+
return (request.headers["x-tenant-id"] as string) || "default";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getTrackingStore(): WorkflowTrackingStore | null {
|
|
12
|
+
try {
|
|
13
|
+
const storeLattice = getStoreLattice("default", "workflowTracking");
|
|
14
|
+
return storeLattice.store;
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ApiResponse<T = any> {
|
|
21
|
+
success: boolean;
|
|
22
|
+
message: string;
|
|
23
|
+
data?: T;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function getDefinitionsFromAssistants(tenantId: string): Promise<Array<{
|
|
27
|
+
assistantId: string;
|
|
28
|
+
assistantName: string;
|
|
29
|
+
topologyEdges: { from: string; to: string; purpose: string }[];
|
|
30
|
+
totalEdges: number;
|
|
31
|
+
}>> {
|
|
32
|
+
try {
|
|
33
|
+
const storeLattice = getStoreLattice("default", "assistant");
|
|
34
|
+
const assistantStore = storeLattice.store;
|
|
35
|
+
const assistants = await assistantStore.getAllAssistants(tenantId);
|
|
36
|
+
|
|
37
|
+
const results: Array<{
|
|
38
|
+
assistantId: string;
|
|
39
|
+
assistantName: string;
|
|
40
|
+
topologyEdges: { from: string; to: string; purpose: string }[];
|
|
41
|
+
totalEdges: number;
|
|
42
|
+
}> = [];
|
|
43
|
+
|
|
44
|
+
for (const a of assistants) {
|
|
45
|
+
const def = a.graphDefinition;
|
|
46
|
+
if (!def || def.type !== "processing") continue;
|
|
47
|
+
if (!def.middleware) continue;
|
|
48
|
+
for (const mw of def.middleware) {
|
|
49
|
+
if (mw.type === "topology" && mw.enabled && mw.config?.edges?.length > 0) {
|
|
50
|
+
results.push({
|
|
51
|
+
assistantId: a.id,
|
|
52
|
+
assistantName: a.name,
|
|
53
|
+
topologyEdges: mw.config.edges,
|
|
54
|
+
totalEdges: mw.config.edges.length,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return results;
|
|
60
|
+
} catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function getAllWorkflowDefinitions(
|
|
66
|
+
request: FastifyRequest,
|
|
67
|
+
reply: FastifyReply
|
|
68
|
+
): Promise<ApiResponse<{ records: any[] }>> {
|
|
69
|
+
const tenantId = getTenantId(request);
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
// Get definitions from assistant configs (always available, even before first run)
|
|
73
|
+
const configDefs = await getDefinitionsFromAssistants(tenantId);
|
|
74
|
+
|
|
75
|
+
if (configDefs.length === 0) {
|
|
76
|
+
return {
|
|
77
|
+
success: true,
|
|
78
|
+
message: "No workflow definitions found",
|
|
79
|
+
data: { records: [] },
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Merge in run-derived data (lastRunAt, runCount) if tracking store exists
|
|
84
|
+
const runMap = new Map<string, { lastRunAt?: string; runCount: number }>();
|
|
85
|
+
try {
|
|
86
|
+
const store = getTrackingStore();
|
|
87
|
+
if (store) {
|
|
88
|
+
const runs = await store.getWorkflowRunsByTenantId(tenantId);
|
|
89
|
+
for (const r of runs) {
|
|
90
|
+
const entry = runMap.get(r.assistantId) || { runCount: 0 };
|
|
91
|
+
entry.runCount++;
|
|
92
|
+
if (!entry.lastRunAt || new Date(r.startedAt) > new Date(entry.lastRunAt)) {
|
|
93
|
+
entry.lastRunAt = r.startedAt instanceof Date ? r.startedAt.toISOString() : String(r.startedAt);
|
|
94
|
+
}
|
|
95
|
+
runMap.set(r.assistantId, entry);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
// tracking store unavailable, definitions from config only
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const definitions = configDefs.map((d) => {
|
|
103
|
+
const runInfo = runMap.get(d.assistantId);
|
|
104
|
+
return {
|
|
105
|
+
...d,
|
|
106
|
+
lastRunAt: runInfo?.lastRunAt || null,
|
|
107
|
+
runCount: runInfo?.runCount || 0,
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
success: true,
|
|
113
|
+
message: "Successfully retrieved workflow definitions",
|
|
114
|
+
data: { records: definitions },
|
|
115
|
+
};
|
|
116
|
+
} catch (error) {
|
|
117
|
+
request.log.error(error, "Failed to get workflow definitions");
|
|
118
|
+
return reply.status(500).send({ success: false, message: "Failed to retrieve workflow definitions" });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function getAllWorkflowRuns(
|
|
123
|
+
request: FastifyRequest<{ Querystring: { assistantId?: string; status?: string } }>,
|
|
124
|
+
reply: FastifyReply
|
|
125
|
+
): Promise<ApiResponse<{ records: WorkflowRun[]; total: number }>> {
|
|
126
|
+
const tenantId = getTenantId(request);
|
|
127
|
+
const { assistantId, status } = request.query;
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const store = getTrackingStore();
|
|
131
|
+
if (!store) {
|
|
132
|
+
return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let runs: WorkflowRun[];
|
|
136
|
+
if (assistantId) {
|
|
137
|
+
runs = await store.getWorkflowRunsByAssistantId(tenantId, assistantId);
|
|
138
|
+
} else {
|
|
139
|
+
runs = await store.getWorkflowRunsByTenantId(tenantId);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (status) {
|
|
143
|
+
runs = runs.filter(r => r.status === status);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
message: "Successfully retrieved workflow runs",
|
|
149
|
+
data: { records: runs, total: runs.length },
|
|
150
|
+
};
|
|
151
|
+
} catch (error) {
|
|
152
|
+
request.log.error(error, "Failed to get workflow runs");
|
|
153
|
+
return reply.status(500).send({ success: false, message: "Failed to retrieve workflow runs" });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// GET /api/workflows/inbox
|
|
158
|
+
export async function getInboxItems(
|
|
159
|
+
request: FastifyRequest,
|
|
160
|
+
reply: FastifyReply
|
|
161
|
+
): Promise<ApiResponse<{ records: any[] }>> {
|
|
162
|
+
const tenantId = getTenantId(request);
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const store = getTrackingStore();
|
|
166
|
+
if (!store) {
|
|
167
|
+
return { success: true, message: "No tracking store configured", data: { records: [] } };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Resolve assistant names
|
|
171
|
+
const nameMap: Record<string, string> = {};
|
|
172
|
+
try {
|
|
173
|
+
const asStoreLattice = getStoreLattice("default", "assistant");
|
|
174
|
+
const assistantStore = asStoreLattice.store;
|
|
175
|
+
const assistants = await assistantStore.getAllAssistants(tenantId);
|
|
176
|
+
for (const a of assistants) {
|
|
177
|
+
nameMap[a.id] = a.name;
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
// names unavailable, will fall back to ID
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const runs = await store.getWorkflowRunsByTenantId(tenantId);
|
|
184
|
+
const runningRuns = runs.filter(r => r.status === "running");
|
|
185
|
+
if (runningRuns.length === 0) {
|
|
186
|
+
return { success: true, message: "No running workflows", data: { records: [] } };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const inboxItems: any[] = [];
|
|
190
|
+
for (const r of runningRuns) {
|
|
191
|
+
try {
|
|
192
|
+
const agent = agentInstanceManager.getAgent({
|
|
193
|
+
assistant_id: r.assistantId,
|
|
194
|
+
thread_id: r.threadId,
|
|
195
|
+
tenant_id: r.tenantId,
|
|
196
|
+
});
|
|
197
|
+
const runStatus = await agent.getRunStatus();
|
|
198
|
+
|
|
199
|
+
if (runStatus !== "interrupted") continue;
|
|
200
|
+
|
|
201
|
+
const state = await agent.getCurrentState();
|
|
202
|
+
const interrupts = state.tasks
|
|
203
|
+
?.flatMap((t: any) => t.interrupts || []) || [];
|
|
204
|
+
|
|
205
|
+
for (const i of interrupts) {
|
|
206
|
+
inboxItems.push({
|
|
207
|
+
runId: r.id,
|
|
208
|
+
assistantId: r.assistantId,
|
|
209
|
+
assistantName: nameMap[r.assistantId] || r.assistantId,
|
|
210
|
+
threadId: r.threadId,
|
|
211
|
+
tenantId: r.tenantId,
|
|
212
|
+
interruptId: i.id,
|
|
213
|
+
interruptValue: i.value,
|
|
214
|
+
startedAt: r.startedAt,
|
|
215
|
+
totalEdges: r.totalEdges,
|
|
216
|
+
completedEdges: r.completedEdges,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
} catch {
|
|
220
|
+
// agent unavailable, skip
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
inboxItems.sort((a, b) =>
|
|
225
|
+
new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime()
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
success: true,
|
|
230
|
+
message: "Successfully retrieved inbox items",
|
|
231
|
+
data: { records: inboxItems },
|
|
232
|
+
};
|
|
233
|
+
} catch (error) {
|
|
234
|
+
request.log.error(error, "Failed to get inbox items");
|
|
235
|
+
return reply.status(500).send({ success: false, message: "Failed to retrieve inbox items" });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export async function getWorkflowDefinitions(
|
|
240
|
+
request: FastifyRequest<{ Params: { assistantId: string } }>,
|
|
241
|
+
reply: FastifyReply
|
|
242
|
+
): Promise<ApiResponse<{ records: any[] }>> {
|
|
243
|
+
const { assistantId } = request.params;
|
|
244
|
+
const tenantId = getTenantId(request);
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
const store = getTrackingStore();
|
|
248
|
+
if (!store) {
|
|
249
|
+
return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const runs = await store.getWorkflowRunsByAssistantId(tenantId, assistantId);
|
|
253
|
+
const seen = new Set<string>();
|
|
254
|
+
const definitions = runs
|
|
255
|
+
.filter(r => {
|
|
256
|
+
const key = JSON.stringify(r.topologyEdges);
|
|
257
|
+
if (seen.has(key)) return false;
|
|
258
|
+
seen.add(key);
|
|
259
|
+
return true;
|
|
260
|
+
})
|
|
261
|
+
.map(r => ({
|
|
262
|
+
assistantId: r.assistantId,
|
|
263
|
+
topologyEdges: r.topologyEdges,
|
|
264
|
+
totalEdges: r.totalEdges,
|
|
265
|
+
lastRunAt: r.startedAt,
|
|
266
|
+
}));
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
success: true,
|
|
270
|
+
message: "Successfully retrieved workflow definitions",
|
|
271
|
+
data: { records: definitions },
|
|
272
|
+
};
|
|
273
|
+
} catch (error) {
|
|
274
|
+
request.log.error(error, "Failed to get workflow definitions");
|
|
275
|
+
return reply.status(500).send({ success: false, message: "Failed to retrieve workflow definitions" });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export async function getWorkflowRuns(
|
|
280
|
+
request: FastifyRequest<{ Params: { assistantId: string; threadId: string } }>,
|
|
281
|
+
reply: FastifyReply
|
|
282
|
+
): Promise<ApiResponse<{ records: WorkflowRun[]; total: number }>> {
|
|
283
|
+
const { threadId } = request.params;
|
|
284
|
+
const tenantId = getTenantId(request);
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
const store = getTrackingStore();
|
|
288
|
+
if (!store) {
|
|
289
|
+
return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const runs = await store.getWorkflowRunsByThreadId(tenantId, threadId);
|
|
293
|
+
return {
|
|
294
|
+
success: true,
|
|
295
|
+
message: "Successfully retrieved workflow runs",
|
|
296
|
+
data: { records: runs, total: runs.length },
|
|
297
|
+
};
|
|
298
|
+
} catch (error) {
|
|
299
|
+
request.log.error(error, "Failed to get workflow runs");
|
|
300
|
+
return reply.status(500).send({ success: false, message: "Failed to retrieve workflow runs" });
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export async function getWorkflowRun(
|
|
305
|
+
request: FastifyRequest<{ Params: { runId: string } }>,
|
|
306
|
+
reply: FastifyReply
|
|
307
|
+
): Promise<ApiResponse<WorkflowRun>> {
|
|
308
|
+
const { runId } = request.params;
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
const store = getTrackingStore();
|
|
312
|
+
if (!store) {
|
|
313
|
+
return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const run = await store.getWorkflowRun(runId);
|
|
317
|
+
if (!run) {
|
|
318
|
+
return reply.status(404).send({ success: false, message: "Workflow run not found" });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return { success: true, message: "Successfully retrieved workflow run", data: run };
|
|
322
|
+
} catch (error) {
|
|
323
|
+
request.log.error(error, "Failed to get workflow run");
|
|
324
|
+
return reply.status(500).send({ success: false, message: "Failed to retrieve workflow run" });
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export async function getRunSteps(
|
|
329
|
+
request: FastifyRequest<{ Params: { runId: string }; Querystring: { step_type?: string; status?: string } }>,
|
|
330
|
+
reply: FastifyReply
|
|
331
|
+
): Promise<ApiResponse<{ records: RunStep[]; total: number }>> {
|
|
332
|
+
const { runId } = request.params;
|
|
333
|
+
const { step_type, status: stepStatus } = request.query;
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
const store = getTrackingStore();
|
|
337
|
+
if (!store) {
|
|
338
|
+
return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
let steps: RunStep[];
|
|
342
|
+
if (step_type) {
|
|
343
|
+
steps = await store.getRunStepsByType(runId, step_type as any);
|
|
344
|
+
} else {
|
|
345
|
+
steps = await store.getRunSteps(runId);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (stepStatus) {
|
|
349
|
+
steps = steps.filter(s => s.status === stepStatus);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
success: true,
|
|
354
|
+
message: "Successfully retrieved run steps",
|
|
355
|
+
data: { records: steps, total: steps.length },
|
|
356
|
+
};
|
|
357
|
+
} catch (error) {
|
|
358
|
+
request.log.error(error, "Failed to get run steps");
|
|
359
|
+
return reply.status(500).send({ success: false, message: "Failed to retrieve run steps" });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export async function getRunTasks(
|
|
364
|
+
request: FastifyRequest<{ Params: { runId: string } }>,
|
|
365
|
+
reply: FastifyReply
|
|
366
|
+
): Promise<ApiResponse<{ records: RunStep[]; total: number }>> {
|
|
367
|
+
const { runId } = request.params;
|
|
368
|
+
|
|
369
|
+
try {
|
|
370
|
+
const store = getTrackingStore();
|
|
371
|
+
if (!store) {
|
|
372
|
+
return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const steps = await store.getInterruptedSteps(runId);
|
|
376
|
+
return {
|
|
377
|
+
success: true,
|
|
378
|
+
message: "Successfully retrieved user tasks",
|
|
379
|
+
data: { records: steps, total: steps.length },
|
|
380
|
+
};
|
|
381
|
+
} catch (error) {
|
|
382
|
+
request.log.error(error, "Failed to get run tasks");
|
|
383
|
+
return reply.status(500).send({ success: false, message: "Failed to retrieve user tasks" });
|
|
384
|
+
}
|
|
385
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
sandboxLatticeManager,
|
|
21
21
|
sqlDatabaseManager,
|
|
22
22
|
getStoreLattice,
|
|
23
|
+
storeLatticeManager,
|
|
23
24
|
agentInstanceManager,
|
|
24
25
|
createSandboxProvider,
|
|
25
26
|
type CreateSandboxProviderConfig,
|
|
@@ -273,6 +274,23 @@ const start = async (config?: LatticeGatewayConfig) => {
|
|
|
273
274
|
logger.info("Registered sandbox manager from env configuration");
|
|
274
275
|
}
|
|
275
276
|
|
|
277
|
+
// Swap workflow tracking to PostgreSQL if DATABASE_URL is set
|
|
278
|
+
if (process.env.DATABASE_URL) {
|
|
279
|
+
try {
|
|
280
|
+
const { PostgreSQLWorkflowTrackingStore } = await import("@axiom-lattice/pg-stores");
|
|
281
|
+
const pgStore = new PostgreSQLWorkflowTrackingStore({
|
|
282
|
+
poolConfig: process.env.DATABASE_URL,
|
|
283
|
+
});
|
|
284
|
+
if (storeLatticeManager.hasLattice("default", "workflowTracking")) {
|
|
285
|
+
storeLatticeManager.removeLattice("default", "workflowTracking");
|
|
286
|
+
}
|
|
287
|
+
storeLatticeManager.registerLattice("default", "workflowTracking", pgStore);
|
|
288
|
+
logger.info("Workflow tracking store switched to PostgreSQL");
|
|
289
|
+
} catch (error) {
|
|
290
|
+
logger.warn("Failed to switch workflow tracking to PostgreSQL, keeping in-memory: " + (error instanceof Error ? error.message : String(error)));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
276
294
|
const target_port = config?.port || Number(process.env.PORT) || 4001;
|
|
277
295
|
|
|
278
296
|
await app.listen({ port: target_port, host: "0.0.0.0" });
|
package/src/routes/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ import * as healthController from "../controllers/health";
|
|
|
13
13
|
import * as skillsController from "../controllers/skills";
|
|
14
14
|
import * as toolsController from "../controllers/tools";
|
|
15
15
|
import * as dataQueryController from "../controllers/data-query";
|
|
16
|
+
import * as workflowTrackingController from "../controllers/workflow-tracking";
|
|
16
17
|
import {
|
|
17
18
|
createRunSchema,
|
|
18
19
|
getAllMemoryItemsSchema,
|
|
@@ -349,6 +350,51 @@ export const registerLatticeRoutes = (app: FastifyInstance): void => {
|
|
|
349
350
|
|
|
350
351
|
registerChannelInstallationRoutes(app);
|
|
351
352
|
|
|
353
|
+
// Workflow tracking routes
|
|
354
|
+
app.get(
|
|
355
|
+
"/api/workflows/definitions",
|
|
356
|
+
workflowTrackingController.getAllWorkflowDefinitions
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
app.get<{
|
|
360
|
+
Querystring: { assistantId?: string; status?: string };
|
|
361
|
+
}>(
|
|
362
|
+
"/api/workflows/runs",
|
|
363
|
+
workflowTrackingController.getAllWorkflowRuns
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
app.get(
|
|
367
|
+
"/api/workflows/inbox",
|
|
368
|
+
workflowTrackingController.getInboxItems
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
app.get<{
|
|
372
|
+
Params: { assistantId: string };
|
|
373
|
+
}>(
|
|
374
|
+
"/api/assistants/:assistantId/workflows/definitions",
|
|
375
|
+
workflowTrackingController.getWorkflowDefinitions
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
app.get<{
|
|
379
|
+
Params: { assistantId: string; threadId: string };
|
|
380
|
+
}>(
|
|
381
|
+
"/api/assistants/:assistantId/threads/:threadId/workflows/runs",
|
|
382
|
+
workflowTrackingController.getWorkflowRuns
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
app.get<{
|
|
386
|
+
Params: { runId: string };
|
|
387
|
+
}>("/api/workflows/runs/:runId", workflowTrackingController.getWorkflowRun);
|
|
388
|
+
|
|
389
|
+
app.get<{
|
|
390
|
+
Params: { runId: string };
|
|
391
|
+
Querystring: { step_type?: string; status?: string };
|
|
392
|
+
}>("/api/workflows/runs/:runId/steps", workflowTrackingController.getRunSteps);
|
|
393
|
+
|
|
394
|
+
app.get<{
|
|
395
|
+
Params: { runId: string };
|
|
396
|
+
}>("/api/workflows/runs/:runId/tasks", workflowTrackingController.getRunTasks);
|
|
397
|
+
|
|
352
398
|
// // Thread 状态查询路由
|
|
353
399
|
// app.get("/api/threads/:thread_id/status", getThreadStatusHandler);
|
|
354
400
|
// app.get("/api/assistants/:assistant_id/threads/status", getAgentThreadsHandler);
|
|
@@ -8,8 +8,11 @@ export interface AgentTaskRequest {
|
|
|
8
8
|
thread_id: string;
|
|
9
9
|
"x-tenant-id": string;
|
|
10
10
|
command?: any;
|
|
11
|
-
callback_event?: string;
|
|
12
|
-
runConfig?: Record<string, any>;
|
|
11
|
+
callback_event?: string;
|
|
12
|
+
runConfig?: Record<string, any>;
|
|
13
|
+
main_thread_id?: string;
|
|
14
|
+
main_tenant_id?: string;
|
|
15
|
+
main_assistant_id?: string;
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
/**
|
|
@@ -29,6 +32,9 @@ const handleAgentTask = async (
|
|
|
29
32
|
command,
|
|
30
33
|
callback_event,
|
|
31
34
|
runConfig,
|
|
35
|
+
main_thread_id,
|
|
36
|
+
main_tenant_id,
|
|
37
|
+
main_assistant_id,
|
|
32
38
|
} = taskRequest;
|
|
33
39
|
|
|
34
40
|
|
|
@@ -50,6 +56,38 @@ const handleAgentTask = async (
|
|
|
50
56
|
state: evt.state,
|
|
51
57
|
config: { assistant_id, thread_id, tenant_id },
|
|
52
58
|
});
|
|
59
|
+
|
|
60
|
+
if (main_thread_id && main_tenant_id) {
|
|
61
|
+
try {
|
|
62
|
+
const mainAgent = agentInstanceManager.getAgent({
|
|
63
|
+
assistant_id: main_assistant_id ?? assistant_id,
|
|
64
|
+
thread_id: main_thread_id,
|
|
65
|
+
tenant_id: main_tenant_id,
|
|
66
|
+
});
|
|
67
|
+
if (mainAgent) {
|
|
68
|
+
const messages = evt.state?.values?.messages;
|
|
69
|
+
const lastAIMessage = messages
|
|
70
|
+
?.filter((m: any) => m.type === 'ai' || m.getType?.() === 'ai')
|
|
71
|
+
.pop();
|
|
72
|
+
const summary = lastAIMessage?.content
|
|
73
|
+
?.substring(0, 500) ?? '(no output)';
|
|
74
|
+
|
|
75
|
+
mainAgent.addMessage(
|
|
76
|
+
{
|
|
77
|
+
input: {
|
|
78
|
+
message: `[Async task completed]\ntask_id: ${thread_id}\n${summary}`,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
).catch((err: Error) => {
|
|
82
|
+
console.error('Failed to notify main thread:', err);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
mainAgent.updateAsyncTaskStatus(thread_id, 'completed');
|
|
86
|
+
}
|
|
87
|
+
} catch (err) {
|
|
88
|
+
console.error('Failed to notify main thread:', err);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
53
91
|
})
|
|
54
92
|
agent.subscribeOnce("message:interrupted", (evt) => {
|
|
55
93
|
eventBus.publish(callback_event, {
|