@axiom-lattice/pg-stores 1.0.53 → 1.0.55

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.js CHANGED
@@ -32,10 +32,11 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ChannelIdentityMappingStore: () => ChannelIdentityMappingStore,
34
34
  MigrationManager: () => MigrationManager,
35
- Pool: () => import_pg16.Pool,
35
+ Pool: () => import_pg17.Pool,
36
36
  PostgreSQLAssistantStore: () => PostgreSQLAssistantStore,
37
37
  PostgreSQLChannelInstallationStore: () => PostgreSQLChannelInstallationStore,
38
38
  PostgreSQLDatabaseConfigStore: () => PostgreSQLDatabaseConfigStore,
39
+ PostgreSQLEvalStore: () => PostgreSQLEvalStore,
39
40
  PostgreSQLMcpServerConfigStore: () => PostgreSQLMcpServerConfigStore,
40
41
  PostgreSQLMetricsServerConfigStore: () => PostgreSQLMetricsServerConfigStore,
41
42
  PostgreSQLProjectStore: () => PostgreSQLProjectStore,
@@ -59,6 +60,11 @@ __export(index_exports, {
59
60
  createChannelIdentityMappingTables: () => createChannelIdentityMappingTables,
60
61
  createChannelInstallationsTable: () => createChannelInstallationsTable,
61
62
  createDatabaseConfigsTable: () => createDatabaseConfigsTable,
63
+ createEvalCasesTable: () => createEvalCasesTable,
64
+ createEvalProjectsTable: () => createEvalProjectsTable,
65
+ createEvalRunResultsTable: () => createEvalRunResultsTable,
66
+ createEvalRunsTable: () => createEvalRunsTable,
67
+ createEvalSuitesTable: () => createEvalSuitesTable,
62
68
  createMcpServerConfigsTable: () => createMcpServerConfigsTable,
63
69
  createMetricsConfigsTable: () => createMetricsConfigsTable,
64
70
  createProjectsTable: () => createProjectsTable,
@@ -70,10 +76,11 @@ __export(index_exports, {
70
76
  createUsersTable: () => createUsersTable,
71
77
  createWorkflowTrackingTables: () => createWorkflowTrackingTables,
72
78
  createWorkspacesTable: () => createWorkspacesTable,
79
+ evalMigrations: () => evalMigrations,
73
80
  getThreadMessageQueueStore: () => getThreadMessageQueueStore
74
81
  });
75
82
  module.exports = __toCommonJS(index_exports);
76
- var import_pg16 = require("pg");
83
+ var import_pg17 = require("pg");
77
84
 
78
85
  // src/stores/PostgreSQLThreadStore.ts
79
86
  var import_pg = require("pg");
@@ -5213,6 +5220,927 @@ var PostgreSQLWorkflowTrackingStore = class {
5213
5220
  };
5214
5221
  }
5215
5222
  };
