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