@axiom-lattice/gateway 2.1.110 → 2.1.112

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.
@@ -510,7 +510,9 @@ export const registerLatticeRoutes = (app: FastifyInstance, channelDeps?: { rout
510
510
  workflowTrackingController.getAllWorkflowRuns
511
511
  );
512
512
 
513
- app.get(
513
+ app.get<{
514
+ Querystring: { page?: string; pageSize?: string };
515
+ }>(
514
516
  "/api/workflows/inbox",
515
517
  workflowTrackingController.getInboxItems
516
518
  );
@@ -546,6 +548,11 @@ export const registerLatticeRoutes = (app: FastifyInstance, channelDeps?: { rout
546
548
  Params: { runId: string };
547
549
  }>("/api/workflows/runs/:runId/tasks", workflowTrackingController.getRunTasks);
548
550
 
551
+ // Abort a running workflow — stops graph + all sub-agents
552
+ app.post<{
553
+ Params: { runId: string };
554
+ }>("/api/workflows/runs/:runId/abort", workflowTrackingController.abortWorkflowRun);
555
+
549
556
  // 简化的 Inbox 任务回复接口(后台 streaming 执行,立即返回)
550
557
  app.post<{
551
558
  Body: { runId: string; answers: Array<{ questionIndex: number; selectedOptions: string[] }> };
@@ -1,6 +1,9 @@
1
1
  import { EventEmitter } from "events";
2
- import { getStoreLattice, modelLatticeManager } from "@axiom-lattice/core";
3
- import { LatticeEvalProject } from "@axiom-lattice/agent-eval";
2
+ import {
3
+ getStoreLattice,
4
+ modelLatticeManager,
5
+ LatticeEvalProject,
6
+ } from "@axiom-lattice/core";
4
7
  import type { EvalStore } from "@axiom-lattice/protocols";
5
8
  import type {
6
9
  CaseRunResult,
@@ -8,8 +11,7 @@ import type {
8
11
  LatticeEvalProjectType,
9
12
  LatticeEvalSuiteType,
10
13
  LatticeEvalLogEvent,
11
- OutputType,
12
- } from "@axiom-lattice/agent-eval";
14
+ } from "@axiom-lattice/core";
13
15
  import { v4 as uuidv4 } from "uuid";
14
16
 
15
17
  export interface EvalStreamEvent {
@@ -48,6 +50,12 @@ class EvalRunner {
48
50
  const project = await store.getProjectById(tenantId, projectId);
49
51
  if (!project) throw new Error("Project not found");
50
52
 
53
+ for (const ctx of this.runs.values()) {
54
+ if (ctx.projectId === projectId) {
55
+ throw new Error("A run is already in progress for this project");
56
+ }
57
+ }
58
+
51
59
  const existingRuns = await store.getRunsByTenant(tenantId, { projectId, status: "running" });
52
60
  if (existingRuns.length > 0) {
53
61
  throw new Error("A run is already in progress for this project");
@@ -65,8 +73,8 @@ class EvalRunner {
65
73
  cases: cases.map((c) => ({
66
74
  caseId: c.id,
67
75
  input: { message: c.inputMessage, files: c.inputFiles },
68
- steps: c.steps as Array<{ agent_id: string; override_message?: string }>,
69
- output: { type: c.outputType } as OutputType,
76
+ steps: c.steps,
77
+ output: { type: "message_content" },
70
78
  eval: {
71
79
  content_assertion: c.contentAssertion,
72
80
  eval_rubrics: c.rubrics?.map((r) => ({
@@ -79,39 +87,35 @@ class EvalRunner {
79
87
  });
80
88
  }
81
89
 
82
- const runId = uuidv4();
83
- const concurrency = (project as unknown as Record<string, unknown>).concurrency as number || 3;
84
- await store.createRun(tenantId, projectId, runId, { totalCases, concurrency });
85
-
86
- const judgeCfg = project.judgeModelConfig as Record<string, unknown>;
87
- const hasModelKey = Boolean(judgeCfg.modelKey);
88
- const hasApiKey = Boolean(judgeCfg.apiKeyEnvName || judgeCfg.apiKey);
89
- const hasCredentials = hasApiKey;
90
-
91
- let judgeModelConfig: { modelKey?: string; model?: any } = {};
90
+ const judgeCfg = (project.judgeModelConfig ?? {}) as Record<string, unknown>;
91
+ const judgeModelKey = (judgeCfg.modelKey as string) || "";
92
+ let resolvedJudgeModelKey: string;
92
93
 
93
- if (hasModelKey) {
94
- judgeModelConfig = { modelKey: judgeCfg.modelKey as string };
95
- } else if (!hasCredentials) {
96
- const firstModel = modelLatticeManager.getAllLattices()[0];
97
- if (firstModel) {
98
- judgeModelConfig = { modelKey: firstModel.key };
99
- } else {
100
- judgeModelConfig = { model: judgeCfg as any };
94
+ if (judgeModelKey) {
95
+ if (!modelLatticeManager.hasLattice(judgeModelKey)) {
96
+ throw new Error(`Judge model "${judgeModelKey}" is not registered`);
101
97
  }
98
+ resolvedJudgeModelKey = judgeModelKey;
102
99
  } else {
103
- judgeModelConfig = { model: judgeCfg as any };
100
+ const firstModel = modelLatticeManager.getAllLattices()[0];
101
+ if (!firstModel) {
102
+ throw new Error("No model registered — cannot run eval without a judge model");
103
+ }
104
+ resolvedJudgeModelKey = firstModel.key;
105
+ console.warn(`[eval-runner] Project "${project.name}" has no judge modelKey — falling back to first registered model "${firstModel.key}"`);
104
106
  }
105
107
 
108
+ const runId = uuidv4();
109
+ const concurrency = Math.max(1, Math.floor((project as unknown as Record<string, unknown>).concurrency as number) || 3);
110
+ await store.createRun(tenantId, projectId, runId, { totalCases, concurrency });
111
+
106
112
  const projectConfig: LatticeEvalProjectType = {
107
113
  projectName: project.name,
108
114
  version: project.version,
109
115
  description: project.description,
110
116
  suites: evalSuites,
111
- judge_agent_config: judgeModelConfig,
117
+ judge_agent_config: { modelKey: resolvedJudgeModelKey },
112
118
  lattice_server_config: {
113
- base_url: (project.targetServerConfig as Record<string, string>).base_url || "",
114
- api_key: (project.targetServerConfig as Record<string, string>).api_key || "",
115
119
  tenant_id: tenantId,
116
120
  },
117
121
  concurrency,
@@ -163,37 +167,41 @@ class EvalRunner {
163
167
  const runPromise = (async () => {
164
168
  try {
165
169
  const evalProject = new LatticeEvalProject(projectConfig, onCaseComplete);
166
- const { report } = await evalProject.runAllSuitesBatch(concurrency);
167
-
168
- const completedCount = stats.passed + stats.failed;
169
- await store.updateRunStatus(tenantId, runId, {
170
- status: "completed",
171
- completedAt: new Date(),
172
- passedCases: stats.passed,
173
- failedCases: stats.failed,
174
- avgScore: completedCount > 0 ? stats.totalScore / completedCount : 0,
175
- });
176
-
177
- this.eventEmitter.emit(`run:${runId}`, {
178
- type: "completed",
179
- runId,
180
- data: { passed: stats.passed, failed: stats.failed, avgScore: completedCount > 0 ? stats.totalScore / completedCount : 0 },
181
- } satisfies EvalStreamEvent);
170
+ const { report } = await evalProject.runAllSuitesBatch(concurrency, abortController.signal);
171
+
172
+ if (!abortController.signal.aborted) {
173
+ const completedCount = stats.passed + stats.failed;
174
+ await store.updateRunStatus(tenantId, runId, {
175
+ status: "completed",
176
+ completedAt: new Date(),
177
+ passedCases: stats.passed,
178
+ failedCases: stats.failed,
179
+ avgScore: completedCount > 0 ? stats.totalScore / completedCount : 0,
180
+ });
181
+
182
+ this.eventEmitter.emit(`run:${runId}`, {
183
+ type: "completed",
184
+ runId,
185
+ data: { passed: stats.passed, failed: stats.failed, avgScore: completedCount > 0 ? stats.totalScore / completedCount : 0 },
186
+ } satisfies EvalStreamEvent);
187
+ }
182
188
 
183
189
  return { report };
184
190
  } catch (err) {
185
191
  const errorMsg = (err as Error).message;
186
- await store.updateRunStatus(tenantId, runId, {
187
- status: "failed",
188
- error: errorMsg,
189
- completedAt: new Date(),
190
- });
191
-
192
- this.eventEmitter.emit(`run:${runId}`, {
193
- type: "error",
194
- runId,
195
- data: { message: errorMsg },
196
- } satisfies EvalStreamEvent);
192
+ if (!abortController.signal.aborted) {
193
+ await store.updateRunStatus(tenantId, runId, {
194
+ status: "failed",
195
+ error: errorMsg,
196
+ completedAt: new Date(),
197
+ });
198
+
199
+ this.eventEmitter.emit(`run:${runId}`, {
200
+ type: "error",
201
+ runId,
202
+ data: { message: errorMsg },
203
+ } satisfies EvalStreamEvent);
204
+ }
197
205
 
198
206
  throw err;
199
207
  } finally {