5223
+
5224
+ // src/stores/PostgreSQLEvalStore.ts
5225
+ var import_pg16 = require("pg");
5226
+
5227
+ // src/migrations/eval_migrations.ts
5228
+ var createEvalProjectsTable = {
5229
+ version: 104,
5230
+ name: "create_eval_projects_table",
5231
+ up: async (client) => {
5232
+ await client.query(`
5233
+ CREATE TABLE IF NOT EXISTS lattice_eval_projects (
5234
+ id UUID PRIMARY KEY,
5235
+ tenant_id VARCHAR(255) NOT NULL,
5236
+ name TEXT NOT NULL,
5237
+ description TEXT,
5238
+ version TEXT,
5239
+ judge_model_config JSONB NOT NULL DEFAULT '{}',
5240
+ target_server_config JSONB NOT NULL DEFAULT '{}',
5241
+ concurrency INTEGER NOT NULL DEFAULT 3,
5242
+ report_config JSONB DEFAULT '{}',
5243
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
5244
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
5245
+ )
5246
+ `);
5247
+ await client.query(`
5248
+ CREATE INDEX IF NOT EXISTS idx_lattice_eval_projects_tenant_id
5249
+ ON lattice_eval_projects(tenant_id)
5250
+ `);
5251
+ await client.query(`
5252
+ CREATE INDEX IF NOT EXISTS idx_lattice_eval_projects_created_at
5253
+ ON lattice_eval_projects(created_at)
5254
+ `);
5255
+ },
5256
+ down: async (client) => {
5257
+ await client.query("DROP INDEX IF EXISTS idx_lattice_eval_projects_created_at");
5258
+ await client.query("DROP INDEX IF EXISTS idx_lattice_eval_projects_tenant_id");
5259
+ await client.query("DROP TABLE IF EXISTS lattice_eval_projects");
5260
+ }
5261
+ };
5262
+ var createEvalSuitesTable = {
5263
+ version: 105,
5264
+ name: "create_eval_suites_table",
5265
+ up: async (client) => {
5266
+ await client.query(`
5267
+ CREATE TABLE IF NOT EXISTS lattice_eval_suites (
5268
+ id UUID PRIMARY KEY,
5269
+ tenant_id VARCHAR(255) NOT NULL,
5270
+ project_id UUID NOT NULL REFERENCES lattice_eval_projects(id) ON DELETE CASCADE,
5271
+ name TEXT NOT NULL,
5272
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
5273
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
5274
+ UNIQUE(project_id, name)
5275
+ )
5276
+ `);
5277
+ await client.query(`
5278
+ CREATE INDEX IF NOT EXISTS idx_lattice_eval_suites_tenant_id
5279
+ ON lattice_eval_suites(tenant_id)
5280
+ `);
5281
+ await client.query(`
5282
+ CREATE INDEX IF NOT EXISTS idx_lattice_eval_suites_project_id
5283
+ ON lattice_eval_suites(project_id)
5284
+ `);
5285
+ },
5286
+ down: async (client) => {
5287
+ await client.query("DROP INDEX IF EXISTS idx_lattice_eval_suites_project_id");
5288
+ await client.query("DROP INDEX IF EXISTS idx_lattice_eval_suites_tenant_id");
5289
+ await client.query("DROP TABLE IF EXISTS lattice_eval_suites");
5290
+ }
5291
+ };
5292
+ var createEvalCasesTable = {
5293
+ version: 106,
5294
+ name: "create_eval_cases_table",
5295
+ up: async (client) => {
5296
+ await client.query(`
5297
+ CREATE TABLE IF NOT EXISTS lattice_eval_cases (
5298
+ id UUID PRIMARY KEY,
5299
+ tenant_id VARCHAR(255) NOT NULL,
5300
+ suite_id UUID NOT NULL REFERENCES lattice_eval_suites(id) ON DELETE CASCADE,
5301
+ template_id UUID,
5302
+ input_message TEXT NOT NULL,
5303
+ input_files JSONB DEFAULT '{}',
5304
+ steps JSONB NOT NULL DEFAULT '[]',
5305
+ output_type TEXT NOT NULL DEFAULT 'message_content',
5306
+ content_assertion TEXT,
5307
+ rubrics JSONB DEFAULT '[]',
5308
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
5309
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
5310
+ )
5311
+ `);
5312
+ await client.query(`
5313
+ CREATE INDEX IF NOT EXISTS idx_lattice_eval_cases_tenant_id
5314
+ ON lattice_eval_cases(tenant_id)
5315
+ `);
5316
+ await client.query(`
5317
+ CREATE INDEX IF NOT EXISTS idx_lattice_eval_cases_suite_id
5318
+ ON lattice_eval_cases(suite_id)
5319
+ `);
5320
+ },
5321
+ down: async (client) => {
5322
+ await client.query("DROP INDEX IF EXISTS idx_lattice_eval_cases_suite_id");
5323
+ await client.query("DROP INDEX IF EXISTS idx_lattice_eval_cases_tenant_id");
5324
+ await client.query("DROP TABLE IF EXISTS lattice_eval_cases");
5325
+ }
5326
+ };
5327
+ var createEvalRunsTable = {
5328
+ version: 107,
5329
+ name: "create_eval_runs_table",
5330
+ up: async (client) => {
5331
+ await client.query(`
5332
+ CREATE TABLE IF NOT EXISTS lattice_eval_runs (
5333
+ id UUID PRIMARY KEY,
5334
+ project_id UUID NOT NULL REFERENCES lattice_eval_projects(id) ON DELETE CASCADE,
5335
+ tenant_id VARCHAR(255) NOT NULL,
5336
+ status TEXT NOT NULL DEFAULT 'running',
5337
+ concurrency INTEGER NOT NULL DEFAULT 3,
5338
+ total_cases INTEGER NOT NULL DEFAULT 0,
5339
+ passed_cases INTEGER NOT NULL DEFAULT 0,
5340
+ failed_cases INTEGER NOT NULL DEFAULT 0,
5341
+ avg_score FLOAT NOT NULL DEFAULT 0,
5342
+ error TEXT,
5343
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
5344
+ started_at TIMESTAMPTZ,
5345
+ completed_at TIMESTAMPTZ
5346
+ )
5347
+ `);
5348
+ await client.query(`
5349
+ CREATE INDEX IF NOT EXISTS idx_lattice_eval_runs_tenant_id
5350
+ ON lattice_eval_runs(tenant_id)
5351
+ `);
5352
+ await client.query(`
5353
+ CREATE INDEX IF NOT EXISTS idx_lattice_eval_runs_project_id
5354
+ ON lattice_eval_runs(project_id)
5355
+ `);
5356
+ await client.query(`
5357
+ CREATE INDEX IF NOT EXISTS idx_lattice_eval_runs_status
5358
+ ON lattice_eval_runs(status)
5359
+ `);
5360
+ await client.query(`
5361
+ CREATE INDEX IF NOT EXISTS idx_lattice_eval_runs_created_at
5362
+ ON lattice_eval_runs(created_at)
5363
+ `);
5364
+ },
5365
+ down: async (client) => {
5366
+ await client.query("DROP INDEX IF EXISTS idx_lattice_eval_runs_created_at");
5367
+ await client.query("DROP INDEX IF EXISTS idx_lattice_eval_runs_status");
5368
+ await client.query("DROP INDEX IF EXISTS idx_lattice_eval_runs_project_id");
5369
+ await client.query("DROP INDEX IF EXISTS idx_lattice_eval_runs_tenant_id");
5370
+ await client.query("DROP TABLE IF EXISTS lattice_eval_runs");
5371
+ }
5372
+ };
5373
+ var createEvalRunResultsTable = {
5374
+ version: 108,
5375
+ name: "create_eval_run_results_table",
5376
+ up: async (client) => {
5377
+ await client.query(`
5378
+ CREATE TABLE IF NOT EXISTS lattice_eval_run_results (
5379
+ id UUID PRIMARY KEY,
5380
+ run_id UUID NOT NULL REFERENCES lattice_eval_runs(id) ON DELETE CASCADE,
5381
+ suite_name TEXT NOT NULL DEFAULT '',
5382
+ case_id UUID,
5383
+ pass BOOLEAN NOT NULL DEFAULT false,
5384
+ score FLOAT NOT NULL DEFAULT 0,
5385
+ summary TEXT,
5386
+ dimension_results JSONB DEFAULT '[]',
5387
+ duration_ms INTEGER DEFAULT 0,
5388
+ messages JSONB DEFAULT '[]',
5389
+ logs JSONB DEFAULT '[]',
5390
+ error TEXT,
5391
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
5392
+ )
5393
+ `);
5394
+ await client.query(`
5395
+ CREATE INDEX IF NOT EXISTS idx_lattice_eval_run_results_run_id
5396
+ ON lattice_eval_run_results(run_id)
5397
+ `);
5398
+ await client.query(`
5399
+ CREATE INDEX IF NOT EXISTS idx_lattice_eval_run_results_case_id
5400
+ ON lattice_eval_run_results(case_id)
5401
+ `);
5402
+ },
5403
+ down: async (client) => {
5404
+ await client.query("DROP INDEX IF EXISTS idx_lattice_eval_run_results_case_id");
5405
+ await client.query("DROP INDEX IF EXISTS idx_lattice_eval_run_results_run_id");
5406
+ await client.query("DROP TABLE IF EXISTS lattice_eval_run_results");
5407
+ }
5408
+ };
5409
+ var evalMigrations = [
5410
+ createEvalProjectsTable,
5411
+ createEvalSuitesTable,
5412
+ createEvalCasesTable,
5413
+ createEvalRunsTable,
5414
+ createEvalRunResultsTable
5415
+ ];
5416
+
5417
+ // src/stores/PostgreSQLEvalStore.ts
5418
+ var import_uuid = require("uuid");
5419
+ var PostgreSQLEvalStore = class {
5420
+ constructor(options) {
5421
+ this.initialized = false;
5422
+ this.ownsPool = true;
5423
+ this.initPromise = null;
5424
+ if (typeof options.poolConfig === "string") {
5425
+ this.pool = new import_pg16.Pool({ connectionString: options.poolConfig });
5426
+ } else {
5427
+ this.pool = new import_pg16.Pool(options.poolConfig);
5428
+ }
5429
+ this.migrationManager = new MigrationManager(this.pool);
5430
+ for (const m of evalMigrations) {
5431
+ this.migrationManager.register(m);
5432
+ }
5433
+ if (options.autoMigrate !== false) {
5434
+ this.initialize().catch((error) => {
5435
+ console.error("Failed to initialize PostgreSQLEvalStore:", error);
5436
+ throw error;
5437
+ });
5438
+ }
5439
+ }
5440
+ /** Dispose resources and close the connection pool */
5441
+ async dispose() {
5442
+ if (this.ownsPool && this.pool) {
5443
+ await this.pool.end();
5444
+ }
5445
+ }
5446
+ /**
5447
+ * Initialize the store and run migrations
5448
+ * Uses a promise-based lock to prevent concurrent initialization
5449
+ */
5450
+ async initialize() {
5451
+ if (this.initialized) {
5452
+ return;
5453
+ }
5454
+ if (this.initPromise) {
5455
+ return this.initPromise;
5456
+ }
5457
+ this.initPromise = (async () => {
5458
+ try {
5459
+ await this.migrationManager.migrate();
5460
+ this.initialized = true;
5461
+ } finally {
5462
+ this.initPromise = null;
5463
+ }
5464
+ })();
5465
+ return this.initPromise;
5466
+ }
5467
+ /** Ensure store is initialized */
5468
+ async ensureInitialized() {
5469
+ if (!this.initialized) {
5470
+ await this.initialize();
5471
+ }
5472
+ }
5473
+ // ---------------------------------------------------------------------------
5474
+ // Row mappers
5475
+ // ---------------------------------------------------------------------------
5476
+ mapRowToProject(row) {
5477
+ return {
5478
+ id: row.id,
5479
+ tenantId: row.tenant_id,
5480
+ name: row.name,
5481
+ description: row.description,
5482
+ version: row.version,
5483
+ judgeModelConfig: this.parseRequiredJson(row.judge_model_config, {}),
5484
+ targetServerConfig: this.parseRequiredJson(row.target_server_config, {}),
5485
+ concurrency: row.concurrency ?? 3,
5486
+ reportConfig: this.parseOptionalJson(row.report_config),
5487
+ createdAt: new Date(row.created_at),
5488
+ updatedAt: new Date(row.updated_at)
5489
+ };
5490
+ }
5491
+ mapRowToSuite(row) {
5492
+ return {
5493
+ id: row.id,
5494
+ tenantId: row.tenant_id,
5495
+ projectId: row.project_id,
5496
+ name: row.name,
5497
+ createdAt: new Date(row.created_at),
5498
+ updatedAt: new Date(row.updated_at),
5499
+ caseCount: row.case_count !== void 0 ? row.case_count : void 0
5500
+ };
5501
+ }
5502
+ mapRowToCase(row) {
5503
+ return {
5504
+ id: row.id,
5505
+ tenantId: row.tenant_id,
5506
+ suiteId: row.suite_id,
5507
+ inputMessage: row.input_message,
5508
+ inputFiles: this.parseOptionalJson(row.input_files),
5509
+ steps: this.parseRequiredJson(row.steps, []),
5510
+ outputType: row.output_type || "message_content",
5511
+ contentAssertion: row.content_assertion || "",
5512
+ rubrics: this.parseOptionalJson(row.rubrics),
5513
+ createdAt: new Date(row.created_at),
5514
+ updatedAt: new Date(row.updated_at)
5515
+ };
5516
+ }
5517
+ mapRowToRun(row) {
5518
+ return {
5519
+ id: row.id,
5520
+ projectId: row.project_id,
5521
+ tenantId: row.tenant_id,
5522
+ status: row.status,
5523
+ concurrency: row.concurrency ?? 3,
5524
+ totalCases: row.total_cases ?? 0,
5525
+ passedCases: row.passed_cases ?? 0,
5526
+ failedCases: row.failed_cases ?? 0,
5527
+ avgScore: row.avg_score ?? 0,
5528
+ error: row.error,
5529
+ createdAt: new Date(row.created_at),
5530
+ startedAt: row.started_at ? new Date(row.started_at) : void 0,
5531
+ completedAt: row.completed_at ? new Date(row.completed_at) : void 0
5532
+ };
5533
+ }
5534
+ mapRowToRunResult(row) {
5535
+ return {
5536
+ id: row.id,
5537
+ runId: row.run_id,
5538
+ suiteName: row.suite_name,
5539
+ caseId: row.case_id,
5540
+ pass: row.pass ?? false,
5541
+ score: row.score ?? 0,
5542
+ summary: row.summary,
5543
+ dimensionResults: this.parseOptionalJson(row.dimension_results),
5544
+ durationMs: row.duration_ms,
5545
+ messages: this.parseOptionalJson(row.messages),
5546
+ logs: this.parseOptionalJson(row.logs),
5547
+ error: row.error,
5548
+ createdAt: new Date(row.created_at)
5549
+ };
5550
+ }
5551
+ parseRequiredJson(val, fallback) {
5552
+ if (val == null) return fallback;
5553
+ if (typeof val === "string") {
5554
+ try {
5555
+ return JSON.parse(val);
5556
+ } catch {
5557
+ return fallback;
5558
+ }
5559
+ }
5560
+ return val;
5561
+ }
5562
+ parseOptionalJson(val) {
5563
+ if (val == null) return void 0;
5564
+ if (typeof val === "string") {
5565
+ try {
5566
+ return JSON.parse(val);
5567
+ } catch {
5568
+ return void 0;
5569
+ }
5570
+ }
5571
+ return val;
5572
+ }
5573
+ // ---------------------------------------------------------------------------
5574
+ // Projects
5575
+ // ---------------------------------------------------------------------------
5576
+ /** Get all eval projects for a tenant */
5577
+ async getProjectsByTenant(tenantId) {
5578
+ await this.ensureInitialized();
5579
+ const { rows } = await this.pool.query(
5580
+ `SELECT id, tenant_id, name, description, version,
5581
+ judge_model_config, target_server_config, concurrency,
5582
+ report_config, created_at, updated_at
5583
+ FROM lattice_eval_projects
5584
+ WHERE tenant_id = $1
5585
+ ORDER BY created_at DESC`,
5586
+ [tenantId]
5587
+ );
5588
+ return rows.map((r) => this.mapRowToProject(r));
5589
+ }
5590
+ /** Get a single eval project by ID for a tenant */
5591
+ async getProjectById(tenantId, id) {
5592
+ await this.ensureInitialized();
5593
+ const { rows } = await this.pool.query(
5594
+ `SELECT id, tenant_id, name, description, version,
5595
+ judge_model_config, target_server_config, concurrency,
5596
+ report_config, created_at, updated_at
5597
+ FROM lattice_eval_projects
5598
+ WHERE id = $1 AND tenant_id = $2`,
5599
+ [id, tenantId]
5600
+ );
5601
+ return rows.length ? this.mapRowToProject(rows[0]) : null;
5602
+ }
5603
+ /** Create a new eval project */
5604
+ async createProject(tenantId, id, data) {
5605
+ await this.ensureInitialized();
5606
+ const actualId = id || (0, import_uuid.v4)();
5607
+ const { rows } = await this.pool.query(
5608
+ `INSERT INTO lattice_eval_projects
5609
+ (id, tenant_id, name, description, version, judge_model_config, target_server_config, concurrency, report_config)
5610
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
5611
+ RETURNING id, tenant_id, name, description, version,
5612
+ judge_model_config, target_server_config, concurrency,
5613
+ report_config, created_at, updated_at`,
5614
+ [
5615
+ actualId,
5616
+ tenantId,
5617
+ data.name,
5618
+ data.description || null,
5619
+ data.version || null,
5620
+ JSON.stringify(data.judgeModelConfig),
5621
+ JSON.stringify(data.targetServerConfig),
5622
+ data.concurrency ?? 3,
5623
+ data.reportConfig === void 0 ? null : JSON.stringify(data.reportConfig)
5624
+ ]
5625
+ );
5626
+ return this.mapRowToProject(rows[0]);
5627
+ }
5628
+ /** Update an eval project */
5629
+ async updateProject(tenantId, id, updates) {
5630
+ await this.ensureInitialized();
5631
+ const set = [];
5632
+ const vals = [];
5633
+ let i = 1;
5634
+ if (updates.name !== void 0) {
5635
+ set.push(`name = $${i++}`);
5636
+ vals.push(updates.name);
5637
+ }
5638
+ if (updates.description !== void 0) {
5639
+ set.push(`description = $${i++}`);
5640
+ vals.push(updates.description);
5641
+ }
5642
+ if (updates.version !== void 0) {
5643
+ set.push(`version = $${i++}`);
5644
+ vals.push(updates.version);
5645
+ }
5646
+ if (updates.judgeModelConfig !== void 0) {
5647
+ set.push(`judge_model_config = $${i++}`);
5648
+ vals.push(JSON.stringify(updates.judgeModelConfig));
5649
+ }
5650
+ if (updates.targetServerConfig !== void 0) {
5651
+ set.push(`target_server_config = $${i++}`);
5652
+ vals.push(JSON.stringify(updates.targetServerConfig));
5653
+ }
5654
+ if (updates.concurrency !== void 0) {
5655
+ set.push(`concurrency = $${i++}`);
5656
+ vals.push(updates.concurrency);
5657
+ }
5658
+ if (updates.reportConfig !== void 0) {
5659
+ set.push(`report_config = $${i++}`);
5660
+ vals.push(JSON.stringify(updates.reportConfig));
5661
+ }
5662
+ if (set.length === 0) return this.getProjectById(tenantId, id);
5663
+ set.push(`updated_at = NOW()`);
5664
+ vals.push(id, tenantId);
5665
+ const { rows } = await this.pool.query(
5666
+ `UPDATE lattice_eval_projects SET ${set.join(", ")}
5667
+ WHERE id = $${i} AND tenant_id = $${i + 1}
5668
+ RETURNING id, tenant_id, name, description, version,
5669
+ judge_model_config, target_server_config, concurrency,
5670
+ report_config, created_at, updated_at`,
5671
+ vals
5672
+ );
5673
+ return rows.length ? this.mapRowToProject(rows[0]) : null;
5674
+ }
5675
+ /** Delete an eval project */
5676
+ async deleteProject(tenantId, id) {
5677
+ await this.ensureInitialized();
5678
+ const result = await this.pool.query(
5679
+ `DELETE FROM lattice_eval_projects WHERE id = $1 AND tenant_id = $2`,
5680
+ [id, tenantId]
5681
+ );
5682
+ return (result.rowCount ?? 0) > 0;
5683
+ }
5684
+ // ---------------------------------------------------------------------------
5685
+ // Suites
5686
+ // ---------------------------------------------------------------------------
5687
+ /** Get all suites in a project with case counts */
5688
+ async getSuitesByProject(tenantId, projectId) {
5689
+ await this.ensureInitialized();
5690
+ const { rows } = await this.pool.query(
5691
+ `SELECT s.id, s.tenant_id, s.project_id, s.name, s.created_at, s.updated_at,
5692
+ COUNT(c.id)::int AS case_count
5693
+ FROM lattice_eval_suites s
5694
+ LEFT JOIN lattice_eval_cases c ON c.suite_id = s.id
5695
+ WHERE s.tenant_id = $1 AND s.project_id = $2
5696
+ GROUP BY s.id
5697
+ ORDER BY s.created_at DESC`,
5698
+ [tenantId, projectId]
5699
+ );
5700
+ return rows.map((r) => this.mapRowToSuite(r));
5701
+ }
5702
+ /** Get a single suite by ID with case count */
5703
+ async getSuiteById(tenantId, id) {
5704
+ await this.ensureInitialized();
5705
+ const { rows } = await this.pool.query(
5706
+ `SELECT s.id, s.tenant_id, s.project_id, s.name, s.created_at, s.updated_at,
5707
+ COUNT(c.id)::int AS case_count
5708
+ FROM lattice_eval_suites s
5709
+ LEFT JOIN lattice_eval_cases c ON c.suite_id = s.id
5710
+ WHERE s.tenant_id = $1 AND s.id = $2
5711
+ GROUP BY s.id`,
5712
+ [tenantId, id]
5713
+ );
5714
+ return rows.length ? this.mapRowToSuite(rows[0]) : null;
5715
+ }
5716
+ /** Create a new eval suite */
5717
+ async createSuite(tenantId, projectId, id, data) {
5718
+ await this.ensureInitialized();
5719
+ const actualId = id || (0, import_uuid.v4)();
5720
+ const { rows } = await this.pool.query(
5721
+ `INSERT INTO lattice_eval_suites (id, tenant_id, project_id, name)
5722
+ VALUES ($1, $2, $3, $4)
5723
+ RETURNING id, tenant_id, project_id, name, created_at, updated_at`,
5724
+ [actualId, tenantId, projectId, data.name]
5725
+ );
5726
+ return this.mapRowToSuite(rows[0]);
5727
+ }
5728
+ /** Update a suite's name */
5729
+ async updateSuite(tenantId, id, updates) {
5730
+ await this.ensureInitialized();
5731
+ const set = [];
5732
+ const vals = [];
5733
+ let i = 1;
5734
+ if (updates.name !== void 0) {
5735
+ set.push(`name = $${i++}`);
5736
+ vals.push(updates.name);
5737
+ }
5738
+ if (set.length === 0) return this.getSuiteById(tenantId, id);
5739
+ set.push(`updated_at = NOW()`);
5740
+ vals.push(id, tenantId);
5741
+ const { rows } = await this.pool.query(
5742
+ `UPDATE lattice_eval_suites SET ${set.join(", ")}
5743
+ WHERE id = $${i} AND tenant_id = $${i + 1}
5744
+ RETURNING id, tenant_id, project_id, name, created_at, updated_at`,
5745
+ vals
5746
+ );
5747
+ return rows.length ? this.mapRowToSuite(rows[0]) : null;
5748
+ }
5749
+ /** Delete a suite */
5750
+ async deleteSuite(tenantId, id) {
5751
+ await this.ensureInitialized();
5752
+ const result = await this.pool.query(
5753
+ `DELETE FROM lattice_eval_suites WHERE id = $1 AND tenant_id = $2`,
5754
+ [id, tenantId]
5755
+ );
5756
+ return (result.rowCount ?? 0) > 0;
5757
+ }
5758
+ // ---------------------------------------------------------------------------
5759
+ // Cases
5760
+ // ---------------------------------------------------------------------------
5761
+ /** Get all test cases in a suite */
5762
+ async getCasesBySuite(tenantId, suiteId) {
5763
+ await this.ensureInitialized();
5764
+ const { rows } = await this.pool.query(
5765
+ `SELECT id, tenant_id, suite_id,
5766
+ input_message, input_files, steps,
5767
+ output_type, content_assertion, rubrics,
5768
+ created_at, updated_at
5769
+ FROM lattice_eval_cases
5770
+ WHERE tenant_id = $1 AND suite_id = $2
5771
+ ORDER BY created_at`,
5772
+ [tenantId, suiteId]
5773
+ );
5774
+ return rows.map((r) => this.mapRowToCase(r));
5775
+ }
5776
+ /** Get a single test case by ID */
5777
+ async getCaseById(tenantId, id) {
5778
+ await this.ensureInitialized();
5779
+ const { rows } = await this.pool.query(
5780
+ `SELECT id, tenant_id, suite_id,
5781
+ input_message, input_files, steps,
5782
+ output_type, content_assertion, rubrics,
5783
+ created_at, updated_at
5784
+ FROM lattice_eval_cases
5785
+ WHERE id = $1 AND tenant_id = $2`,
5786
+ [id, tenantId]
5787
+ );
5788
+ return rows.length ? this.mapRowToCase(rows[0]) : null;
5789
+ }
5790
+ /** Create a new test case */
5791
+ async createCase(tenantId, suiteId, id, data) {
5792
+ await this.ensureInitialized();
5793
+ const actualId = id || (0, import_uuid.v4)();
5794
+ const { rows } = await this.pool.query(
5795
+ `INSERT INTO lattice_eval_cases
5796
+ (id, tenant_id, suite_id, input_message, input_files, steps, output_type, content_assertion, rubrics)
5797
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
5798
+ RETURNING id, tenant_id, suite_id,
5799
+ input_message, input_files, steps,
5800
+ output_type, content_assertion, rubrics,
5801
+ created_at, updated_at`,
5802
+ [
5803
+ actualId,
5804
+ tenantId,
5805
+ suiteId,
5806
+ data.inputMessage,
5807
+ JSON.stringify(data.inputFiles || {}),
5808
+ JSON.stringify(data.steps),
5809
+ data.outputType,
5810
+ data.contentAssertion || null,
5811
+ JSON.stringify(data.rubrics || [])
5812
+ ]
5813
+ );
5814
+ return this.mapRowToCase(rows[0]);
5815
+ }
5816
+ /** Update a test case */
5817
+ async updateCase(tenantId, id, updates) {
5818
+ await this.ensureInitialized();
5819
+ const set = [];
5820
+ const vals = [];
5821
+ let i = 1;
5822
+ if (updates.inputMessage !== void 0) {
5823
+ set.push(`input_message = $${i++}`);
5824
+ vals.push(updates.inputMessage);
5825
+ }
5826
+ if (updates.inputFiles !== void 0) {
5827
+ set.push(`input_files = $${i++}`);
5828
+ vals.push(JSON.stringify(updates.inputFiles));
5829
+ }
5830
+ if (updates.steps !== void 0) {
5831
+ set.push(`steps = $${i++}`);
5832
+ vals.push(JSON.stringify(updates.steps));
5833
+ }
5834
+ if (updates.outputType !== void 0) {
5835
+ set.push(`output_type = $${i++}`);
5836
+ vals.push(updates.outputType);
5837
+ }
5838
+ if (updates.contentAssertion !== void 0) {
5839
+ set.push(`content_assertion = $${i++}`);
5840
+ vals.push(updates.contentAssertion);
5841
+ }
5842
+ if (updates.rubrics !== void 0) {
5843
+ set.push(`rubrics = $${i++}`);
5844
+ vals.push(JSON.stringify(updates.rubrics));
5845
+ }
5846
+ if (set.length === 0) return this.getCaseById(tenantId, id);
5847
+ set.push(`updated_at = NOW()`);
5848
+ vals.push(id, tenantId);
5849
+ const { rows } = await this.pool.query(
5850
+ `UPDATE lattice_eval_cases SET ${set.join(", ")}
5851
+ WHERE id = $${i} AND tenant_id = $${i + 1}
5852
+ RETURNING id, tenant_id, suite_id,
5853
+ input_message, input_files, steps,
5854
+ output_type, content_assertion, rubrics,
5855
+ created_at, updated_at`,
5856
+ vals
5857
+ );
5858
+ return rows.length ? this.mapRowToCase(rows[0]) : null;
5859
+ }
5860
+ /** Delete a test case */
5861
+ async deleteCase(tenantId, id) {
5862
+ await this.ensureInitialized();
5863
+ const result = await this.pool.query(
5864
+ `DELETE FROM lattice_eval_cases WHERE id = $1 AND tenant_id = $2`,
5865
+ [id, tenantId]
5866
+ );
5867
+ return (result.rowCount ?? 0) > 0;
5868
+ }
5869
+ // ---------------------------------------------------------------------------
5870
+ // Runs
5871
+ // ---------------------------------------------------------------------------
5872
+ /** Get all runs for a tenant, optionally filtered by project or status */
5873
+ async getRunsByTenant(tenantId, opts) {
5874
+ await this.ensureInitialized();
5875
+ const conditions = ["tenant_id = $1"];
5876
+ const vals = [tenantId];
5877
+ let i = 2;
5878
+ if (opts?.projectId) {
5879
+ conditions.push(`project_id = $${i++}`);
5880
+ vals.push(opts.projectId);
5881
+ }
5882
+ if (opts?.status) {
5883
+ conditions.push(`status = $${i++}`);
5884
+ vals.push(opts.status);
5885
+ }
5886
+ const { rows } = await this.pool.query(
5887
+ `SELECT id, project_id, tenant_id,
5888
+ status, concurrency, total_cases,
5889
+ passed_cases, failed_cases, avg_score,
5890
+ error, created_at, started_at, completed_at
5891
+ FROM lattice_eval_runs
5892
+ WHERE ${conditions.join(" AND ")}
5893
+ ORDER BY created_at DESC`,
5894
+ vals
5895
+ );
5896
+ return rows.map((r) => this.mapRowToRun(r));
5897
+ }
5898
+ /** Get a single run by ID */
5899
+ async getRunById(tenantId, id) {
5900
+ await this.ensureInitialized();
5901
+ const { rows } = await this.pool.query(
5902
+ `SELECT id, project_id, tenant_id,
5903
+ status, concurrency, total_cases,
5904
+ passed_cases, failed_cases, avg_score,
5905
+ error, created_at, started_at, completed_at
5906
+ FROM lattice_eval_runs
5907
+ WHERE id = $1 AND tenant_id = $2`,
5908
+ [id, tenantId]
5909
+ );
5910
+ return rows.length ? this.mapRowToRun(rows[0]) : null;
5911
+ }
5912
+ /** Create a new eval run */
5913
+ async createRun(tenantId, projectId, id, data) {
5914
+ await this.ensureInitialized();
5915
+ const actualId = id || (0, import_uuid.v4)();
5916
+ const { rows } = await this.pool.query(
5917
+ `INSERT INTO lattice_eval_runs
5918
+ (id, project_id, tenant_id, status, concurrency, total_cases, passed_cases, failed_cases, avg_score, started_at)
5919
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
5920
+ RETURNING id, project_id, tenant_id,
5921
+ status, concurrency, total_cases,
5922
+ passed_cases, failed_cases, avg_score,
5923
+ error, created_at, started_at, completed_at`,
5924
+ [
5925
+ actualId,
5926
+ projectId,
5927
+ tenantId,
5928
+ "running",
5929
+ data.concurrency,
5930
+ data.totalCases,
5931
+ 0,
5932
+ 0,
5933
+ 0,
5934
+ /* @__PURE__ */ new Date()
5935
+ ]
5936
+ );
5937
+ return this.mapRowToRun(rows[0]);
5938
+ }
5939
+ /** Update a run's status and aggregate fields */
5940
+ async updateRunStatus(tenantId, id, updates) {
5941
+ await this.ensureInitialized();
5942
+ const set = [];
5943
+ const vals = [];
5944
+ let i = 1;
5945
+ if (updates.status !== void 0) {
5946
+ set.push(`status = $${i++}`);
5947
+ vals.push(updates.status);
5948
+ }
5949
+ if (updates.passedCases !== void 0) {
5950
+ set.push(`passed_cases = $${i++}`);
5951
+ vals.push(updates.passedCases);
5952
+ }
5953
+ if (updates.failedCases !== void 0) {
5954
+ set.push(`failed_cases = $${i++}`);
5955
+ vals.push(updates.failedCases);
5956
+ }
5957
+ if (updates.avgScore !== void 0) {
5958
+ set.push(`avg_score = $${i++}`);
5959
+ vals.push(updates.avgScore);
5960
+ }
5961
+ if (updates.completedAt !== void 0) {
5962
+ set.push(`completed_at = $${i++}`);
5963
+ vals.push(updates.completedAt);
5964
+ }
5965
+ if (updates.error !== void 0) {
5966
+ set.push(`error = $${i++}`);
5967
+ vals.push(updates.error);
5968
+ }
5969
+ if (set.length === 0) return this.getRunById(tenantId, id);
5970
+ vals.push(id, tenantId);
5971
+ const { rows } = await this.pool.query(
5972
+ `UPDATE lattice_eval_runs SET ${set.join(", ")}
5973
+ WHERE id = $${i} AND tenant_id = $${i + 1}
5974
+ RETURNING id, project_id, tenant_id,
5975
+ status, concurrency, total_cases,
5976
+ passed_cases, failed_cases, avg_score,
5977
+ error, created_at, started_at, completed_at`,
5978
+ vals
5979
+ );
5980
+ return rows.length ? this.mapRowToRun(rows[0]) : null;
5981
+ }
5982
+ /** Delete a run and its results */
5983
+ async deleteRun(tenantId, id) {
5984
+ await this.ensureInitialized();
5985
+ await this.pool.query(`DELETE FROM lattice_eval_run_results WHERE run_id = $1 AND tenant_id = $2`, [id, tenantId]);
5986
+ const { rowCount } = await this.pool.query(
5987
+ `DELETE FROM lattice_eval_runs WHERE id = $1 AND tenant_id = $2`,
5988
+ [id, tenantId]
5989
+ );
5990
+ return (rowCount ?? 0) > 0;
5991
+ }
5992
+ // ---------------------------------------------------------------------------
5993
+ // Run Results
5994
+ // ---------------------------------------------------------------------------
5995
+ /** Get all results belonging to a run */
5996
+ async getResultsByRun(tenantId, runId) {
5997
+ await this.ensureInitialized();
5998
+ const { rows } = await this.pool.query(
5999
+ `SELECT rr.id, rr.run_id, rr.suite_name, rr.case_id,
6000
+ rr.pass, rr.score, rr.summary, rr.dimension_results,
6001
+ rr.duration_ms, rr.messages, rr.logs, rr.error, rr.created_at
6002
+ FROM lattice_eval_run_results rr
6003
+ INNER JOIN lattice_eval_runs r ON r.id = rr.run_id
6004
+ WHERE r.tenant_id = $1 AND rr.run_id = $2
6005
+ ORDER BY rr.created_at`,
6006
+ [tenantId, runId]
6007
+ );
6008
+ return rows.map((r) => this.mapRowToRunResult(r));
6009
+ }
6010
+ /** Create a result for a case within a run */
6011
+ async createRunResult(tenantId, runId, id, data) {
6012
+ await this.ensureInitialized();
6013
+ const actualId = id || (0, import_uuid.v4)();
6014
+ const { rows } = await this.pool.query(
6015
+ `INSERT INTO lattice_eval_run_results
6016
+ (id, run_id, suite_name, case_id, pass, score, summary, dimension_results, duration_ms, messages, logs, error)
6017
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
6018
+ RETURNING id, run_id, suite_name, case_id,
6019
+ pass, score, summary, dimension_results,
6020
+ duration_ms, messages, logs, error, created_at`,
6021
+ [
6022
+ actualId,
6023
+ runId,
6024
+ data.suiteName,
6025
+ data.caseId || null,
6026
+ data.pass,
6027
+ data.score,
6028
+ data.summary || null,
6029
+ JSON.stringify(data.dimensionResults || []),
6030
+ data.durationMs ?? null,
6031
+ JSON.stringify(data.messages || []),
6032
+ JSON.stringify(data.logs || []),
6033
+ data.error || null
6034
+ ]
6035
+ );
6036
+ return this.mapRowToRunResult(rows[0]);
6037
+ }
6038
+ /** Update a run result with tenant isolation via the parent run */
6039
+ async updateRunResult(tenantId, id, updates) {
6040
+ await this.ensureInitialized();
6041
+ if (updates.runId !== void 0) {
6042
+ throw new Error("runId cannot be updated on an existing result");
6043
+ }
6044
+ const set = [];
6045
+ const vals = [];
6046
+ let i = 1;
6047
+ if (updates.suiteName !== void 0) {
6048
+ set.push(`suite_name = $${i++}`);
6049
+ vals.push(updates.suiteName);
6050
+ }
6051
+ if (updates.caseId !== void 0) {
6052
+ set.push(`case_id = $${i++}`);
6053
+ vals.push(updates.caseId);
6054
+ }
6055
+ if (updates.pass !== void 0) {
6056
+ set.push(`pass = $${i++}`);
6057
+ vals.push(updates.pass);
6058
+ }
6059
+ if (updates.score !== void 0) {
6060
+ set.push(`score = $${i++}`);
6061
+ vals.push(updates.score);
6062
+ }
6063
+ if (updates.summary !== void 0) {
6064
+ set.push(`summary = $${i++}`);
6065
+ vals.push(updates.summary);
6066
+ }
6067
+ if (updates.dimensionResults !== void 0) {
6068
+ set.push(`dimension_results = $${i++}`);
6069
+ vals.push(JSON.stringify(updates.dimensionResults));
6070
+ }
6071
+ if (updates.durationMs !== void 0) {
6072
+ set.push(`duration_ms = $${i++}`);
6073
+ vals.push(updates.durationMs);
6074
+ }
6075
+ if (updates.messages !== void 0) {
6076
+ set.push(`messages = $${i++}`);
6077
+ vals.push(JSON.stringify(updates.messages));
6078
+ }
6079
+ if (updates.logs !== void 0) {
6080
+ set.push(`logs = $${i++}`);
6081
+ vals.push(JSON.stringify(updates.logs));
6082
+ }
6083
+ if (updates.error !== void 0) {
6084
+ set.push(`error = $${i++}`);
6085
+ vals.push(updates.error);
6086
+ }
6087
+ if (set.length === 0) return this.getRunResultById(tenantId, id);
6088
+ vals.push(id, tenantId);
6089
+ const { rows } = await this.pool.query(
6090
+ `UPDATE lattice_eval_run_results SET ${set.join(", ")}
6091
+ WHERE id = $${i}
6092
+ AND run_id IN (SELECT id FROM lattice_eval_runs WHERE tenant_id = $${i + 1})
6093
+ RETURNING id, run_id, suite_name, case_id,
6094
+ pass, score, summary, dimension_results,
6095
+ duration_ms, messages, logs, error, created_at`,
6096
+ vals
6097
+ );
6098
+ return rows.length ? this.mapRowToRunResult(rows[0]) : null;
6099
+ }
6100
+ /** Get a single run result by ID with tenant isolation */
6101
+ async getRunResultById(tenantId, id) {
6102
+ await this.ensureInitialized();
6103
+ const { rows } = await this.pool.query(
6104
+ `SELECT rr.id, rr.run_id, rr.suite_name, rr.case_id,
6105
+ rr.pass, rr.score, rr.summary, rr.dimension_results,
6106
+ rr.duration_ms, rr.messages, rr.logs, rr.error, rr.created_at
6107
+ FROM lattice_eval_run_results rr
6108
+ INNER JOIN lattice_eval_runs r ON r.id = rr.run_id
6109
+ WHERE rr.id = $1 AND r.tenant_id = $2`,
6110
+ [id, tenantId]
6111
+ );
6112
+ return rows.length ? this.mapRowToRunResult(rows[0]) : null;
6113
+ }
6114
+ // ---------------------------------------------------------------------------
6115
+ // Reports
6116
+ // ---------------------------------------------------------------------------
6117
+ /** Aggregate report for a project including all runs */
6118
+ async getProjectReport(tenantId, projectId) {
6119
+ await this.ensureInitialized();
6120
+ const projectRows = await this.pool.query(
6121
+ `SELECT id, tenant_id, name, description, version,
6122
+ judge_model_config, target_server_config, concurrency,
6123
+ report_config, created_at, updated_at
6124
+ FROM lattice_eval_projects
6125
+ WHERE id = $1 AND tenant_id = $2`,
6126
+ [projectId, tenantId]
6127
+ );
6128
+ if (projectRows.rows.length === 0) return null;
6129
+ const project = this.mapRowToProject(projectRows.rows[0]);
6130
+ const runs = await this.getRunsByTenant(tenantId, { projectId });
6131
+ const totalRuns = runs.length;
6132
+ const latestPassRate = runs.length > 0 ? runs[0].totalCases > 0 ? runs[0].passedCases / runs[0].totalCases : 0 : 0;
6133
+ const avgScore = runs.length > 0 ? runs.reduce((sum, r) => sum + r.avgScore, 0) / runs.length : 0;
6134
+ return {
6135
+ projectId: project.id,
6136
+ projectName: project.name,
6137
+ totalRuns,
6138
+ latestPassRate,
6139
+ avgScore,
6140
+ runs
6141
+ };
6142
+ }
6143
+ };
5216
6144
  // Annotate the CommonJS export names for ESM import in node:
