@axiom-lattice/gateway 2.1.72 → 2.1.74
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 +21 -0
- package/dist/index.js +624 -44
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +605 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
- package/src/controllers/eval.ts +469 -0
- package/src/controllers/workflow-tracking.ts +3 -1
- package/src/routes/index.ts +3 -0
- package/src/services/eval-runner.ts +228 -0
package/dist/index.mjs
CHANGED
|
@@ -1743,10 +1743,11 @@ async function getDefinitionsFromAssistants(tenantId) {
|
|
|
1743
1743
|
if (!def || def.type !== "processing") continue;
|
|
1744
1744
|
if (!def.middleware) continue;
|
|
1745
1745
|
for (const mw of def.middleware) {
|
|
1746
|
-
if (mw.type === "topology" && mw.enabled
|
|
1746
|
+
if (mw.type === "topology" && mw.enabled) {
|
|
1747
1747
|
results.push({
|
|
1748
1748
|
assistantId: a.id,
|
|
1749
1749
|
assistantName: a.name,
|
|
1750
|
+
description: a.description,
|
|
1750
1751
|
topologyEdges: mw.config.edges,
|
|
1751
1752
|
totalEdges: mw.config.edges.length
|
|
1752
1753
|
});
|
|
@@ -4592,12 +4593,590 @@ function registerMcpServerConfigRoutes(app2) {
|
|
|
4592
4593
|
app2.post("/api/mcp-servers/test-tools", testMcpServerTools);
|
|
4593
4594
|
}
|
|
4594
4595
|
|
|
4595
|
-
// src/controllers/
|
|
4596
|
-
import { getStoreLattice as
|
|
4596
|
+
// src/controllers/eval.ts
|
|
4597
|
+
import { getStoreLattice as getStoreLattice10 } from "@axiom-lattice/core";
|
|
4598
|
+
import { v4 as uuidv43 } from "uuid";
|
|
4599
|
+
|
|
4600
|
+
// src/services/eval-runner.ts
|
|
4601
|
+
import { EventEmitter } from "events";
|
|
4602
|
+
import { getStoreLattice as getStoreLattice9, modelLatticeManager as modelLatticeManager2 } from "@axiom-lattice/core";
|
|
4603
|
+
import { LatticeEvalProject } from "@axiom-lattice/agent-eval";
|
|
4597
4604
|
import { v4 as uuidv42 } from "uuid";
|
|
4605
|
+
function mapLogs(logs) {
|
|
4606
|
+
return logs.map((l) => ({
|
|
4607
|
+
timestamp: l.ts,
|
|
4608
|
+
level: l.level,
|
|
4609
|
+
message: l.message,
|
|
4610
|
+
data: l.data
|
|
4611
|
+
}));
|
|
4612
|
+
}
|
|
4613
|
+
var EvalRunner = class {
|
|
4614
|
+
constructor() {
|
|
4615
|
+
this.runs = /* @__PURE__ */ new Map();
|
|
4616
|
+
this.eventEmitter = new EventEmitter();
|
|
4617
|
+
}
|
|
4618
|
+
getEventEmitter() {
|
|
4619
|
+
return this.eventEmitter;
|
|
4620
|
+
}
|
|
4621
|
+
async startRun(tenantId, projectId) {
|
|
4622
|
+
const store = this.getEvalStore();
|
|
4623
|
+
const project = await store.getProjectById(tenantId, projectId);
|
|
4624
|
+
if (!project) throw new Error("Project not found");
|
|
4625
|
+
const existingRuns = await store.getRunsByTenant(tenantId, { projectId, status: "running" });
|
|
4626
|
+
if (existingRuns.length > 0) {
|
|
4627
|
+
throw new Error("A run is already in progress for this project");
|
|
4628
|
+
}
|
|
4629
|
+
const suites = await store.getSuitesByProject(tenantId, projectId);
|
|
4630
|
+
const evalSuites = [];
|
|
4631
|
+
let totalCases = 0;
|
|
4632
|
+
for (const suite of suites) {
|
|
4633
|
+
const cases = await store.getCasesBySuite(tenantId, suite.id);
|
|
4634
|
+
totalCases += cases.length;
|
|
4635
|
+
evalSuites.push({
|
|
4636
|
+
suiteName: suite.name,
|
|
4637
|
+
cases: cases.map((c) => ({
|
|
4638
|
+
caseId: c.id,
|
|
4639
|
+
input: { message: c.inputMessage, files: c.inputFiles },
|
|
4640
|
+
steps: c.steps,
|
|
4641
|
+
output: { type: c.outputType },
|
|
4642
|
+
eval: {
|
|
4643
|
+
content_assertion: c.contentAssertion,
|
|
4644
|
+
eval_rubrics: c.rubrics?.map((r) => ({
|
|
4645
|
+
dimension: r.name,
|
|
4646
|
+
weight: r.weight,
|
|
4647
|
+
description: r.description
|
|
4648
|
+
}))
|
|
4649
|
+
}
|
|
4650
|
+
}))
|
|
4651
|
+
});
|
|
4652
|
+
}
|
|
4653
|
+
const runId = uuidv42();
|
|
4654
|
+
const concurrency = project.concurrency || 3;
|
|
4655
|
+
await store.createRun(tenantId, projectId, runId, { totalCases, concurrency });
|
|
4656
|
+
const judgeCfg = project.judgeModelConfig;
|
|
4657
|
+
const hasModelKey = Boolean(judgeCfg.modelKey);
|
|
4658
|
+
const hasApiKey = Boolean(judgeCfg.apiKeyEnvName || judgeCfg.apiKey);
|
|
4659
|
+
const hasCredentials = hasApiKey;
|
|
4660
|
+
let judgeModelConfig = {};
|
|
4661
|
+
if (hasModelKey) {
|
|
4662
|
+
judgeModelConfig = { modelKey: judgeCfg.modelKey };
|
|
4663
|
+
} else if (!hasCredentials) {
|
|
4664
|
+
const firstModel = modelLatticeManager2.getAllLattices()[0];
|
|
4665
|
+
if (firstModel) {
|
|
4666
|
+
judgeModelConfig = { modelKey: firstModel.key };
|
|
4667
|
+
} else {
|
|
4668
|
+
judgeModelConfig = { model: judgeCfg };
|
|
4669
|
+
}
|
|
4670
|
+
} else {
|
|
4671
|
+
judgeModelConfig = { model: judgeCfg };
|
|
4672
|
+
}
|
|
4673
|
+
const projectConfig = {
|
|
4674
|
+
projectName: project.name,
|
|
4675
|
+
version: project.version,
|
|
4676
|
+
description: project.description,
|
|
4677
|
+
suites: evalSuites,
|
|
4678
|
+
judge_agent_config: judgeModelConfig,
|
|
4679
|
+
lattice_server_config: {
|
|
4680
|
+
base_url: project.targetServerConfig.base_url || "",
|
|
4681
|
+
api_key: project.targetServerConfig.api_key || "",
|
|
4682
|
+
tenant_id: tenantId
|
|
4683
|
+
},
|
|
4684
|
+
concurrency
|
|
4685
|
+
};
|
|
4686
|
+
const abortController = new AbortController();
|
|
4687
|
+
const stats = { passed: 0, failed: 0, totalScore: 0 };
|
|
4688
|
+
const onCaseComplete = async (result, suiteName) => {
|
|
4689
|
+
const passed = result.result?.pass ?? false;
|
|
4690
|
+
const score = result.result?.final_score ?? 0;
|
|
4691
|
+
if (passed) stats.passed++;
|
|
4692
|
+
else stats.failed++;
|
|
4693
|
+
stats.totalScore += score;
|
|
4694
|
+
const completedCount = stats.passed + stats.failed;
|
|
4695
|
+
await store.createRunResult(tenantId, runId, uuidv42(), {
|
|
4696
|
+
suiteName,
|
|
4697
|
+
caseId: result.caseId,
|
|
4698
|
+
pass: passed,
|
|
4699
|
+
score,
|
|
4700
|
+
summary: result.result?.summary,
|
|
4701
|
+
dimensionResults: result.result?.dimension_results?.map((d) => ({
|
|
4702
|
+
name: d.name,
|
|
4703
|
+
score: d.score,
|
|
4704
|
+
reason: d.reason
|
|
4705
|
+
})),
|
|
4706
|
+
durationMs: result.duration_ms,
|
|
4707
|
+
messages: result.messages,
|
|
4708
|
+
logs: mapLogs(result.logs),
|
|
4709
|
+
error: result.error
|
|
4710
|
+
});
|
|
4711
|
+
const avgScore = completedCount > 0 ? stats.totalScore / completedCount : 0;
|
|
4712
|
+
await store.updateRunStatus(tenantId, runId, {
|
|
4713
|
+
passedCases: stats.passed,
|
|
4714
|
+
failedCases: stats.failed,
|
|
4715
|
+
avgScore
|
|
4716
|
+
});
|
|
4717
|
+
this.eventEmitter.emit(`run:${runId}`, {
|
|
4718
|
+
type: "progress",
|
|
4719
|
+
runId,
|
|
4720
|
+
data: { suiteName, caseId: result.caseId, pass: passed, score, completed: completedCount, total: totalCases }
|
|
4721
|
+
});
|
|
4722
|
+
};
|
|
4723
|
+
const runPromise = (async () => {
|
|
4724
|
+
try {
|
|
4725
|
+
const evalProject = new LatticeEvalProject(projectConfig, onCaseComplete);
|
|
4726
|
+
const { report } = await evalProject.runAllSuitesBatch(concurrency);
|
|
4727
|
+
const completedCount = stats.passed + stats.failed;
|
|
4728
|
+
await store.updateRunStatus(tenantId, runId, {
|
|
4729
|
+
status: "completed",
|
|
4730
|
+
completedAt: /* @__PURE__ */ new Date(),
|
|
4731
|
+
passedCases: stats.passed,
|
|
4732
|
+
failedCases: stats.failed,
|
|
4733
|
+
avgScore: completedCount > 0 ? stats.totalScore / completedCount : 0
|
|
4734
|
+
});
|
|
4735
|
+
this.eventEmitter.emit(`run:${runId}`, {
|
|
4736
|
+
type: "completed",
|
|
4737
|
+
runId,
|
|
4738
|
+
data: { passed: stats.passed, failed: stats.failed, avgScore: completedCount > 0 ? stats.totalScore / completedCount : 0 }
|
|
4739
|
+
});
|
|
4740
|
+
return { report };
|
|
4741
|
+
} catch (err) {
|
|
4742
|
+
const errorMsg = err.message;
|
|
4743
|
+
await store.updateRunStatus(tenantId, runId, {
|
|
4744
|
+
status: "failed",
|
|
4745
|
+
error: errorMsg,
|
|
4746
|
+
completedAt: /* @__PURE__ */ new Date()
|
|
4747
|
+
});
|
|
4748
|
+
this.eventEmitter.emit(`run:${runId}`, {
|
|
4749
|
+
type: "error",
|
|
4750
|
+
runId,
|
|
4751
|
+
data: { message: errorMsg }
|
|
4752
|
+
});
|
|
4753
|
+
throw err;
|
|
4754
|
+
} finally {
|
|
4755
|
+
this.runs.delete(runId);
|
|
4756
|
+
}
|
|
4757
|
+
})();
|
|
4758
|
+
this.runs.set(runId, { runId, projectId, tenantId, abortController, promise: runPromise });
|
|
4759
|
+
runPromise.catch(() => {
|
|
4760
|
+
});
|
|
4761
|
+
return runId;
|
|
4762
|
+
}
|
|
4763
|
+
async abortRun(runId) {
|
|
4764
|
+
const ctx = this.runs.get(runId);
|
|
4765
|
+
if (!ctx) return false;
|
|
4766
|
+
ctx.abortController.abort();
|
|
4767
|
+
const store = this.getEvalStore();
|
|
4768
|
+
await store.updateRunStatus(ctx.tenantId, runId, { status: "aborted", completedAt: /* @__PURE__ */ new Date() });
|
|
4769
|
+
return true;
|
|
4770
|
+
}
|
|
4771
|
+
isRunning(runId) {
|
|
4772
|
+
return this.runs.has(runId);
|
|
4773
|
+
}
|
|
4774
|
+
getEvalStore() {
|
|
4775
|
+
return getStoreLattice9("default", "eval").store;
|
|
4776
|
+
}
|
|
4777
|
+
};
|
|
4778
|
+
var evalRunner = new EvalRunner();
|
|
4779
|
+
|
|
4780
|
+
// src/controllers/eval.ts
|
|
4781
|
+
function getTenantId10(request) {
|
|
4782
|
+
const userTenantId = request.user?.tenantId;
|
|
4783
|
+
if (userTenantId) {
|
|
4784
|
+
return userTenantId;
|
|
4785
|
+
}
|
|
4786
|
+
return request.headers["x-tenant-id"] || "default";
|
|
4787
|
+
}
|
|
4788
|
+
function getEvalStore() {
|
|
4789
|
+
return getStoreLattice10("default", "eval").store;
|
|
4790
|
+
}
|
|
4791
|
+
async function createProject(request, reply) {
|
|
4792
|
+
try {
|
|
4793
|
+
const tenantId = getTenantId10(request);
|
|
4794
|
+
const store = getEvalStore();
|
|
4795
|
+
const id = uuidv43();
|
|
4796
|
+
const data = request.body;
|
|
4797
|
+
const project = await store.createProject(tenantId, id, {
|
|
4798
|
+
name: data.name,
|
|
4799
|
+
description: data.description,
|
|
4800
|
+
version: data.version,
|
|
4801
|
+
judgeModelConfig: data.judgeModelConfig ?? {},
|
|
4802
|
+
targetServerConfig: data.targetServerConfig ?? {},
|
|
4803
|
+
concurrency: data.concurrency ?? 1,
|
|
4804
|
+
reportConfig: data.reportConfig
|
|
4805
|
+
});
|
|
4806
|
+
return reply.status(201).send({
|
|
4807
|
+
success: true,
|
|
4808
|
+
message: "Successfully created project",
|
|
4809
|
+
data: project
|
|
4810
|
+
});
|
|
4811
|
+
} catch (err) {
|
|
4812
|
+
const message = err instanceof Error ? err.message : "Failed to create project";
|
|
4813
|
+
return reply.status(500).send({ success: false, message });
|
|
4814
|
+
}
|
|
4815
|
+
}
|
|
4816
|
+
async function listProjects(request, reply) {
|
|
4817
|
+
try {
|
|
4818
|
+
const tenantId = getTenantId10(request);
|
|
4819
|
+
const store = getEvalStore();
|
|
4820
|
+
let projects = await store.getProjectsByTenant(tenantId);
|
|
4821
|
+
if (projects.length === 0) {
|
|
4822
|
+
try {
|
|
4823
|
+
const { modelLatticeManager: modelLatticeManager3 } = await import("@axiom-lattice/core");
|
|
4824
|
+
const models = modelLatticeManager3.getAllLattices();
|
|
4825
|
+
const first = models[0];
|
|
4826
|
+
const judgeModel = first ? { modelKey: first.key } : { provider: "openai", model: "gpt-4" };
|
|
4827
|
+
const host = request.hostname || "localhost";
|
|
4828
|
+
const baseUrl = `http://${host}:${process.env.PORT || 4001}`;
|
|
4829
|
+
await store.createProject(tenantId, uuidv43(), {
|
|
4830
|
+
name: "Default",
|
|
4831
|
+
description: "Built-in project for testing eval against this server",
|
|
4832
|
+
judgeModelConfig: judgeModel,
|
|
4833
|
+
targetServerConfig: { base_url: baseUrl, api_key: "" },
|
|
4834
|
+
concurrency: 3
|
|
4835
|
+
});
|
|
4836
|
+
projects = await store.getProjectsByTenant(tenantId);
|
|
4837
|
+
} catch {
|
|
4838
|
+
}
|
|
4839
|
+
}
|
|
4840
|
+
return {
|
|
4841
|
+
success: true,
|
|
4842
|
+
message: "Ok",
|
|
4843
|
+
data: { records: projects, total: projects.length }
|
|
4844
|
+
};
|
|
4845
|
+
} catch (err) {
|
|
4846
|
+
const message = err instanceof Error ? err.message : "Failed to list projects";
|
|
4847
|
+
return reply.status(500).send({ success: false, message });
|
|
4848
|
+
}
|
|
4849
|
+
}
|
|
4850
|
+
async function getProject(request, reply) {
|
|
4851
|
+
try {
|
|
4852
|
+
const tenantId = getTenantId10(request);
|
|
4853
|
+
const store = getEvalStore();
|
|
4854
|
+
const { pid } = request.params;
|
|
4855
|
+
const project = await store.getProjectById(tenantId, pid);
|
|
4856
|
+
if (!project) {
|
|
4857
|
+
return reply.status(404).send({ success: false, message: "Project not found" });
|
|
4858
|
+
}
|
|
4859
|
+
const suites = await store.getSuitesByProject(tenantId, pid);
|
|
4860
|
+
return {
|
|
4861
|
+
success: true,
|
|
4862
|
+
message: "Ok",
|
|
4863
|
+
data: { project, suites }
|
|
4864
|
+
};
|
|
4865
|
+
} catch (err) {
|
|
4866
|
+
const message = err instanceof Error ? err.message : "Failed to get project";
|
|
4867
|
+
return reply.status(500).send({ success: false, message });
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
async function updateProject(request, reply) {
|
|
4871
|
+
try {
|
|
4872
|
+
const tenantId = getTenantId10(request);
|
|
4873
|
+
const store = getEvalStore();
|
|
4874
|
+
const { pid } = request.params;
|
|
4875
|
+
const existing = await store.getProjectById(tenantId, pid);
|
|
4876
|
+
if (!existing) {
|
|
4877
|
+
return reply.status(404).send({ success: false, message: "Project not found" });
|
|
4878
|
+
}
|
|
4879
|
+
const updated = await store.updateProject(tenantId, pid, request.body);
|
|
4880
|
+
return {
|
|
4881
|
+
success: true,
|
|
4882
|
+
message: "Successfully updated project",
|
|
4883
|
+
data: updated
|
|
4884
|
+
};
|
|
4885
|
+
} catch (err) {
|
|
4886
|
+
const message = err instanceof Error ? err.message : "Failed to update project";
|
|
4887
|
+
return reply.status(500).send({ success: false, message });
|
|
4888
|
+
}
|
|
4889
|
+
}
|
|
4890
|
+
async function deleteProject(request, reply) {
|
|
4891
|
+
try {
|
|
4892
|
+
const tenantId = getTenantId10(request);
|
|
4893
|
+
const store = getEvalStore();
|
|
4894
|
+
const { pid } = request.params;
|
|
4895
|
+
const existing = await store.getProjectById(tenantId, pid);
|
|
4896
|
+
if (!existing) {
|
|
4897
|
+
return reply.status(404).send({ success: false, message: "Project not found" });
|
|
4898
|
+
}
|
|
4899
|
+
const deleted = await store.deleteProject(tenantId, pid);
|
|
4900
|
+
if (!deleted) {
|
|
4901
|
+
return reply.status(500).send({ success: false, message: "Failed to delete project" });
|
|
4902
|
+
}
|
|
4903
|
+
return { success: true, message: "Successfully deleted project" };
|
|
4904
|
+
} catch (err) {
|
|
4905
|
+
const message = err instanceof Error ? err.message : "Failed to delete project";
|
|
4906
|
+
return reply.status(500).send({ success: false, message });
|
|
4907
|
+
}
|
|
4908
|
+
}
|
|
4909
|
+
async function createSuite(request, reply) {
|
|
4910
|
+
try {
|
|
4911
|
+
const tenantId = getTenantId10(request);
|
|
4912
|
+
const store = getEvalStore();
|
|
4913
|
+
const { pid } = request.params;
|
|
4914
|
+
const project = await store.getProjectById(tenantId, pid);
|
|
4915
|
+
if (!project) {
|
|
4916
|
+
return reply.status(404).send({ success: false, message: "Project not found" });
|
|
4917
|
+
}
|
|
4918
|
+
const id = uuidv43();
|
|
4919
|
+
const data = request.body;
|
|
4920
|
+
const suite = await store.createSuite(tenantId, pid, id, { name: data.name });
|
|
4921
|
+
return reply.status(201).send({
|
|
4922
|
+
success: true,
|
|
4923
|
+
message: "Successfully created suite",
|
|
4924
|
+
data: suite
|
|
4925
|
+
});
|
|
4926
|
+
} catch (err) {
|
|
4927
|
+
const message = err instanceof Error ? err.message : "Failed to create suite";
|
|
4928
|
+
return reply.status(500).send({ success: false, message });
|
|
4929
|
+
}
|
|
4930
|
+
}
|
|
4931
|
+
async function updateSuite(request, reply) {
|
|
4932
|
+
try {
|
|
4933
|
+
const tenantId = getTenantId10(request);
|
|
4934
|
+
const store = getEvalStore();
|
|
4935
|
+
const { sid } = request.params;
|
|
4936
|
+
const existing = await store.getSuiteById(tenantId, sid);
|
|
4937
|
+
if (!existing) {
|
|
4938
|
+
return reply.status(404).send({ success: false, message: "Suite not found" });
|
|
4939
|
+
}
|
|
4940
|
+
const updated = await store.updateSuite(tenantId, sid, request.body);
|
|
4941
|
+
return {
|
|
4942
|
+
success: true,
|
|
4943
|
+
message: "Successfully updated suite",
|
|
4944
|
+
data: updated
|
|
4945
|
+
};
|
|
4946
|
+
} catch (err) {
|
|
4947
|
+
const message = err instanceof Error ? err.message : "Failed to update suite";
|
|
4948
|
+
return reply.status(500).send({ success: false, message });
|
|
4949
|
+
}
|
|
4950
|
+
}
|
|
4951
|
+
async function deleteSuite(request, reply) {
|
|
4952
|
+
try {
|
|
4953
|
+
const tenantId = getTenantId10(request);
|
|
4954
|
+
const store = getEvalStore();
|
|
4955
|
+
const { sid } = request.params;
|
|
4956
|
+
const existing = await store.getSuiteById(tenantId, sid);
|
|
4957
|
+
if (!existing) {
|
|
4958
|
+
return reply.status(404).send({ success: false, message: "Suite not found" });
|
|
4959
|
+
}
|
|
4960
|
+
const deleted = await store.deleteSuite(tenantId, sid);
|
|
4961
|
+
if (!deleted) {
|
|
4962
|
+
return reply.status(500).send({ success: false, message: "Failed to delete suite" });
|
|
4963
|
+
}
|
|
4964
|
+
return { success: true, message: "Successfully deleted suite" };
|
|
4965
|
+
} catch (err) {
|
|
4966
|
+
const message = err instanceof Error ? err.message : "Failed to delete suite";
|
|
4967
|
+
return reply.status(500).send({ success: false, message });
|
|
4968
|
+
}
|
|
4969
|
+
}
|
|
4970
|
+
async function createCase(request, reply) {
|
|
4971
|
+
try {
|
|
4972
|
+
const tenantId = getTenantId10(request);
|
|
4973
|
+
const store = getEvalStore();
|
|
4974
|
+
const { sid } = request.params;
|
|
4975
|
+
const suite = await store.getSuiteById(tenantId, sid);
|
|
4976
|
+
if (!suite) {
|
|
4977
|
+
return reply.status(404).send({ success: false, message: "Suite not found" });
|
|
4978
|
+
}
|
|
4979
|
+
const id = uuidv43();
|
|
4980
|
+
const data = request.body;
|
|
4981
|
+
const created = await store.createCase(tenantId, sid, id, {
|
|
4982
|
+
inputMessage: data.inputMessage,
|
|
4983
|
+
inputFiles: data.inputFiles,
|
|
4984
|
+
steps: data.steps ?? [],
|
|
4985
|
+
outputType: data.outputType ?? "message_content",
|
|
4986
|
+
contentAssertion: data.contentAssertion ?? "",
|
|
4987
|
+
rubrics: data.rubrics
|
|
4988
|
+
});
|
|
4989
|
+
return reply.status(201).send({
|
|
4990
|
+
success: true,
|
|
4991
|
+
message: "Successfully created case",
|
|
4992
|
+
data: created
|
|
4993
|
+
});
|
|
4994
|
+
} catch (err) {
|
|
4995
|
+
const message = err instanceof Error ? err.message : "Failed to create case";
|
|
4996
|
+
return reply.status(500).send({ success: false, message });
|
|
4997
|
+
}
|
|
4998
|
+
}
|
|
4999
|
+
async function listCasesForSuite(request, reply) {
|
|
5000
|
+
try {
|
|
5001
|
+
const tenantId = getTenantId10(request);
|
|
5002
|
+
const store = getEvalStore();
|
|
5003
|
+
const { sid } = request.params;
|
|
5004
|
+
const cases = await store.getCasesBySuite(tenantId, sid);
|
|
5005
|
+
return {
|
|
5006
|
+
success: true,
|
|
5007
|
+
message: "Ok",
|
|
5008
|
+
data: { records: cases, total: cases.length }
|
|
5009
|
+
};
|
|
5010
|
+
} catch (err) {
|
|
5011
|
+
const message = err instanceof Error ? err.message : "Failed to list cases";
|
|
5012
|
+
return reply.status(500).send({ success: false, message });
|
|
5013
|
+
}
|
|
5014
|
+
}
|
|
5015
|
+
async function updateCase(request, reply) {
|
|
5016
|
+
try {
|
|
5017
|
+
const tenantId = getTenantId10(request);
|
|
5018
|
+
const store = getEvalStore();
|
|
5019
|
+
const { cid } = request.params;
|
|
5020
|
+
const existing = await store.getCaseById(tenantId, cid);
|
|
5021
|
+
if (!existing) {
|
|
5022
|
+
return reply.status(404).send({ success: false, message: "Case not found" });
|
|
5023
|
+
}
|
|
5024
|
+
const updated = await store.updateCase(tenantId, cid, request.body);
|
|
5025
|
+
return {
|
|
5026
|
+
success: true,
|
|
5027
|
+
message: "Successfully updated case",
|
|
5028
|
+
data: updated
|
|
5029
|
+
};
|
|
5030
|
+
} catch (err) {
|
|
5031
|
+
const message = err instanceof Error ? err.message : "Failed to update case";
|
|
5032
|
+
return reply.status(500).send({ success: false, message });
|
|
5033
|
+
}
|
|
5034
|
+
}
|
|
5035
|
+
async function deleteCase(request, reply) {
|
|
5036
|
+
try {
|
|
5037
|
+
const tenantId = getTenantId10(request);
|
|
5038
|
+
const store = getEvalStore();
|
|
5039
|
+
const { cid } = request.params;
|
|
5040
|
+
const existing = await store.getCaseById(tenantId, cid);
|
|
5041
|
+
if (!existing) {
|
|
5042
|
+
return reply.status(404).send({ success: false, message: "Case not found" });
|
|
5043
|
+
}
|
|
5044
|
+
const deleted = await store.deleteCase(tenantId, cid);
|
|
5045
|
+
if (!deleted) {
|
|
5046
|
+
return reply.status(500).send({ success: false, message: "Failed to delete case" });
|
|
5047
|
+
}
|
|
5048
|
+
return { success: true, message: "Successfully deleted case" };
|
|
5049
|
+
} catch (err) {
|
|
5050
|
+
const message = err instanceof Error ? err.message : "Failed to delete case";
|
|
5051
|
+
return reply.status(500).send({ success: false, message });
|
|
5052
|
+
}
|
|
5053
|
+
}
|
|
5054
|
+
function registerEvalRoutes(app2) {
|
|
5055
|
+
app2.post("/api/eval/projects", createProject);
|
|
5056
|
+
app2.get("/api/eval/projects", listProjects);
|
|
5057
|
+
app2.get("/api/eval/projects/:pid", getProject);
|
|
5058
|
+
app2.put("/api/eval/projects/:pid", updateProject);
|
|
5059
|
+
app2.delete("/api/eval/projects/:pid", deleteProject);
|
|
5060
|
+
app2.post("/api/eval/projects/:pid/suites", createSuite);
|
|
5061
|
+
app2.put("/api/eval/projects/:pid/suites/:sid", updateSuite);
|
|
5062
|
+
app2.delete("/api/eval/projects/:pid/suites/:sid", deleteSuite);
|
|
5063
|
+
app2.post("/api/eval/projects/:pid/suites/:sid/cases", createCase);
|
|
5064
|
+
app2.get("/api/eval/projects/:pid/suites/:sid/cases", listCasesForSuite);
|
|
5065
|
+
app2.put("/api/eval/projects/:pid/suites/:sid/cases/:cid", updateCase);
|
|
5066
|
+
app2.delete("/api/eval/projects/:pid/suites/:sid/cases/:cid", deleteCase);
|
|
5067
|
+
app2.post("/api/eval/projects/:pid/runs", async (request, reply) => {
|
|
5068
|
+
try {
|
|
5069
|
+
const tenantId = getTenantId10(request);
|
|
5070
|
+
const { pid } = request.params;
|
|
5071
|
+
const runId = await evalRunner.startRun(tenantId, pid);
|
|
5072
|
+
reply.status(202).send({ success: true, message: "Run started", data: { run_id: runId } });
|
|
5073
|
+
} catch (err) {
|
|
5074
|
+
const msg = err.message;
|
|
5075
|
+
const code = msg === "Project not found" ? 404 : msg.includes("already in progress") ? 409 : 500;
|
|
5076
|
+
reply.status(code).send({ success: false, message: msg });
|
|
5077
|
+
}
|
|
5078
|
+
});
|
|
5079
|
+
app2.get("/api/eval/runs", async (request, reply) => {
|
|
5080
|
+
try {
|
|
5081
|
+
const tenantId = getTenantId10(request);
|
|
5082
|
+
const query = request.query;
|
|
5083
|
+
const runs = await getEvalStore().getRunsByTenant(tenantId, { projectId: query.project_id, status: query.status });
|
|
5084
|
+
reply.send({ success: true, message: "Ok", data: { records: runs, total: runs.length } });
|
|
5085
|
+
} catch (err) {
|
|
5086
|
+
reply.status(500).send({ success: false, message: err.message });
|
|
5087
|
+
}
|
|
5088
|
+
});
|
|
5089
|
+
app2.get("/api/eval/runs/:rid", async (request, reply) => {
|
|
5090
|
+
try {
|
|
5091
|
+
const tenantId = getTenantId10(request);
|
|
5092
|
+
const { rid } = request.params;
|
|
5093
|
+
const run = await getEvalStore().getRunById(tenantId, rid);
|
|
5094
|
+
if (!run) return reply.status(404).send({ success: false, message: "Run not found" });
|
|
5095
|
+
const results = await getEvalStore().getResultsByRun(tenantId, rid);
|
|
5096
|
+
reply.send({ success: true, message: "Ok", data: { ...run, results } });
|
|
5097
|
+
} catch (err) {
|
|
5098
|
+
reply.status(500).send({ success: false, message: err.message });
|
|
5099
|
+
}
|
|
5100
|
+
});
|
|
5101
|
+
app2.get("/api/eval/runs/:rid/stream", async (request, reply) => {
|
|
5102
|
+
reply.hijack();
|
|
5103
|
+
reply.raw.writeHead(200, {
|
|
5104
|
+
"Content-Type": "text/event-stream",
|
|
5105
|
+
"Cache-Control": "no-cache",
|
|
5106
|
+
Connection: "keep-alive",
|
|
5107
|
+
"Access-Control-Allow-Origin": "*"
|
|
5108
|
+
});
|
|
5109
|
+
const { rid } = request.params;
|
|
5110
|
+
const emitter = evalRunner.getEventEmitter();
|
|
5111
|
+
const eventKey = `run:${rid}`;
|
|
5112
|
+
if (!evalRunner.isRunning(rid)) {
|
|
5113
|
+
const tenantId = getTenantId10(request);
|
|
5114
|
+
const run = await getEvalStore().getRunById(tenantId, rid);
|
|
5115
|
+
if (run) {
|
|
5116
|
+
const eventType = run.status === "completed" ? "completed" : "error";
|
|
5117
|
+
reply.raw.write(`event: ${eventType}
|
|
5118
|
+
data: ${JSON.stringify({ passed: run.passedCases, failed: run.failedCases, avgScore: run.avgScore })}
|
|
5119
|
+
|
|
5120
|
+
`);
|
|
5121
|
+
}
|
|
5122
|
+
reply.raw.end();
|
|
5123
|
+
return;
|
|
5124
|
+
}
|
|
5125
|
+
const handler = (event) => {
|
|
5126
|
+
reply.raw.write(`event: ${event.type}
|
|
5127
|
+
data: ${JSON.stringify(event.data)}
|
|
5128
|
+
|
|
5129
|
+
`);
|
|
5130
|
+
if (event.type === "completed" || event.type === "error") {
|
|
5131
|
+
emitter.off(eventKey, handler);
|
|
5132
|
+
reply.raw.end();
|
|
5133
|
+
}
|
|
5134
|
+
};
|
|
5135
|
+
emitter.on(eventKey, handler);
|
|
5136
|
+
request.raw.on("close", () => {
|
|
5137
|
+
emitter.off(eventKey, handler);
|
|
5138
|
+
});
|
|
5139
|
+
});
|
|
5140
|
+
app2.post("/api/eval/runs/:rid/abort", async (request, reply) => {
|
|
5141
|
+
try {
|
|
5142
|
+
const { rid } = request.params;
|
|
5143
|
+
const aborted = await evalRunner.abortRun(rid);
|
|
5144
|
+
if (!aborted) return reply.status(404).send({ success: false, message: "Run not found or not running" });
|
|
5145
|
+
reply.send({ success: true, message: "Run aborted" });
|
|
5146
|
+
} catch (err) {
|
|
5147
|
+
reply.status(500).send({ success: false, message: err.message });
|
|
5148
|
+
}
|
|
5149
|
+
});
|
|
5150
|
+
app2.delete("/api/eval/runs/:rid", async (request, reply) => {
|
|
5151
|
+
try {
|
|
5152
|
+
const tenantId = getTenantId10(request);
|
|
5153
|
+
const { rid } = request.params;
|
|
5154
|
+
const deleted = await getEvalStore().deleteRun(tenantId, rid);
|
|
5155
|
+
if (!deleted) return reply.status(404).send({ success: false, message: "Run not found" });
|
|
5156
|
+
reply.send({ success: true, message: "Run deleted" });
|
|
5157
|
+
} catch (err) {
|
|
5158
|
+
reply.status(500).send({ success: false, message: err.message });
|
|
5159
|
+
}
|
|
5160
|
+
});
|
|
5161
|
+
app2.get("/api/eval/reports/projects/:pid", async (request, reply) => {
|
|
5162
|
+
try {
|
|
5163
|
+
const tenantId = getTenantId10(request);
|
|
5164
|
+
const { pid } = request.params;
|
|
5165
|
+
const report = await getEvalStore().getProjectReport(tenantId, pid);
|
|
5166
|
+
if (!report) return reply.status(404).send({ success: false, message: "Project not found" });
|
|
5167
|
+
reply.send({ success: true, message: "Ok", data: report });
|
|
5168
|
+
} catch (err) {
|
|
5169
|
+
reply.status(500).send({ success: false, message: err.message });
|
|
5170
|
+
}
|
|
5171
|
+
});
|
|
5172
|
+
}
|
|
5173
|
+
|
|
5174
|
+
// src/controllers/users.ts
|
|
5175
|
+
import { getStoreLattice as getStoreLattice11 } from "@axiom-lattice/core";
|
|
5176
|
+
import { v4 as uuidv44 } from "uuid";
|
|
4598
5177
|
var UsersController = class {
|
|
4599
5178
|
constructor() {
|
|
4600
|
-
this.userStore =
|
|
5179
|
+
this.userStore = getStoreLattice11("default", "user").store;
|
|
4601
5180
|
}
|
|
4602
5181
|
async listUsers(request, reply) {
|
|
4603
5182
|
const { email } = request.query;
|
|
@@ -4610,7 +5189,7 @@ var UsersController = class {
|
|
|
4610
5189
|
}
|
|
4611
5190
|
async createUser(request, reply) {
|
|
4612
5191
|
const data = request.body;
|
|
4613
|
-
const id =
|
|
5192
|
+
const id = uuidv44();
|
|
4614
5193
|
const existingUser = await this.userStore.getUserByEmail(data.email);
|
|
4615
5194
|
if (existingUser) {
|
|
4616
5195
|
return reply.status(409).send({
|
|
@@ -4675,11 +5254,11 @@ function registerUserRoutes(app2) {
|
|
|
4675
5254
|
}
|
|
4676
5255
|
|
|
4677
5256
|
// src/controllers/tenants.ts
|
|
4678
|
-
import { getStoreLattice as
|
|
4679
|
-
import { v4 as
|
|
5257
|
+
import { getStoreLattice as getStoreLattice12 } from "@axiom-lattice/core";
|
|
5258
|
+
import { v4 as uuidv45 } from "uuid";
|
|
4680
5259
|
var TenantsController = class {
|
|
4681
5260
|
constructor() {
|
|
4682
|
-
this.tenantStore =
|
|
5261
|
+
this.tenantStore = getStoreLattice12("default", "tenant").store;
|
|
4683
5262
|
}
|
|
4684
5263
|
// ==================== Tenant CRUD ====================
|
|
4685
5264
|
async listTenants(request, reply) {
|
|
@@ -4688,7 +5267,7 @@ var TenantsController = class {
|
|
|
4688
5267
|
}
|
|
4689
5268
|
async createTenant(request, reply) {
|
|
4690
5269
|
const data = request.body;
|
|
4691
|
-
const id =
|
|
5270
|
+
const id = uuidv45();
|
|
4692
5271
|
const tenant = await this.tenantStore.createTenant(id, data);
|
|
4693
5272
|
return reply.status(201).send({ success: true, data: tenant });
|
|
4694
5273
|
}
|
|
@@ -4743,8 +5322,8 @@ function registerTenantRoutes(app2) {
|
|
|
4743
5322
|
}
|
|
4744
5323
|
|
|
4745
5324
|
// src/controllers/auth.ts
|
|
4746
|
-
import { getStoreLattice as
|
|
4747
|
-
import { v4 as
|
|
5325
|
+
import { getStoreLattice as getStoreLattice13 } from "@axiom-lattice/core";
|
|
5326
|
+
import { v4 as uuidv46 } from "uuid";
|
|
4748
5327
|
var defaultAuthConfig = {
|
|
4749
5328
|
autoApproveUsers: true,
|
|
4750
5329
|
allowTenantRegistration: true,
|
|
@@ -4752,9 +5331,9 @@ var defaultAuthConfig = {
|
|
|
4752
5331
|
};
|
|
4753
5332
|
var AuthController = class {
|
|
4754
5333
|
constructor(config = {}) {
|
|
4755
|
-
this.userStore =
|
|
4756
|
-
this.tenantStore =
|
|
4757
|
-
this.userTenantLinkStore =
|
|
5334
|
+
this.userStore = getStoreLattice13("default", "user").store;
|
|
5335
|
+
this.tenantStore = getStoreLattice13("default", "tenant").store;
|
|
5336
|
+
this.userTenantLinkStore = getStoreLattice13("default", "userTenantLink").store;
|
|
4758
5337
|
this.config = { ...defaultAuthConfig, ...config };
|
|
4759
5338
|
}
|
|
4760
5339
|
async register(request, reply) {
|
|
@@ -4767,7 +5346,7 @@ var AuthController = class {
|
|
|
4767
5346
|
error: "User with this email already exists"
|
|
4768
5347
|
});
|
|
4769
5348
|
}
|
|
4770
|
-
const userId =
|
|
5349
|
+
const userId = uuidv46();
|
|
4771
5350
|
const userStatus = this.config.autoApproveUsers ? "active" : "pending";
|
|
4772
5351
|
const userData = {
|
|
4773
5352
|
email,
|
|
@@ -5122,7 +5701,7 @@ function registerAuthRoutes(app2, config) {
|
|
|
5122
5701
|
}
|
|
5123
5702
|
|
|
5124
5703
|
// src/channels/lark/routes.ts
|
|
5125
|
-
import { getStoreLattice as
|
|
5704
|
+
import { getStoreLattice as getStoreLattice14 } from "@axiom-lattice/core";
|
|
5126
5705
|
import {
|
|
5127
5706
|
ChannelIdentityMappingStore,
|
|
5128
5707
|
PostgreSQLChannelInstallationStore
|
|
@@ -5498,7 +6077,7 @@ function createDefaultLarkDependencies() {
|
|
|
5498
6077
|
const installationStore = new PostgreSQLChannelInstallationStore({
|
|
5499
6078
|
poolConfig: getDatabaseUrl()
|
|
5500
6079
|
});
|
|
5501
|
-
const threadStore =
|
|
6080
|
+
const threadStore = getStoreLattice14("default", "thread").store;
|
|
5502
6081
|
const mappingStore = new ChannelIdentityMappingStore({
|
|
5503
6082
|
poolConfig: getDatabaseUrl()
|
|
5504
6083
|
});
|
|
@@ -5579,7 +6158,7 @@ function registerChannelRoutes(app2, dependencies = {}) {
|
|
|
5579
6158
|
|
|
5580
6159
|
// src/controllers/channel-installations.ts
|
|
5581
6160
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
5582
|
-
function
|
|
6161
|
+
function getTenantId11(request) {
|
|
5583
6162
|
const userTenantId = request.user?.tenantId;
|
|
5584
6163
|
if (userTenantId) {
|
|
5585
6164
|
return userTenantId;
|
|
@@ -5597,7 +6176,7 @@ async function getInstallationStore() {
|
|
|
5597
6176
|
});
|
|
5598
6177
|
}
|
|
5599
6178
|
async function getChannelInstallationList(request, reply) {
|
|
5600
|
-
const tenantId =
|
|
6179
|
+
const tenantId = getTenantId11(request);
|
|
5601
6180
|
const { channel } = request.query;
|
|
5602
6181
|
try {
|
|
5603
6182
|
const store = await getInstallationStore();
|
|
@@ -5623,7 +6202,7 @@ async function getChannelInstallationList(request, reply) {
|
|
|
5623
6202
|
}
|
|
5624
6203
|
}
|
|
5625
6204
|
async function getChannelInstallation(request, reply) {
|
|
5626
|
-
const tenantId =
|
|
6205
|
+
const tenantId = getTenantId11(request);
|
|
5627
6206
|
const { installationId } = request.params;
|
|
5628
6207
|
try {
|
|
5629
6208
|
const store = await getInstallationStore();
|
|
@@ -5656,7 +6235,7 @@ async function getChannelInstallation(request, reply) {
|
|
|
5656
6235
|
}
|
|
5657
6236
|
}
|
|
5658
6237
|
async function createChannelInstallation(request, reply) {
|
|
5659
|
-
const tenantId =
|
|
6238
|
+
const tenantId = getTenantId11(request);
|
|
5660
6239
|
const body = request.body;
|
|
5661
6240
|
try {
|
|
5662
6241
|
if (!body.channel) {
|
|
@@ -5719,7 +6298,7 @@ async function createChannelInstallation(request, reply) {
|
|
|
5719
6298
|
}
|
|
5720
6299
|
}
|
|
5721
6300
|
async function updateChannelInstallation(request, reply) {
|
|
5722
|
-
const tenantId =
|
|
6301
|
+
const tenantId = getTenantId11(request);
|
|
5723
6302
|
const { installationId } = request.params;
|
|
5724
6303
|
const body = request.body;
|
|
5725
6304
|
try {
|
|
@@ -5765,7 +6344,7 @@ async function updateChannelInstallation(request, reply) {
|
|
|
5765
6344
|
}
|
|
5766
6345
|
}
|
|
5767
6346
|
async function deleteChannelInstallation(request, reply) {
|
|
5768
|
-
const tenantId =
|
|
6347
|
+
const tenantId = getTenantId11(request);
|
|
5769
6348
|
const { installationId } = request.params;
|
|
5770
6349
|
try {
|
|
5771
6350
|
const store = await getInstallationStore();
|
|
@@ -5934,6 +6513,7 @@ var registerLatticeRoutes = (app2) => {
|
|
|
5934
6513
|
filterSkillsByLicense
|
|
5935
6514
|
);
|
|
5936
6515
|
registerSandboxProxyRoutes(app2);
|
|
6516
|
+
registerEvalRoutes(app2);
|
|
5937
6517
|
registerWorkspaceRoutes(app2);
|
|
5938
6518
|
registerDatabaseConfigRoutes(app2);
|
|
5939
6519
|
registerMetricsServerConfigRoutes(app2);
|
|
@@ -6308,7 +6888,7 @@ import {
|
|
|
6308
6888
|
loggerLatticeManager,
|
|
6309
6889
|
sandboxLatticeManager as sandboxLatticeManager2,
|
|
6310
6890
|
sqlDatabaseManager as sqlDatabaseManager2,
|
|
6311
|
-
getStoreLattice as
|
|
6891
|
+
getStoreLattice as getStoreLattice15,
|
|
6312
6892
|
agentInstanceManager as agentInstanceManager8,
|
|
6313
6893
|
createSandboxProvider
|
|
6314
6894
|
} from "@axiom-lattice/core";
|
|
@@ -6464,7 +7044,7 @@ var start = async (config) => {
|
|
|
6464
7044
|
app.decorate("loggerLattice", loggerLattice);
|
|
6465
7045
|
registerLatticeRoutes(app);
|
|
6466
7046
|
try {
|
|
6467
|
-
const storeLattice =
|
|
7047
|
+
const storeLattice = getStoreLattice15("default", "database");
|
|
6468
7048
|
const store = storeLattice.store;
|
|
6469
7049
|
sqlDatabaseManager2.setConfigStore(store);
|
|
6470
7050
|
logger.info("Database config store set for SqlDatabaseManager");
|