@contractspec/module.provider-ranking 0.1.2

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.
@@ -0,0 +1,134 @@
1
+ // src/pipeline/ingestion-pipeline.ts
2
+ import { normalizeBenchmarkResults } from "@contractspec/lib.provider-ranking/scoring";
3
+
4
+ class IngestionPipeline {
5
+ store;
6
+ registry;
7
+ ingesterOptions;
8
+ constructor(options) {
9
+ this.store = options.store;
10
+ this.registry = options.ingesterRegistry;
11
+ this.ingesterOptions = options.ingesterOptions;
12
+ }
13
+ async ingest(source, params) {
14
+ const ingester = this.registry.get(source);
15
+ if (!ingester) {
16
+ throw new Error(`No ingester registered for source: ${source}`);
17
+ }
18
+ return this.runIngester(ingester, params);
19
+ }
20
+ async ingestAll(params) {
21
+ const results = [];
22
+ for (const ingester of this.registry.list()) {
23
+ const result = await this.runIngester(ingester, params);
24
+ results.push(result);
25
+ }
26
+ return results;
27
+ }
28
+ mergeOptions(params) {
29
+ const merged = { ...this.ingesterOptions };
30
+ if (params?.fromDate)
31
+ merged.fromDate = new Date(params.fromDate);
32
+ if (params?.toDate)
33
+ merged.toDate = new Date(params.toDate);
34
+ if (params?.dimensions?.length)
35
+ merged.dimensions = params.dimensions;
36
+ return merged;
37
+ }
38
+ async runIngester(ingester, params) {
39
+ const ingestionId = `ingest-${ingester.source}-${Date.now()}`;
40
+ const run = {
41
+ id: ingestionId,
42
+ source: ingester.source,
43
+ status: "running",
44
+ resultsCount: 0,
45
+ startedAt: new Date,
46
+ completedAt: null,
47
+ error: null
48
+ };
49
+ await this.store.createIngestionRun(run);
50
+ try {
51
+ const opts = this.mergeOptions(params);
52
+ const rawResults = await ingester.ingest(opts);
53
+ const normalized = normalizeBenchmarkResults(rawResults);
54
+ for (const result of normalized) {
55
+ await this.store.upsertBenchmarkResult(result);
56
+ }
57
+ await this.store.updateIngestionRun(ingestionId, {
58
+ status: "completed",
59
+ resultsCount: normalized.length,
60
+ completedAt: new Date
61
+ });
62
+ return {
63
+ ingestionId,
64
+ source: ingester.source,
65
+ resultsCount: normalized.length,
66
+ status: "completed"
67
+ };
68
+ } catch (error) {
69
+ const errorMessage = error instanceof Error ? error.message : String(error);
70
+ await this.store.updateIngestionRun(ingestionId, {
71
+ status: "failed",
72
+ completedAt: new Date,
73
+ error: errorMessage
74
+ });
75
+ return {
76
+ ingestionId,
77
+ source: ingester.source,
78
+ resultsCount: 0,
79
+ status: "failed"
80
+ };
81
+ }
82
+ }
83
+ }
84
+
85
+ // src/pipeline/ranking-pipeline.ts
86
+ import { computeModelRankings } from "@contractspec/lib.provider-ranking/scoring";
87
+
88
+ class RankingPipeline {
89
+ store;
90
+ constructor(options) {
91
+ this.store = options.store;
92
+ }
93
+ async refresh(params) {
94
+ let allResults = await this.loadAllBenchmarkResults();
95
+ if (params?.dimensions?.length) {
96
+ const dimSet = new Set(params.dimensions);
97
+ allResults = allResults.filter((r) => dimSet.has(r.dimension));
98
+ }
99
+ const existingRankings = params?.forceRecalculate ? new Map : new Map((await this.store.listModelRankings({
100
+ limit: 1e4,
101
+ requiredTransport: params?.requiredTransport,
102
+ requiredAuthMethod: params?.requiredAuthMethod
103
+ })).rankings.map((r) => [r.modelId, r]));
104
+ const newRankings = computeModelRankings(allResults, params?.weightOverrides ? { weightOverrides: params.weightOverrides } : undefined, existingRankings);
105
+ for (const ranking of newRankings) {
106
+ await this.store.upsertModelRanking(ranking);
107
+ }
108
+ return {
109
+ modelsRanked: newRankings.length,
110
+ updatedAt: new Date
111
+ };
112
+ }
113
+ async loadAllBenchmarkResults() {
114
+ const pageSize = 500;
115
+ let offset = 0;
116
+ const allResults = [];
117
+ while (true) {
118
+ const page = await this.store.listBenchmarkResults({
119
+ limit: pageSize,
120
+ offset
121
+ });
122
+ allResults.push(...page.results);
123
+ if (allResults.length >= page.total || page.results.length < pageSize) {
124
+ break;
125
+ }
126
+ offset += pageSize;
127
+ }
128
+ return allResults;
129
+ }
130
+ }
131
+ export {
132
+ RankingPipeline,
133
+ IngestionPipeline
134
+ };
@@ -0,0 +1,86 @@
1
+ // src/pipeline/ingestion-pipeline.ts
2
+ import { normalizeBenchmarkResults } from "@contractspec/lib.provider-ranking/scoring";
3
+
4
+ class IngestionPipeline {
5
+ store;
6
+ registry;
7
+ ingesterOptions;
8
+ constructor(options) {
9
+ this.store = options.store;
10
+ this.registry = options.ingesterRegistry;
11
+ this.ingesterOptions = options.ingesterOptions;
12
+ }
13
+ async ingest(source, params) {
14
+ const ingester = this.registry.get(source);
15
+ if (!ingester) {
16
+ throw new Error(`No ingester registered for source: ${source}`);
17
+ }
18
+ return this.runIngester(ingester, params);
19
+ }
20
+ async ingestAll(params) {
21
+ const results = [];
22
+ for (const ingester of this.registry.list()) {
23
+ const result = await this.runIngester(ingester, params);
24
+ results.push(result);
25
+ }
26
+ return results;
27
+ }
28
+ mergeOptions(params) {
29
+ const merged = { ...this.ingesterOptions };
30
+ if (params?.fromDate)
31
+ merged.fromDate = new Date(params.fromDate);
32
+ if (params?.toDate)
33
+ merged.toDate = new Date(params.toDate);
34
+ if (params?.dimensions?.length)
35
+ merged.dimensions = params.dimensions;
36
+ return merged;
37
+ }
38
+ async runIngester(ingester, params) {
39
+ const ingestionId = `ingest-${ingester.source}-${Date.now()}`;
40
+ const run = {
41
+ id: ingestionId,
42
+ source: ingester.source,
43
+ status: "running",
44
+ resultsCount: 0,
45
+ startedAt: new Date,
46
+ completedAt: null,
47
+ error: null
48
+ };
49
+ await this.store.createIngestionRun(run);
50
+ try {
51
+ const opts = this.mergeOptions(params);
52
+ const rawResults = await ingester.ingest(opts);
53
+ const normalized = normalizeBenchmarkResults(rawResults);
54
+ for (const result of normalized) {
55
+ await this.store.upsertBenchmarkResult(result);
56
+ }
57
+ await this.store.updateIngestionRun(ingestionId, {
58
+ status: "completed",
59
+ resultsCount: normalized.length,
60
+ completedAt: new Date
61
+ });
62
+ return {
63
+ ingestionId,
64
+ source: ingester.source,
65
+ resultsCount: normalized.length,
66
+ status: "completed"
67
+ };
68
+ } catch (error) {
69
+ const errorMessage = error instanceof Error ? error.message : String(error);
70
+ await this.store.updateIngestionRun(ingestionId, {
71
+ status: "failed",
72
+ completedAt: new Date,
73
+ error: errorMessage
74
+ });
75
+ return {
76
+ ingestionId,
77
+ source: ingester.source,
78
+ resultsCount: 0,
79
+ status: "failed"
80
+ };
81
+ }
82
+ }
83
+ }
84
+ export {
85
+ IngestionPipeline
86
+ };
@@ -0,0 +1,49 @@
1
+ // src/pipeline/ranking-pipeline.ts
2
+ import { computeModelRankings } from "@contractspec/lib.provider-ranking/scoring";
3
+
4
+ class RankingPipeline {
5
+ store;
6
+ constructor(options) {
7
+ this.store = options.store;
8
+ }
9
+ async refresh(params) {
10
+ let allResults = await this.loadAllBenchmarkResults();
11
+ if (params?.dimensions?.length) {
12
+ const dimSet = new Set(params.dimensions);
13
+ allResults = allResults.filter((r) => dimSet.has(r.dimension));
14
+ }
15
+ const existingRankings = params?.forceRecalculate ? new Map : new Map((await this.store.listModelRankings({
16
+ limit: 1e4,
17
+ requiredTransport: params?.requiredTransport,
18
+ requiredAuthMethod: params?.requiredAuthMethod
19
+ })).rankings.map((r) => [r.modelId, r]));
20
+ const newRankings = computeModelRankings(allResults, params?.weightOverrides ? { weightOverrides: params.weightOverrides } : undefined, existingRankings);
21
+ for (const ranking of newRankings) {
22
+ await this.store.upsertModelRanking(ranking);
23
+ }
24
+ return {
25
+ modelsRanked: newRankings.length,
26
+ updatedAt: new Date
27
+ };
28
+ }
29
+ async loadAllBenchmarkResults() {
30
+ const pageSize = 500;
31
+ let offset = 0;
32
+ const allResults = [];
33
+ while (true) {
34
+ const page = await this.store.listBenchmarkResults({
35
+ limit: pageSize,
36
+ offset
37
+ });
38
+ allResults.push(...page.results);
39
+ if (allResults.length >= page.total || page.results.length < pageSize) {
40
+ break;
41
+ }
42
+ offset += pageSize;
43
+ }
44
+ return allResults;
45
+ }
46
+ }
47
+ export {
48
+ RankingPipeline
49
+ };
@@ -0,0 +1,299 @@
1
+ // src/storage/index.ts
2
+ class PostgresProviderRankingStore {
3
+ database;
4
+ schema;
5
+ createTablesIfMissing;
6
+ ensured = false;
7
+ constructor(options) {
8
+ this.database = options.database;
9
+ this.schema = options.schema ?? "lssm_ranking";
10
+ this.createTablesIfMissing = options.createTablesIfMissing ?? true;
11
+ }
12
+ async upsertBenchmarkResult(result) {
13
+ await this.ensureTables();
14
+ await this.database.execute(`INSERT INTO ${this.table("benchmark_result")}
15
+ (id, model_id, provider_key, source, dimension, score, raw_score, metadata, measured_at, ingested_at)
16
+ VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8::jsonb, $9, $10)
17
+ ON CONFLICT (id)
18
+ DO UPDATE SET
19
+ score = EXCLUDED.score,
20
+ raw_score = EXCLUDED.raw_score,
21
+ metadata = EXCLUDED.metadata,
22
+ measured_at = EXCLUDED.measured_at,
23
+ ingested_at = EXCLUDED.ingested_at;`, [
24
+ result.id,
25
+ result.modelId,
26
+ result.providerKey,
27
+ result.source,
28
+ result.dimension,
29
+ result.score,
30
+ JSON.stringify(result.rawScore),
31
+ result.metadata ? JSON.stringify(result.metadata) : null,
32
+ result.measuredAt.toISOString(),
33
+ result.ingestedAt.toISOString()
34
+ ]);
35
+ }
36
+ async getBenchmarkResult(id) {
37
+ await this.ensureTables();
38
+ const rows = await this.database.query(`SELECT * FROM ${this.table("benchmark_result")} WHERE id = $1;`, [id]);
39
+ return rows.rows[0] ? this.mapBenchmarkResult(rows.rows[0]) : null;
40
+ }
41
+ async listBenchmarkResults(query) {
42
+ await this.ensureTables();
43
+ const limit = query.limit ?? 50;
44
+ const offset = query.offset ?? 0;
45
+ const countFilters = [];
46
+ const countParams = [];
47
+ if (query.source) {
48
+ countParams.push(query.source);
49
+ countFilters.push(`source = $${countParams.length}`);
50
+ }
51
+ if (query.modelId) {
52
+ countParams.push(query.modelId);
53
+ countFilters.push(`model_id = $${countParams.length}`);
54
+ }
55
+ if (query.dimension) {
56
+ countParams.push(query.dimension);
57
+ countFilters.push(`dimension = $${countParams.length}`);
58
+ }
59
+ if (query.providerKey) {
60
+ countParams.push(query.providerKey);
61
+ countFilters.push(`provider_key = $${countParams.length}`);
62
+ }
63
+ const where = countFilters.length ? `WHERE ${countFilters.join(" AND ")}` : "";
64
+ const countResult = await this.database.query(`SELECT COUNT(*)::int as total FROM ${this.table("benchmark_result")} ${where};`, countParams);
65
+ const total = Number(countResult.rows[0]?.total ?? 0);
66
+ const dataParams = [
67
+ limit,
68
+ offset,
69
+ ...countParams
70
+ ];
71
+ const dataFilters = countFilters.map((_f, i) => _f.replace(`$${i + 1}`, `$${i + 3}`));
72
+ const dataWhere = dataFilters.length ? `WHERE ${dataFilters.join(" AND ")}` : "";
73
+ const rows = await this.database.query(`SELECT * FROM ${this.table("benchmark_result")}
74
+ ${dataWhere}
75
+ ORDER BY ingested_at DESC
76
+ LIMIT $1 OFFSET $2;`, dataParams);
77
+ const results = rows.rows.map((row) => this.mapBenchmarkResult(row));
78
+ const nextOffset = offset + results.length < total ? offset + results.length : undefined;
79
+ return { results, total, nextOffset };
80
+ }
81
+ async upsertModelRanking(ranking) {
82
+ await this.ensureTables();
83
+ await this.database.execute(`INSERT INTO ${this.table("model_ranking")}
84
+ (model_id, provider_key, composite_score, dimension_scores, rank, previous_rank, updated_at)
85
+ VALUES ($1, $2, $3, $4::jsonb, $5, $6, $7)
86
+ ON CONFLICT (model_id)
87
+ DO UPDATE SET
88
+ provider_key = EXCLUDED.provider_key,
89
+ composite_score = EXCLUDED.composite_score,
90
+ dimension_scores = EXCLUDED.dimension_scores,
91
+ rank = EXCLUDED.rank,
92
+ previous_rank = EXCLUDED.previous_rank,
93
+ updated_at = EXCLUDED.updated_at;`, [
94
+ ranking.modelId,
95
+ ranking.providerKey,
96
+ ranking.compositeScore,
97
+ JSON.stringify(ranking.dimensionScores),
98
+ ranking.rank,
99
+ ranking.previousRank,
100
+ ranking.updatedAt.toISOString()
101
+ ]);
102
+ }
103
+ async getModelRanking(modelId) {
104
+ await this.ensureTables();
105
+ const rows = await this.database.query(`SELECT * FROM ${this.table("model_ranking")} WHERE model_id = $1;`, [modelId]);
106
+ return rows.rows[0] ? this.mapModelRanking(rows.rows[0]) : null;
107
+ }
108
+ async listModelRankings(query) {
109
+ await this.ensureTables();
110
+ const limit = query.limit ?? 50;
111
+ const offset = query.offset ?? 0;
112
+ const countFilters = [];
113
+ const countParams = [];
114
+ if (query.providerKey) {
115
+ countParams.push(query.providerKey);
116
+ countFilters.push(`provider_key = $${countParams.length}`);
117
+ }
118
+ const where = countFilters.length ? `WHERE ${countFilters.join(" AND ")}` : "";
119
+ const countResult = await this.database.query(`SELECT COUNT(*)::int as total FROM ${this.table("model_ranking")} ${where};`, countParams);
120
+ const total = Number(countResult.rows[0]?.total ?? 0);
121
+ const dataParams = [
122
+ limit,
123
+ offset,
124
+ ...countParams
125
+ ];
126
+ const dataFilters = countFilters.map((_f, i) => _f.replace(`$${i + 1}`, `$${i + 3}`));
127
+ const dataWhere = dataFilters.length ? `WHERE ${dataFilters.join(" AND ")}` : "";
128
+ const orderBy = query.dimension ? `(dimension_scores->>'${query.dimension}')::jsonb->>'score' DESC NULLS LAST` : "rank ASC";
129
+ const rows = await this.database.query(`SELECT * FROM ${this.table("model_ranking")}
130
+ ${dataWhere}
131
+ ORDER BY ${orderBy}
132
+ LIMIT $1 OFFSET $2;`, dataParams);
133
+ const rankings = rows.rows.map((row) => this.mapModelRanking(row));
134
+ const nextOffset = offset + rankings.length < total ? offset + rankings.length : undefined;
135
+ return { rankings, total, nextOffset };
136
+ }
137
+ async getModelProfile(modelId) {
138
+ await this.ensureTables();
139
+ const ranking = await this.getModelRanking(modelId);
140
+ const benchResults = await this.database.query(`SELECT * FROM ${this.table("benchmark_result")}
141
+ WHERE model_id = $1
142
+ ORDER BY ingested_at DESC;`, [modelId]);
143
+ if (!ranking && benchResults.rows.length === 0)
144
+ return null;
145
+ return {
146
+ modelId,
147
+ providerKey: ranking?.providerKey ?? String(benchResults.rows[0]?.provider_key ?? "unknown"),
148
+ displayName: modelId,
149
+ contextWindow: 0,
150
+ costPerMillion: null,
151
+ capabilities: [],
152
+ ranking: ranking ?? null,
153
+ benchmarkResults: benchResults.rows.map((row) => this.mapBenchmarkResult(row))
154
+ };
155
+ }
156
+ async createIngestionRun(run) {
157
+ await this.ensureTables();
158
+ await this.database.execute(`INSERT INTO ${this.table("ingestion_run")}
159
+ (id, source, status, results_count, started_at, completed_at, error)
160
+ VALUES ($1, $2, $3, $4, $5, $6, $7);`, [
161
+ run.id,
162
+ run.source,
163
+ run.status,
164
+ run.resultsCount,
165
+ run.startedAt.toISOString(),
166
+ run.completedAt?.toISOString() ?? null,
167
+ run.error
168
+ ]);
169
+ }
170
+ async updateIngestionRun(id, update) {
171
+ await this.ensureTables();
172
+ const sets = [];
173
+ const params = [id];
174
+ if (update.status !== undefined) {
175
+ params.push(update.status);
176
+ sets.push(`status = $${params.length}`);
177
+ }
178
+ if (update.resultsCount !== undefined) {
179
+ params.push(update.resultsCount);
180
+ sets.push(`results_count = $${params.length}`);
181
+ }
182
+ if (update.completedAt !== undefined) {
183
+ params.push(update.completedAt?.toISOString() ?? null);
184
+ sets.push(`completed_at = $${params.length}`);
185
+ }
186
+ if (update.error !== undefined) {
187
+ params.push(update.error);
188
+ sets.push(`error = $${params.length}`);
189
+ }
190
+ if (sets.length === 0)
191
+ return;
192
+ await this.database.execute(`UPDATE ${this.table("ingestion_run")} SET ${sets.join(", ")} WHERE id = $1;`, params);
193
+ }
194
+ async getIngestionRun(id) {
195
+ await this.ensureTables();
196
+ const rows = await this.database.query(`SELECT * FROM ${this.table("ingestion_run")} WHERE id = $1;`, [id]);
197
+ return rows.rows[0] ? this.mapIngestionRun(rows.rows[0]) : null;
198
+ }
199
+ async ensureTables() {
200
+ if (this.ensured || !this.createTablesIfMissing)
201
+ return;
202
+ await this.database.execute(`CREATE SCHEMA IF NOT EXISTS ${this.schema};`);
203
+ await this.database.execute(`CREATE TABLE IF NOT EXISTS ${this.table("benchmark_result")} (
204
+ id text PRIMARY KEY,
205
+ model_id text NOT NULL,
206
+ provider_key text NOT NULL,
207
+ source text NOT NULL,
208
+ dimension text NOT NULL,
209
+ score double precision NOT NULL,
210
+ raw_score jsonb,
211
+ metadata jsonb,
212
+ measured_at timestamptz NOT NULL,
213
+ ingested_at timestamptz NOT NULL
214
+ );`);
215
+ await this.database.execute(`CREATE INDEX IF NOT EXISTS benchmark_result_model_idx
216
+ ON ${this.table("benchmark_result")} (model_id);`);
217
+ await this.database.execute(`CREATE INDEX IF NOT EXISTS benchmark_result_source_idx
218
+ ON ${this.table("benchmark_result")} (source);`);
219
+ await this.database.execute(`CREATE INDEX IF NOT EXISTS benchmark_result_dimension_idx
220
+ ON ${this.table("benchmark_result")} (dimension);`);
221
+ await this.database.execute(`CREATE TABLE IF NOT EXISTS ${this.table("model_ranking")} (
222
+ model_id text PRIMARY KEY,
223
+ provider_key text NOT NULL,
224
+ composite_score double precision NOT NULL,
225
+ dimension_scores jsonb NOT NULL,
226
+ rank int NOT NULL,
227
+ previous_rank int,
228
+ updated_at timestamptz NOT NULL
229
+ );`);
230
+ await this.database.execute(`CREATE INDEX IF NOT EXISTS model_ranking_rank_idx
231
+ ON ${this.table("model_ranking")} (rank);`);
232
+ await this.database.execute(`CREATE TABLE IF NOT EXISTS ${this.table("ingestion_run")} (
233
+ id text PRIMARY KEY,
234
+ source text NOT NULL,
235
+ status text NOT NULL,
236
+ results_count int NOT NULL DEFAULT 0,
237
+ started_at timestamptz NOT NULL,
238
+ completed_at timestamptz,
239
+ error text
240
+ );`);
241
+ this.ensured = true;
242
+ }
243
+ table(name) {
244
+ return `${this.schema}.${name}`;
245
+ }
246
+ mapBenchmarkResult(row) {
247
+ return {
248
+ id: String(row.id),
249
+ modelId: String(row.model_id),
250
+ providerKey: String(row.provider_key),
251
+ source: String(row.source),
252
+ dimension: String(row.dimension),
253
+ score: Number(row.score),
254
+ rawScore: parseJson(row.raw_score),
255
+ metadata: parseJson(row.metadata) ?? {},
256
+ measuredAt: new Date(String(row.measured_at)),
257
+ ingestedAt: new Date(String(row.ingested_at))
258
+ };
259
+ }
260
+ mapModelRanking(row) {
261
+ return {
262
+ modelId: String(row.model_id),
263
+ providerKey: String(row.provider_key),
264
+ compositeScore: Number(row.composite_score),
265
+ dimensionScores: parseJson(row.dimension_scores) ?? {},
266
+ rank: Number(row.rank),
267
+ previousRank: row.previous_rank != null ? Number(row.previous_rank) : null,
268
+ updatedAt: new Date(String(row.updated_at))
269
+ };
270
+ }
271
+ mapIngestionRun(row) {
272
+ return {
273
+ id: String(row.id),
274
+ source: String(row.source),
275
+ status: String(row.status),
276
+ resultsCount: Number(row.results_count),
277
+ startedAt: new Date(String(row.started_at)),
278
+ completedAt: row.completed_at ? new Date(String(row.completed_at)) : null,
279
+ error: row.error ? String(row.error) : null
280
+ };
281
+ }
282
+ }
283
+ function parseJson(value) {
284
+ if (value == null)
285
+ return null;
286
+ if (typeof value === "object")
287
+ return value;
288
+ if (typeof value === "string") {
289
+ try {
290
+ return JSON.parse(value);
291
+ } catch {
292
+ return null;
293
+ }
294
+ }
295
+ return value;
296
+ }
297
+ export {
298
+ PostgresProviderRankingStore
299
+ };
@@ -0,0 +1,2 @@
1
+ export { IngestionPipeline, type IngestionPipelineOptions, type IngestionPipelineResult, type IngestParams, } from './ingestion-pipeline';
2
+ export { RankingPipeline, type RankingPipelineOptions, type RankingPipelineResult, type RefreshParams, } from './ranking-pipeline';
@@ -0,0 +1,135 @@
1
+ // @bun
2
+ // src/pipeline/ingestion-pipeline.ts
3
+ import { normalizeBenchmarkResults } from "@contractspec/lib.provider-ranking/scoring";
4
+
5
+ class IngestionPipeline {
6
+ store;
7
+ registry;
8
+ ingesterOptions;
9
+ constructor(options) {
10
+ this.store = options.store;
11
+ this.registry = options.ingesterRegistry;
12
+ this.ingesterOptions = options.ingesterOptions;
13
+ }
14
+ async ingest(source, params) {
15
+ const ingester = this.registry.get(source);
16
+ if (!ingester) {
17
+ throw new Error(`No ingester registered for source: ${source}`);
18
+ }
19
+ return this.runIngester(ingester, params);
20
+ }
21
+ async ingestAll(params) {
22
+ const results = [];
23
+ for (const ingester of this.registry.list()) {
24
+ const result = await this.runIngester(ingester, params);
25
+ results.push(result);
26
+ }
27
+ return results;
28
+ }
29
+ mergeOptions(params) {
30
+ const merged = { ...this.ingesterOptions };
31
+ if (params?.fromDate)
32
+ merged.fromDate = new Date(params.fromDate);
33
+ if (params?.toDate)
34
+ merged.toDate = new Date(params.toDate);
35
+ if (params?.dimensions?.length)
36
+ merged.dimensions = params.dimensions;
37
+ return merged;
38
+ }
39
+ async runIngester(ingester, params) {
40
+ const ingestionId = `ingest-${ingester.source}-${Date.now()}`;
41
+ const run = {
42
+ id: ingestionId,
43
+ source: ingester.source,
44
+ status: "running",
45
+ resultsCount: 0,
46
+ startedAt: new Date,
47
+ completedAt: null,
48
+ error: null
49
+ };
50
+ await this.store.createIngestionRun(run);
51
+ try {
52
+ const opts = this.mergeOptions(params);
53
+ const rawResults = await ingester.ingest(opts);
54
+ const normalized = normalizeBenchmarkResults(rawResults);
55
+ for (const result of normalized) {
56
+ await this.store.upsertBenchmarkResult(result);
57
+ }
58
+ await this.store.updateIngestionRun(ingestionId, {
59
+ status: "completed",
60
+ resultsCount: normalized.length,
61
+ completedAt: new Date
62
+ });
63
+ return {
64
+ ingestionId,
65
+ source: ingester.source,
66
+ resultsCount: normalized.length,
67
+ status: "completed"
68
+ };
69
+ } catch (error) {
70
+ const errorMessage = error instanceof Error ? error.message : String(error);
71
+ await this.store.updateIngestionRun(ingestionId, {
72
+ status: "failed",
73
+ completedAt: new Date,
74
+ error: errorMessage
75
+ });
76
+ return {
77
+ ingestionId,
78
+ source: ingester.source,
79
+ resultsCount: 0,
80
+ status: "failed"
81
+ };
82
+ }
83
+ }
84
+ }
85
+
86
+ // src/pipeline/ranking-pipeline.ts
87
+ import { computeModelRankings } from "@contractspec/lib.provider-ranking/scoring";
88
+
89
+ class RankingPipeline {
90
+ store;
91
+ constructor(options) {
92
+ this.store = options.store;
93
+ }
94
+ async refresh(params) {
95
+ let allResults = await this.loadAllBenchmarkResults();
96
+ if (params?.dimensions?.length) {
97
+ const dimSet = new Set(params.dimensions);
98
+ allResults = allResults.filter((r) => dimSet.has(r.dimension));
99
+ }
100
+ const existingRankings = params?.forceRecalculate ? new Map : new Map((await this.store.listModelRankings({
101
+ limit: 1e4,
102
+ requiredTransport: params?.requiredTransport,
103
+ requiredAuthMethod: params?.requiredAuthMethod
104
+ })).rankings.map((r) => [r.modelId, r]));
105
+ const newRankings = computeModelRankings(allResults, params?.weightOverrides ? { weightOverrides: params.weightOverrides } : undefined, existingRankings);
106
+ for (const ranking of newRankings) {
107
+ await this.store.upsertModelRanking(ranking);
108
+ }
109
+ return {
110
+ modelsRanked: newRankings.length,
111
+ updatedAt: new Date
112
+ };
113
+ }
114
+ async loadAllBenchmarkResults() {
115
+ const pageSize = 500;
116
+ let offset = 0;
117
+ const allResults = [];
118
+ while (true) {
119
+ const page = await this.store.listBenchmarkResults({
120
+ limit: pageSize,
121
+ offset
122
+ });
123
+ allResults.push(...page.results);
124
+ if (allResults.length >= page.total || page.results.length < pageSize) {
125
+ break;
126
+ }
127
+ offset += pageSize;
128
+ }
129
+ return allResults;
130
+ }
131
+ }
132
+ export {
133
+ RankingPipeline,
134
+ IngestionPipeline
135
+ };