5217
6145
  0 && (module.exports = {
5218
6146
  ChannelIdentityMappingStore,
@@ -5221,6 +6149,7 @@ var PostgreSQLWorkflowTrackingStore = class {
5221
6149
  PostgreSQLAssistantStore,
5222
6150
  PostgreSQLChannelInstallationStore,
5223
6151
  PostgreSQLDatabaseConfigStore,
6152
+ PostgreSQLEvalStore,
5224
6153
  PostgreSQLMcpServerConfigStore,
5225
6154
  PostgreSQLMetricsServerConfigStore,
5226
6155
  PostgreSQLProjectStore,
@@ -5244,6 +6173,11 @@ var PostgreSQLWorkflowTrackingStore = class {
5244
6173
  createChannelIdentityMappingTables,
5245
6174
  createChannelInstallationsTable,
5246
6175
  createDatabaseConfigsTable,
6176
+ createEvalCasesTable,
6177
+ createEvalProjectsTable,
6178
+ createEvalRunResultsTable,
6179
+ createEvalRunsTable,
6180
+ createEvalSuitesTable,
5247
6181
  createMcpServerConfigsTable,
5248
6182
  createMetricsConfigsTable,
5249
6183
  createProjectsTable,
@@ -5255,6 +6189,7 @@ var PostgreSQLWorkflowTrackingStore = class {
5255
6189
  createUsersTable,
5256
6190
  createWorkflowTrackingTables,
5257
6191
  createWorkspacesTable,
6192
+ evalMigrations,
5258
6193
  getThreadMessageQueueStore
5259
6194
  });
5260
6195
  //# sourceMappingURL=index.js.map