@axiom-lattice/gateway 2.1.111 → 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.
package/dist/index.mjs CHANGED
@@ -2227,7 +2227,7 @@ async function executeSqlQuery(client, body, reply) {
2227
2227
  }
2228
2228
 
2229
2229
  // src/controllers/workflow-tracking.ts
2230
- import { getStoreLattice as getStoreLattice4, agentInstanceManager as agentInstanceManager4, ThreadStatus as ThreadStatus2 } from "@axiom-lattice/core";
2230
+ import { getStoreLattice as getStoreLattice4, agentInstanceManager as agentInstanceManager4, ThreadStatus as ThreadStatus2, abortWorkflowRun as signalAbort } from "@axiom-lattice/core";
2231
2231
  import { MessageChunkTypes as MessageChunkTypes2 } from "@axiom-lattice/protocols";
2232
2232
  function getTenantId7(request) {
2233
2233
  const userTenantId = request.user?.tenantId;
@@ -2366,6 +2366,8 @@ async function getAllWorkflowRuns(request, reply) {
2366
2366
  });
2367
2367
  }
2368
2368
  }
2369
+ var INBOX_DEFAULT_PAGE_SIZE = 20;
2370
+ var INBOX_MAX_PAGE_SIZE = 100;
2369
2371
  async function getInboxItems(request, reply) {
2370
2372
  const tenantId = getTenantId7(request);
2371
2373
  try {
@@ -2383,10 +2385,30 @@ async function getInboxItems(request, reply) {
2383
2385
  }
2384
2386
  } catch {
2385
2387
  }
2386
- const runs = await store.getWorkflowRunsByTenantId(tenantId);
2387
- const pendingRuns = runs.filter((r) => r.status === "interrupted");
2388
+ const { page: pageParam, pageSize: pageSizeParam } = request.query;
2389
+ const paged = pageParam !== void 0 || pageSizeParam !== void 0;
2390
+ const page = Math.max(1, parseInt(pageParam ?? "", 10) || 1);
2391
+ const pageSize = Math.min(INBOX_MAX_PAGE_SIZE, Math.max(1, parseInt(pageSizeParam ?? "", 10) || INBOX_DEFAULT_PAGE_SIZE));
2392
+ let pendingRuns;
2393
+ let total;
2394
+ if (paged) {
2395
+ const result = await store.queryWorkflowRuns(tenantId, {
2396
+ status: "interrupted",
2397
+ limit: pageSize,
2398
+ offset: (page - 1) * pageSize
2399
+ });
2400
+ pendingRuns = result.records;
2401
+ total = result.total;
2402
+ } else {
2403
+ const result = await store.queryWorkflowRuns(tenantId, { status: "interrupted" });
2404
+ pendingRuns = result.records;
2405
+ }
2388
2406
  if (pendingRuns.length === 0) {
2389
- return { success: true, message: "No pending workflows", data: { records: [] } };
2407
+ return {
2408
+ success: true,
2409
+ message: "No pending workflows",
2410
+ data: paged ? { records: [], total, page, pageSize } : { records: [] }
2411
+ };
2390
2412
  }
2391
2413
  const checkPromises = pendingRuns.map(async (r) => {
2392
2414
  try {
@@ -2443,13 +2465,10 @@ async function getInboxItems(request, reply) {
2443
2465
  });
2444
2466
  const results = await Promise.all(checkPromises);
2445
2467
  const inboxItems = results.flat();
2446
- inboxItems.sort(
2447
- (a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime()
2448
- );
2449
2468
  return {
2450
2469
  success: true,
2451
2470
  message: "Successfully retrieved inbox items",
2452
- data: { records: inboxItems }
2471
+ data: paged ? { records: inboxItems, total, page, pageSize } : { records: inboxItems }
2453
2472
  };
2454
2473
  } catch (error) {
2455
2474
  request.log.error(error, "Failed to get inbox items");
@@ -2682,6 +2701,54 @@ async function replyInboxTask(request, reply) {
2682
2701
  });
2683
2702
  }
2684
2703
  }
2704
+ async function abortWorkflowRun(request, reply) {
2705
+ try {
2706
+ const { runId } = request.params;
2707
+ const tenantId = getTenantId7(request);
2708
+ const store = getTrackingStore();
2709
+ if (!store) {
2710
+ return reply.status(404).send({
2711
+ success: false,
2712
+ message: "No workflow tracking store configured"
2713
+ });
2714
+ }
2715
+ const run = await store.getWorkflowRun(runId);
2716
+ if (!run) {
2717
+ return reply.status(404).send({
2718
+ success: false,
2719
+ message: "Workflow run not found"
2720
+ });
2721
+ }
2722
+ await store.updateWorkflowRun(runId, {
2723
+ status: "cancelled",
2724
+ errorMessage: "Aborted by user",
2725
+ completedAt: /* @__PURE__ */ new Date()
2726
+ }).catch(() => {
2727
+ });
2728
+ const workspace_id = request.headers["x-workspace-id"];
2729
+ const project_id = request.headers["x-project-id"];
2730
+ const agent = agentInstanceManager4.getAgent({
2731
+ assistant_id: run.assistantId,
2732
+ thread_id: run.threadId,
2733
+ tenant_id: tenantId,
2734
+ workspace_id,
2735
+ project_id
2736
+ });
2737
+ await agent.abort();
2738
+ signalAbort(runId);
2739
+ return reply.status(200).send({
2740
+ success: true,
2741
+ message: "Workflow aborted",
2742
+ data: { runId, assistantId: run.assistantId, threadId: run.threadId }
2743
+ });
2744
+ } catch (error) {
2745
+ request.log.error(error, "Failed to abort workflow run");
2746
+ return reply.status(500).send({
2747
+ success: false,
2748
+ message: `Abort failed: ${error.message}`
2749
+ });
2750
+ }
2751
+ }
2685
2752
 
2686
2753
  // src/controllers/personal-assistant.ts
2687
2754
  import { getStoreLattice as getStoreLattice5, PersonalAssistantConfig } from "@axiom-lattice/core";
@@ -5026,6 +5093,10 @@ var testOrDiscoverBody = z.object({
5026
5093
  config: z.record(z.unknown()).optional()
5027
5094
  });
5028
5095
  function getTenant(request) {
5096
+ const userTenantId = request.user?.tenantId;
5097
+ if (userTenantId) {
5098
+ return userTenantId;
5099
+ }
5029
5100
  return request.headers["x-tenant-id"] || "default";
5030
5101
  }
5031
5102
  function maskSecrets(config) {
@@ -5171,8 +5242,11 @@ import { v4 as uuidv44 } from "uuid";
5171
5242
 
5172
5243
  // src/services/eval-runner.ts
5173
5244
  import { EventEmitter } from "events";
5174
- import { getStoreLattice as getStoreLattice10, modelLatticeManager as modelLatticeManager2 } from "@axiom-lattice/core";
5175
- import { LatticeEvalProject } from "@axiom-lattice/agent-eval";
5245
+ import {
5246
+ getStoreLattice as getStoreLattice10,
5247
+ modelLatticeManager as modelLatticeManager2,
5248
+ LatticeEvalProject
5249
+ } from "@axiom-lattice/core";
5176
5250
  import { v4 as uuidv43 } from "uuid";
5177
5251
  function mapLogs(logs) {
5178
5252
  return logs.map((l) => ({
@@ -5194,6 +5268,11 @@ var EvalRunner = class {
5194
5268
  const store = this.getEvalStore();
5195
5269
  const project = await store.getProjectById(tenantId, projectId);
5196
5270
  if (!project) throw new Error("Project not found");
5271
+ for (const ctx of this.runs.values()) {
5272
+ if (ctx.projectId === projectId) {
5273
+ throw new Error("A run is already in progress for this project");
5274
+ }
5275
+ }
5197
5276
  const existingRuns = await store.getRunsByTenant(tenantId, { projectId, status: "running" });
5198
5277
  if (existingRuns.length > 0) {
5199
5278
  throw new Error("A run is already in progress for this project");
@@ -5210,7 +5289,7 @@ var EvalRunner = class {
5210
5289
  caseId: c.id,
5211
5290
  input: { message: c.inputMessage, files: c.inputFiles },
5212
5291
  steps: c.steps,
5213
- output: { type: c.outputType },
5292
+ output: { type: "message_content" },
5214
5293
  eval: {
5215
5294
  content_assertion: c.contentAssertion,
5216
5295
  eval_rubrics: c.rubrics?.map((r) => ({
@@ -5222,35 +5301,32 @@ var EvalRunner = class {
5222
5301
  }))
5223
5302
  });
5224
5303
  }
5225
- const runId = uuidv43();
5226
- const concurrency = project.concurrency || 3;
5227
- await store.createRun(tenantId, projectId, runId, { totalCases, concurrency });
5228
- const judgeCfg = project.judgeModelConfig;
5229
- const hasModelKey = Boolean(judgeCfg.modelKey);
5230
- const hasApiKey = Boolean(judgeCfg.apiKeyEnvName || judgeCfg.apiKey);
5231
- const hasCredentials = hasApiKey;
5232
- let judgeModelConfig = {};
5233
- if (hasModelKey) {
5234
- judgeModelConfig = { modelKey: judgeCfg.modelKey };
5235
- } else if (!hasCredentials) {
5236
- const firstModel = modelLatticeManager2.getAllLattices()[0];
5237
- if (firstModel) {
5238
- judgeModelConfig = { modelKey: firstModel.key };
5239
- } else {
5240
- judgeModelConfig = { model: judgeCfg };
5304
+ const judgeCfg = project.judgeModelConfig ?? {};
5305
+ const judgeModelKey = judgeCfg.modelKey || "";
5306
+ let resolvedJudgeModelKey;
5307
+ if (judgeModelKey) {
5308
+ if (!modelLatticeManager2.hasLattice(judgeModelKey)) {
5309
+ throw new Error(`Judge model "${judgeModelKey}" is not registered`);
5241
5310
  }
5311
+ resolvedJudgeModelKey = judgeModelKey;
5242
5312
  } else {
5243
- judgeModelConfig = { model: judgeCfg };
5313
+ const firstModel = modelLatticeManager2.getAllLattices()[0];
5314
+ if (!firstModel) {
5315
+ throw new Error("No model registered \u2014 cannot run eval without a judge model");
5316
+ }
5317
+ resolvedJudgeModelKey = firstModel.key;
5318
+ console.warn(`[eval-runner] Project "${project.name}" has no judge modelKey \u2014 falling back to first registered model "${firstModel.key}"`);
5244
5319
  }
5320
+ const runId = uuidv43();
5321
+ const concurrency = Math.max(1, Math.floor(project.concurrency) || 3);
5322
+ await store.createRun(tenantId, projectId, runId, { totalCases, concurrency });
5245
5323
  const projectConfig = {
5246
5324
  projectName: project.name,
5247
5325
  version: project.version,
5248
5326
  description: project.description,
5249
5327
  suites: evalSuites,
5250
- judge_agent_config: judgeModelConfig,
5328
+ judge_agent_config: { modelKey: resolvedJudgeModelKey },
5251
5329
  lattice_server_config: {
5252
- base_url: project.targetServerConfig.base_url || "",
5253
- api_key: project.targetServerConfig.api_key || "",
5254
5330
  tenant_id: tenantId
5255
5331
  },
5256
5332
  concurrency
@@ -5295,33 +5371,37 @@ var EvalRunner = class {
5295
5371
  const runPromise = (async () => {
5296
5372
  try {
5297
5373
  const evalProject = new LatticeEvalProject(projectConfig, onCaseComplete);
5298
- const { report } = await evalProject.runAllSuitesBatch(concurrency);
5299
- const completedCount = stats.passed + stats.failed;
5300
- await store.updateRunStatus(tenantId, runId, {
5301
- status: "completed",
5302
- completedAt: /* @__PURE__ */ new Date(),
5303
- passedCases: stats.passed,
5304
- failedCases: stats.failed,
5305
- avgScore: completedCount > 0 ? stats.totalScore / completedCount : 0
5306
- });
5307
- this.eventEmitter.emit(`run:${runId}`, {
5308
- type: "completed",
5309
- runId,
5310
- data: { passed: stats.passed, failed: stats.failed, avgScore: completedCount > 0 ? stats.totalScore / completedCount : 0 }
5311
- });
5374
+ const { report } = await evalProject.runAllSuitesBatch(concurrency, abortController.signal);
5375
+ if (!abortController.signal.aborted) {
5376
+ const completedCount = stats.passed + stats.failed;
5377
+ await store.updateRunStatus(tenantId, runId, {
5378
+ status: "completed",
5379
+ completedAt: /* @__PURE__ */ new Date(),
5380
+ passedCases: stats.passed,
5381
+ failedCases: stats.failed,
5382
+ avgScore: completedCount > 0 ? stats.totalScore / completedCount : 0
5383
+ });
5384
+ this.eventEmitter.emit(`run:${runId}`, {
5385
+ type: "completed",
5386
+ runId,
5387
+ data: { passed: stats.passed, failed: stats.failed, avgScore: completedCount > 0 ? stats.totalScore / completedCount : 0 }
5388
+ });
5389
+ }
5312
5390
  return { report };
5313
5391
  } catch (err) {
5314
5392
  const errorMsg = err.message;
5315
- await store.updateRunStatus(tenantId, runId, {
5316
- status: "failed",
5317
- error: errorMsg,
5318
- completedAt: /* @__PURE__ */ new Date()
5319
- });
5320
- this.eventEmitter.emit(`run:${runId}`, {
5321
- type: "error",
5322
- runId,
5323
- data: { message: errorMsg }
5324
- });
5393
+ if (!abortController.signal.aborted) {
5394
+ await store.updateRunStatus(tenantId, runId, {
5395
+ status: "failed",
5396
+ error: errorMsg,
5397
+ completedAt: /* @__PURE__ */ new Date()
5398
+ });
5399
+ this.eventEmitter.emit(`run:${runId}`, {
5400
+ type: "error",
5401
+ runId,
5402
+ data: { message: errorMsg }
5403
+ });
5404
+ }
5325
5405
  throw err;
5326
5406
  } finally {
5327
5407
  this.runs.delete(runId);
@@ -5366,12 +5446,27 @@ async function createProject(request, reply) {
5366
5446
  const store = getEvalStore();
5367
5447
  const id = uuidv44();
5368
5448
  const data = request.body;
5449
+ const serverCfg = data.targetServerConfig || {};
5450
+ const judgeCfg = data.judgeModelConfig || {};
5451
+ const modelKey = judgeCfg.modelKey || "";
5452
+ if (modelKey) {
5453
+ const { modelLatticeManager: modelLatticeManager3 } = await import("@axiom-lattice/core");
5454
+ if (!modelLatticeManager3.hasLattice(modelKey)) {
5455
+ return reply.status(400).send({ success: false, message: `Judge model "${modelKey}" is not registered` });
5456
+ }
5457
+ }
5369
5458
  const project = await store.createProject(tenantId, id, {
5370
5459
  name: data.name,
5371
5460
  description: data.description,
5372
5461
  version: data.version,
5373
- judgeModelConfig: data.judgeModelConfig ?? {},
5374
- targetServerConfig: data.targetServerConfig ?? {},
5462
+ judgeModelConfig: {
5463
+ modelKey,
5464
+ displayName: judgeCfg.displayName || ""
5465
+ },
5466
+ targetServerConfig: {
5467
+ base_url: serverCfg.base_url || "",
5468
+ api_key: serverCfg.api_key || ""
5469
+ },
5375
5470
  concurrency: data.concurrency ?? 1,
5376
5471
  reportConfig: data.reportConfig
5377
5472
  });
@@ -5395,7 +5490,7 @@ async function listProjects(request, reply) {
5395
5490
  const { modelLatticeManager: modelLatticeManager3 } = await import("@axiom-lattice/core");
5396
5491
  const models = modelLatticeManager3.getAllLattices();
5397
5492
  const first = models[0];
5398
- const judgeModel = first ? { modelKey: first.key } : { provider: "openai", model: "gpt-4" };
5493
+ const judgeModel = first ? { modelKey: first.key } : {};
5399
5494
  const host = request.hostname || "localhost";
5400
5495
  const baseUrl = `http://${host}:${process.env.PORT || 4001}`;
5401
5496
  await store.createProject(tenantId, uuidv44(), {
@@ -5448,7 +5543,30 @@ async function updateProject(request, reply) {
5448
5543
  if (!existing) {
5449
5544
  return reply.status(404).send({ success: false, message: "Project not found" });
5450
5545
  }
5451
- const updated = await store.updateProject(tenantId, pid, request.body);
5546
+ const body = request.body;
5547
+ const updateData = { ...body };
5548
+ const serverCfg = body.targetServerConfig || {};
5549
+ if (body.targetServerConfig !== void 0) {
5550
+ updateData.targetServerConfig = {
5551
+ base_url: serverCfg.base_url || "",
5552
+ api_key: serverCfg.api_key || ""
5553
+ };
5554
+ }
5555
+ if (body.judgeModelConfig !== void 0) {
5556
+ const judgeCfg = body.judgeModelConfig || {};
5557
+ const modelKey = judgeCfg.modelKey || "";
5558
+ if (modelKey) {
5559
+ const { modelLatticeManager: modelLatticeManager3 } = await import("@axiom-lattice/core");
5560
+ if (!modelLatticeManager3.hasLattice(modelKey)) {
5561
+ return reply.status(400).send({ success: false, message: `Judge model "${modelKey}" is not registered` });
5562
+ }
5563
+ }
5564
+ updateData.judgeModelConfig = {
5565
+ modelKey,
5566
+ displayName: judgeCfg.displayName || ""
5567
+ };
5568
+ }
5569
+ const updated = await store.updateProject(tenantId, pid, updateData);
5452
5570
  return {
5453
5571
  success: true,
5454
5572
  message: "Successfully updated project",
@@ -7499,6 +7617,7 @@ var registerLatticeRoutes = (app2, channelDeps) => {
7499
7617
  app2.delete("/api/workflows/runs/:runId", deleteWorkflowRun);
7500
7618
  app2.get("/api/workflows/runs/:runId/steps", getRunSteps);
7501
7619
  app2.get("/api/workflows/runs/:runId/tasks", getRunTasks);
7620
+ app2.post("/api/workflows/runs/:runId/abort", abortWorkflowRun);
7502
7621
  app2.post("/api/workflows/inbox/reply", replyInboxTask);
7503
7622
  app2.delete(
7504
7623
  "/api/assistants/:assistant_id/threads/:thread_id/pending-messages/:message_id",
@@ -8018,7 +8137,7 @@ function createAuditLoggerMiddleware() {
8018
8137
  }
8019
8138
 
8020
8139
  // src/index.ts
8021
- import { setBindingRegistry, setMenuRegistry } from "@axiom-lattice/core";
8140
+ import { setBindingRegistry, setMenuRegistry, setEvalRunService } from "@axiom-lattice/core";
8022
8141
 
8023
8142
  // src/swagger.ts
8024
8143
  import swagger from "@fastify/swagger";
@@ -8573,6 +8692,7 @@ var start = async (config) => {
8573
8692
  error: err instanceof Error ? err.message : String(err)
8574
8693
  });
8575
8694
  }
8695
+ setEvalRunService(evalRunner);
8576
8696
  try {
8577
8697
  const menuStore = getStoreLattice16("default", "menu").store;
8578
8698
  setMenuRegistry(menuStore);
@@ -8633,11 +8753,13 @@ var start = async (config) => {
8633
8753
  agentTaskConsumer.startPollingQueue();
8634
8754
  }
8635
8755
  }
8636
- agentInstanceManager8.restore().then((stats) => {
8637
- logger4.info(`Agent recovery complete: ${stats.restored} threads restored, ${stats.errors} errors`);
8638
- }).catch((error) => {
8639
- logger4.error("Agent recovery failed", { error });
8640
- });
8756
+ if (process.env.AXIOM_RESTORE_PENDING_ON_STARTUP === "true") {
8757
+ agentInstanceManager8.restore().then((stats) => {
8758
+ logger4.info(`Agent recovery complete: ${stats.restored} threads restored, ${stats.errors} errors`);
8759
+ }).catch((error) => {
8760
+ logger4.error("Agent recovery failed", { error });
8761
+ });
8762
+ }
8641
8763
  restoreMcpConnections().catch((error) => {
8642
8764
  logger4.error("MCP connection restoration failed", { error });
8643
8765
  });