@danypops/jittor 0.8.0 → 0.9.0
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/README.md +4 -2
- package/extension/src/benchmark-tui.ts +1 -1
- package/package.json +1 -1
- package/src/adapters/artificial-analysis-direct-source.ts +80 -0
- package/src/adapters/lmarena-hf-source.ts +111 -0
- package/src/daemon.ts +9 -6
- package/src/domain/model-observation.ts +1 -1
- package/src/adapters/openrouter-benchmark-index-source.ts +0 -94
package/README.md
CHANGED
|
@@ -80,9 +80,11 @@ Token-budget thresholds are optional and must be configured by the user; Jittor
|
|
|
80
80
|
|
|
81
81
|
### Benchmark evidence
|
|
82
82
|
|
|
83
|
-
Jittor can ingest bounded OpenRouter model metadata, p50 latency/throughput ordering,
|
|
83
|
+
Jittor can ingest bounded OpenRouter model metadata, p50 latency/throughput ordering, and Design Arena Elo rankings as provenance-bearing evidence without treating OpenRouter as model-scope authority. Enable online ingestion explicitly with `JITTOR_OPENROUTER_BENCHMARKS=1`; it is off by default. OpenRouter model metadata and operational ordering are public; Design Arena ingestion additionally uses `OPENROUTER_API_KEY` from the supervised service environment without retaining it. Snapshots preserve the upstream publisher, normalized model identities, immutable retrieval revisions, source URLs, confidence, license terms, and explicit freshness deadlines. A malformed or oversized refresh leaves the last complete snapshot visible and records only a payload-safe failure state.
|
|
84
84
|
|
|
85
|
-
Design Arena rates models across dozens of arena/category pairs (music, video, text-to-speech, ASCII art, ...); Jittor ingests only the bounded allowlist of categories (`codecategories`, `website`, `uicomponent`, `dataviz`, `svg`) that measure frontend/UI-generation skill relevant to routing a coding agent, tagged into one `design` domain distinct from `coding`. A model with no OpenRouter-reachable identity (proprietary platforms, image/video generators) is skipped rather than fabricated into unroutable evidence.
|
|
85
|
+
Design Arena rates models across dozens of arena/category pairs (music, video, text-to-speech, ASCII art, ...); Jittor ingests only the bounded allowlist of categories (`codecategories`, `website`, `uicomponent`, `dataviz`, `svg`) that measure frontend/UI-generation skill relevant to routing a coding agent, tagged into one `design` domain distinct from `coding`. A model with no OpenRouter-reachable identity (proprietary platforms, image/video generators) is skipped rather than fabricated into unroutable evidence. Kept on the OpenRouter passthrough rather than migrated to a direct integration: Design Arena's own native API requires a manually reviewed application (1-2 business days), unlike Artificial Analysis's instant self-serve signup below.
|
|
86
|
+
|
|
87
|
+
Jittor also ingests LMArena's own official Hugging Face dataset (`lmarena-ai/leaderboard-dataset`, via the public `datasets-server.huggingface.co` API -- no credential required) for its Code Arena (`webdev`) and Agent Arena human-preference battles, and, when `ARTIFICIAL_ANALYSIS_API_KEY` is configured, Artificial Analysis's own direct API (replaces the former OpenRouter passthrough to the same publisher; adds a `math` domain and measured per-model latency the passthrough never exposed). LMArena's Bradley-Terry/IPS ratings aren't on the same scale as Artificial Analysis's 0-100 indices, so they're tagged under distinct `-arena`-suffixed dimensions (`quality-coding-arena`, `quality-type-planning-arena`) instead of blended into the same average -- stored and queryable on their own, not yet part of the default ranked "quality" score.
|
|
86
88
|
|
|
87
89
|
Use the authenticated CLI channels independently:
|
|
88
90
|
|
|
@@ -85,7 +85,7 @@ export async function showBenchmarkPanel(
|
|
|
85
85
|
context: MODEL_RANKING_DEFAULT_CONTEXT_WEIGHT,
|
|
86
86
|
reliability: MODEL_RANKING_DEFAULT_RELIABILITY_WEIGHT,
|
|
87
87
|
},
|
|
88
|
-
sourceIds: ["openrouter-models", "
|
|
88
|
+
sourceIds: ["openrouter-models", "lmarena-hf", "artificial-analysis-direct", "openrouter-design-arena"],
|
|
89
89
|
}) as ModelRankingResult;
|
|
90
90
|
if (ctx.mode !== "tui") {
|
|
91
91
|
ctx.ui.notify(renderBenchmarkView(result, currentIdentity, 100, { fg: (_color, text) => text, bold: (text) => text }).join("\n"), "info");
|
package/package.json
CHANGED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { BENCHMARK_MAX_MODELS_PER_SOURCE, BENCHMARK_REFRESH_INTERVAL_MS, BENCHMARK_SOURCE_MAX_RESPONSE_BYTES } from "../constants.ts";
|
|
2
|
+
import { normalizeModelIdentity, validateBenchmarkObservation, type BenchmarkObservation, type BenchmarkSourceSnapshot } from "../domain/benchmark.ts";
|
|
3
|
+
import type { BenchmarkSource } from "../ports/benchmark-source.ts";
|
|
4
|
+
import { contractRecord } from "../providers/openrouter-contracts.ts";
|
|
5
|
+
|
|
6
|
+
const SOURCE_ID = "artificial-analysis-direct";
|
|
7
|
+
const ENDPOINT = "https://artificialanalysis.ai/api/v2/data/llms/models";
|
|
8
|
+
|
|
9
|
+
export type ArtificialAnalysisTransport = (request: Request) => Promise<Response>;
|
|
10
|
+
|
|
11
|
+
function requiredText(value: unknown, name: string): string {
|
|
12
|
+
if (typeof value !== "string" || value.length === 0 || value.length > 500) throw new Error(`Artificial Analysis ${name} schema changed`);
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function requiredNumber(value: unknown, name: string): number {
|
|
17
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`Artificial Analysis ${name} schema changed`);
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Replaces the removed OpenRouter passthrough to the same publisher; adds math_index and measured latency it never exposed. Same dimension names, since it's the same facts. */
|
|
22
|
+
export class ArtificialAnalysisDirectSource implements BenchmarkSource {
|
|
23
|
+
readonly id = SOURCE_ID;
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
private readonly apiKey: string,
|
|
27
|
+
private readonly transport: ArtificialAnalysisTransport = fetch,
|
|
28
|
+
private readonly clock: () => number = Date.now,
|
|
29
|
+
) {
|
|
30
|
+
if (apiKey.length === 0) throw new Error("Artificial Analysis API key is required");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async fetch(): Promise<BenchmarkSourceSnapshot> {
|
|
34
|
+
const retrievedAt = this.clock();
|
|
35
|
+
if (!Number.isSafeInteger(retrievedAt) || retrievedAt <= 0) throw new Error("benchmark retrieval time is invalid");
|
|
36
|
+
const response = await this.transport(new Request(ENDPOINT, { headers: { "x-api-key": this.apiKey } }));
|
|
37
|
+
if (!response.ok) throw new Error(`Artificial Analysis benchmarks failed with HTTP ${response.status}`);
|
|
38
|
+
const text = await response.text();
|
|
39
|
+
if (new TextEncoder().encode(text).byteLength > BENCHMARK_SOURCE_MAX_RESPONSE_BYTES) throw new Error("Artificial Analysis benchmark response exceeds the size limit");
|
|
40
|
+
let payload: unknown;
|
|
41
|
+
try { payload = JSON.parse(text); } catch { throw new Error("Artificial Analysis benchmark response is not valid JSON"); }
|
|
42
|
+
const root = contractRecord(payload, "benchmark response");
|
|
43
|
+
if (!Array.isArray(root["data"]) || root["data"].length > BENCHMARK_MAX_MODELS_PER_SOURCE) throw new Error("Artificial Analysis benchmark result count is invalid");
|
|
44
|
+
const revision = String(retrievedAt);
|
|
45
|
+
const observations = root["data"].flatMap((value): BenchmarkObservation[] => {
|
|
46
|
+
const row = contractRecord(value, "benchmark row");
|
|
47
|
+
const creator = contractRecord(row["model_creator"], "model creator");
|
|
48
|
+
const evaluations = contractRecord(row["evaluations"], "evaluations");
|
|
49
|
+
const identity = normalizeModelIdentity(requiredText(creator["slug"], "creator slug"), requiredText(row["slug"], "model slug"), [`artificial-analysis/${requiredText(row["id"], "model id")}`]);
|
|
50
|
+
const provenance = {
|
|
51
|
+
sourceId: SOURCE_ID,
|
|
52
|
+
sourceType: "creator" as const,
|
|
53
|
+
publisher: "Artificial Analysis",
|
|
54
|
+
url: "https://artificialanalysis.ai/",
|
|
55
|
+
revision,
|
|
56
|
+
publishedAt: retrievedAt,
|
|
57
|
+
retrievedAt,
|
|
58
|
+
freshUntil: retrievedAt + BENCHMARK_REFRESH_INTERVAL_MS,
|
|
59
|
+
license: "Attribution to artificialanalysis.ai required; see their free API terms",
|
|
60
|
+
confidence: 0.85,
|
|
61
|
+
};
|
|
62
|
+
const methodology = { basis: "Artificial Analysis direct API, /data/llms/models" };
|
|
63
|
+
const observations: BenchmarkObservation[] = [];
|
|
64
|
+
const index = (field: string, dimension: string): void => {
|
|
65
|
+
const raw = evaluations[field];
|
|
66
|
+
if (raw === undefined || raw === null) return;
|
|
67
|
+
observations.push(validateBenchmarkObservation({ model: identity, dimension, value: requiredNumber(raw, field), unit: "ratio", provenance, methodology }));
|
|
68
|
+
};
|
|
69
|
+
index("artificial_analysis_coding_index", "quality-coding");
|
|
70
|
+
index("artificial_analysis_intelligence_index", "quality-general");
|
|
71
|
+
index("artificial_analysis_math_index", "quality-math");
|
|
72
|
+
const ttft = row["median_time_to_first_token_seconds"];
|
|
73
|
+
if (typeof ttft === "number" && Number.isFinite(ttft)) {
|
|
74
|
+
observations.push(validateBenchmarkObservation({ model: identity, dimension: "latency", value: ttft * 1_000, unit: "milliseconds", provenance, methodology }));
|
|
75
|
+
}
|
|
76
|
+
return observations;
|
|
77
|
+
});
|
|
78
|
+
return { sourceId: this.id, snapshotId: `${this.id}:${revision}`, retrievedAt, observations };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { BENCHMARK_MAX_MODELS_PER_SOURCE, BENCHMARK_REFRESH_INTERVAL_MS, BENCHMARK_SOURCE_MAX_RESPONSE_BYTES } from "../constants.ts";
|
|
2
|
+
import { normalizeModelIdentity, validateBenchmarkObservation, type BenchmarkObservation, type BenchmarkSourceSnapshot } from "../domain/benchmark.ts";
|
|
3
|
+
import type { BenchmarkSource } from "../ports/benchmark-source.ts";
|
|
4
|
+
import { contractRecord } from "../providers/openrouter-contracts.ts";
|
|
5
|
+
|
|
6
|
+
const SOURCE_ID = "lmarena-hf";
|
|
7
|
+
const BASE_URL = "https://datasets-server.huggingface.co/rows";
|
|
8
|
+
/** The server's own per-page cap; observed directly against the live API. */
|
|
9
|
+
const HF_ROWS_PER_PAGE = 100;
|
|
10
|
+
|
|
11
|
+
export type LmArenaTransport = (request: Request) => Promise<Response>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Limited to webdev (Code Arena) and agent (Agent Arena); text/text_style_control's "coding"
|
|
15
|
+
* category exists but its "latest" split spans tens of thousands of rows, unbounded compared to
|
|
16
|
+
* these two's few hundred.
|
|
17
|
+
*
|
|
18
|
+
* Dimensions are distinct from AA's (quality-coding-arena, not quality-coding) because the
|
|
19
|
+
* scales don't match: Bradley-Terry rating (~1200-1700) and IPS score (~0) vs AA's 0-100 ratio.
|
|
20
|
+
* withEvidence() averages same-named dimensions with no unit conversion, so sharing a name would
|
|
21
|
+
* blend incompatible scales into a meaningless number. Stored and queryable on their own,
|
|
22
|
+
* intentionally not folded into models.rank's blended "quality" component yet.
|
|
23
|
+
*/
|
|
24
|
+
const ARENAS: ReadonlyArray<{ config: string; dimension: string; unit: "ratio" }> = [
|
|
25
|
+
{ config: "webdev", dimension: "quality-coding-arena", unit: "ratio" },
|
|
26
|
+
{ config: "agent", dimension: "quality-type-planning-arena", unit: "ratio" },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const THINKING_SUFFIX = /\s*\((?:high|xhigh|low|medium|max|fast|thinking|codex-harness)\)\s*$/i;
|
|
30
|
+
|
|
31
|
+
function requiredText(value: unknown, name: string): string {
|
|
32
|
+
if (typeof value !== "string" || value.length === 0 || value.length > 500) throw new Error(`LMArena benchmark ${name} schema changed`);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function requiredNumber(value: unknown, name: string): number {
|
|
37
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`LMArena benchmark ${name} schema changed`);
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Best-effort: LMArena has no machine identity field, just a display name and org slug. A wrong
|
|
43
|
+
* guess is safe, not a correctness bug -- identity only ever matches exactly against real
|
|
44
|
+
* candidates the caller already supplied, so a bad slug or an unreleased codename ("Inkling")
|
|
45
|
+
* is just inert evidence, never misattributed evidence.
|
|
46
|
+
*/
|
|
47
|
+
function bestEffortIdentity(organization: string, displayName: string): { provider: string; model: string; aliases: string[] } {
|
|
48
|
+
const base = displayName.replace(THINKING_SUFFIX, "").trim();
|
|
49
|
+
const slug = base.toLowerCase().replace(/[^a-z0-9.]+/g, "-").replace(/^-+|-+$/g, "");
|
|
50
|
+
return { provider: organization, model: slug, aliases: [displayName.toLowerCase()] };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class LmArenaHfSource implements BenchmarkSource {
|
|
54
|
+
readonly id = SOURCE_ID;
|
|
55
|
+
|
|
56
|
+
constructor(
|
|
57
|
+
private readonly transport: LmArenaTransport = fetch,
|
|
58
|
+
private readonly clock: () => number = Date.now,
|
|
59
|
+
) {}
|
|
60
|
+
|
|
61
|
+
async fetch(): Promise<BenchmarkSourceSnapshot> {
|
|
62
|
+
const retrievedAt = this.clock();
|
|
63
|
+
if (!Number.isSafeInteger(retrievedAt) || retrievedAt <= 0) throw new Error("benchmark retrieval time is invalid");
|
|
64
|
+
const perArena = await Promise.all(ARENAS.map((arena) => this.fetchArena(arena, retrievedAt)));
|
|
65
|
+
const observations = perArena.flatMap((page) => page.observations).slice(0, BENCHMARK_MAX_MODELS_PER_SOURCE * ARENAS.length);
|
|
66
|
+
const asOf = perArena.map((page) => page.asOf).sort().at(-1) ?? "unknown";
|
|
67
|
+
return { sourceId: this.id, snapshotId: `${this.id}:${asOf}`, retrievedAt, observations };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private async fetchArena(arena: { config: string; dimension: string; unit: "ratio" }, retrievedAt: number): Promise<{ asOf: string; observations: BenchmarkObservation[] }> {
|
|
71
|
+
const url = `${BASE_URL}?dataset=lmarena-ai%2Fleaderboard-dataset&config=${arena.config}&split=latest&length=${HF_ROWS_PER_PAGE}`;
|
|
72
|
+
const response = await this.transport(new Request(url));
|
|
73
|
+
if (!response.ok) throw new Error(`LMArena benchmark fetch failed with HTTP ${response.status}`);
|
|
74
|
+
const text = await response.text();
|
|
75
|
+
if (new TextEncoder().encode(text).byteLength > BENCHMARK_SOURCE_MAX_RESPONSE_BYTES) throw new Error("LMArena benchmark response exceeds the size limit");
|
|
76
|
+
let payload: unknown;
|
|
77
|
+
try { payload = JSON.parse(text); } catch { throw new Error("LMArena benchmark response is not valid JSON"); }
|
|
78
|
+
const root = contractRecord(payload, "benchmark response");
|
|
79
|
+
if (!Array.isArray(root["rows"]) || root["rows"].length > HF_ROWS_PER_PAGE) throw new Error("LMArena benchmark row count is invalid");
|
|
80
|
+
let asOf = "";
|
|
81
|
+
const observations = root["rows"].flatMap((entry): BenchmarkObservation[] => {
|
|
82
|
+
const wrapper = contractRecord(entry, "benchmark row wrapper");
|
|
83
|
+
const row = contractRecord(wrapper["row"], "benchmark row");
|
|
84
|
+
const publishDate = requiredText(row["leaderboard_publish_date"], "publish date");
|
|
85
|
+
if (publishDate > asOf) asOf = publishDate;
|
|
86
|
+
// Skip finer per-category splits, if any exist for this config -- out of scope for now.
|
|
87
|
+
if (requiredText(row["category"], "category") !== "overall") return [];
|
|
88
|
+
const scoreValue = row["rating"] ?? row["score"];
|
|
89
|
+
const value = requiredNumber(scoreValue, "score");
|
|
90
|
+
const organization = requiredText(row["organization"], "organization");
|
|
91
|
+
const displayName = requiredText(row["model_name"], "model name");
|
|
92
|
+
const guessed = bestEffortIdentity(organization, displayName);
|
|
93
|
+
const identity = normalizeModelIdentity(guessed.provider, guessed.model, guessed.aliases);
|
|
94
|
+
const provenance = {
|
|
95
|
+
sourceId: SOURCE_ID,
|
|
96
|
+
sourceType: "preference" as const,
|
|
97
|
+
publisher: "LMArena",
|
|
98
|
+
url: "https://huggingface.co/datasets/lmarena-ai/leaderboard-dataset",
|
|
99
|
+
revision: `${arena.config}:${publishDate}`,
|
|
100
|
+
publishedAt: Date.parse(publishDate) || null,
|
|
101
|
+
retrievedAt,
|
|
102
|
+
freshUntil: retrievedAt + BENCHMARK_REFRESH_INTERVAL_MS,
|
|
103
|
+
license: "See lmarena-ai/leaderboard-dataset on Hugging Face for per-model license terms",
|
|
104
|
+
confidence: 0.6,
|
|
105
|
+
};
|
|
106
|
+
const methodology = { basis: "LMArena human-preference battles", arena: arena.config, displayName, publishDate };
|
|
107
|
+
return [validateBenchmarkObservation({ model: identity, dimension: arena.dimension, value, unit: arena.unit, provenance, methodology })];
|
|
108
|
+
});
|
|
109
|
+
return { asOf, observations };
|
|
110
|
+
}
|
|
111
|
+
}
|
package/src/daemon.ts
CHANGED
|
@@ -3,9 +3,10 @@ import { MAINTENANCE_INTERVAL_MS, TELEMETRY_POLL_INTERVAL_MS } from "./constants
|
|
|
3
3
|
import { DEFAULT_POLICY, UNCONFIGURED_ROUTE } from "./config.ts";
|
|
4
4
|
import { SQLiteMetricStore } from "./adapters/sqlite-metric-store.ts";
|
|
5
5
|
import { MetricBenchmarkStore } from "./adapters/metric-benchmark-store.ts";
|
|
6
|
-
import { OpenRouterBenchmarkIndexSource } from "./adapters/openrouter-benchmark-index-source.ts";
|
|
7
6
|
import { OpenRouterBenchmarkSource } from "./adapters/openrouter-benchmark-source.ts";
|
|
8
7
|
import { OpenRouterDesignArenaSource } from "./adapters/openrouter-design-arena-source.ts";
|
|
8
|
+
import { LmArenaHfSource } from "./adapters/lmarena-hf-source.ts";
|
|
9
|
+
import { ArtificialAnalysisDirectSource } from "./adapters/artificial-analysis-direct-source.ts";
|
|
9
10
|
import { openJittorDb } from "./db.ts";
|
|
10
11
|
import { BenchmarkCatalog } from "./domain/benchmark.ts";
|
|
11
12
|
import { EvidenceModelRanker } from "./domain/model-ranking-service.ts";
|
|
@@ -26,13 +27,15 @@ export function reportMaintenanceFailure(event: string, error: unknown): void {
|
|
|
26
27
|
logEvent("error", event, { message: error instanceof Error ? error.message : String(error) });
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
// Flag name predates non-OpenRouter sources; kept as the one "opt into online benchmark
|
|
31
|
+
// ingestion" toggle rather than adding a second flag for the same decision.
|
|
29
32
|
export function benchmarkSourcesFromEnvironment(env: Record<string, string | undefined> = process.env): BenchmarkSource[] {
|
|
30
33
|
if (env["JITTOR_OPENROUTER_BENCHMARKS"] !== "1") return [];
|
|
31
|
-
const sources: BenchmarkSource[] = [new OpenRouterBenchmarkSource()];
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
const sources: BenchmarkSource[] = [new OpenRouterBenchmarkSource(), new LmArenaHfSource()];
|
|
35
|
+
// Design Arena's own API needs manual approval, unlike Artificial Analysis's instant signup --
|
|
36
|
+
// no direct alternative exists yet, so the OpenRouter passthrough stays.
|
|
37
|
+
if (env["OPENROUTER_API_KEY"]) sources.push(new OpenRouterDesignArenaSource(env["OPENROUTER_API_KEY"]));
|
|
38
|
+
if (env["ARTIFICIAL_ANALYSIS_API_KEY"]) sources.push(new ArtificialAnalysisDirectSource(env["ARTIFICIAL_ANALYSIS_API_KEY"]));
|
|
36
39
|
return sources;
|
|
37
40
|
}
|
|
38
41
|
|
|
@@ -16,7 +16,7 @@ import type { MetricObservation, MetricUnit, StoredMetricObservation } from "./m
|
|
|
16
16
|
* usage carries no distinguishing signal for that axis; a run can score coding on domain and
|
|
17
17
|
* research on type simultaneously (e.g. reading a file, then searching the web in one turn).
|
|
18
18
|
*/
|
|
19
|
-
export const TASK_DOMAINS = ["coding", "design", "general"] as const;
|
|
19
|
+
export const TASK_DOMAINS = ["coding", "design", "math", "general"] as const;
|
|
20
20
|
export type ModelTaskDomain = typeof TASK_DOMAINS[number];
|
|
21
21
|
export const TASK_TYPES = ["research", "planning", "general"] as const;
|
|
22
22
|
export type ModelTaskType = typeof TASK_TYPES[number];
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BENCHMARK_MAX_MODELS_PER_SOURCE,
|
|
3
|
-
BENCHMARK_REFRESH_INTERVAL_MS,
|
|
4
|
-
BENCHMARK_SOURCE_MAX_RESPONSE_BYTES,
|
|
5
|
-
} from "../constants.ts";
|
|
6
|
-
import { normalizeModelIdentity, validateBenchmarkObservation, type BenchmarkObservation, type BenchmarkSourceSnapshot } from "../domain/benchmark.ts";
|
|
7
|
-
import type { BenchmarkSource } from "../ports/benchmark-source.ts";
|
|
8
|
-
import { contractRecord } from "../providers/openrouter-contracts.ts";
|
|
9
|
-
import type { OpenRouterBenchmarkTransport } from "./openrouter-benchmark-source.ts";
|
|
10
|
-
|
|
11
|
-
const SOURCE_ID = "openrouter-artificial-analysis";
|
|
12
|
-
const ENDPOINT = `https://openrouter.ai/api/v1/benchmarks?source=artificial-analysis&task_type=coding&max_results=${BENCHMARK_MAX_MODELS_PER_SOURCE}`;
|
|
13
|
-
|
|
14
|
-
function requiredText(value: unknown, name: string): string {
|
|
15
|
-
if (typeof value !== "string" || value.length === 0 || value.length > 500) throw new Error(`OpenRouter benchmark ${name} schema changed`);
|
|
16
|
-
return value;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function requiredNumber(value: unknown, name: string): number {
|
|
20
|
-
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`OpenRouter benchmark ${name} schema changed`);
|
|
21
|
-
return value;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function price(value: unknown, name: string): number {
|
|
25
|
-
const parsed = Number(requiredText(value, name));
|
|
26
|
-
if (!Number.isFinite(parsed) || parsed < 0) throw new Error(`OpenRouter benchmark ${name} schema changed`);
|
|
27
|
-
return parsed;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export class OpenRouterBenchmarkIndexSource implements BenchmarkSource {
|
|
31
|
-
readonly id = SOURCE_ID;
|
|
32
|
-
|
|
33
|
-
constructor(
|
|
34
|
-
private readonly apiKey: string,
|
|
35
|
-
private readonly transport: OpenRouterBenchmarkTransport = fetch,
|
|
36
|
-
private readonly clock: () => number = Date.now,
|
|
37
|
-
) {
|
|
38
|
-
if (apiKey.length === 0) throw new Error("OpenRouter API key is required for benchmark indices");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async fetch(): Promise<BenchmarkSourceSnapshot> {
|
|
42
|
-
const response = await this.transport(new Request(ENDPOINT, { headers: { authorization: `Bearer ${this.apiKey}` } }));
|
|
43
|
-
if (!response.ok) throw new Error(`OpenRouter benchmarks failed with HTTP ${response.status}`);
|
|
44
|
-
const text = await response.text();
|
|
45
|
-
if (new TextEncoder().encode(text).byteLength > BENCHMARK_SOURCE_MAX_RESPONSE_BYTES) throw new Error("OpenRouter benchmark response exceeds the size limit");
|
|
46
|
-
let payload: unknown;
|
|
47
|
-
try { payload = JSON.parse(text); } catch { throw new Error("OpenRouter benchmark response is not valid JSON"); }
|
|
48
|
-
const root = contractRecord(payload, "benchmark response");
|
|
49
|
-
const meta = contractRecord(root["meta"], "benchmark metadata");
|
|
50
|
-
if (!Array.isArray(root["data"]) || root["data"].length === 0 || root["data"].length > BENCHMARK_MAX_MODELS_PER_SOURCE) throw new Error("OpenRouter benchmark result count is invalid");
|
|
51
|
-
if (requiredText(meta["source"], "source") !== "artificial-analysis") throw new Error("OpenRouter benchmark source schema changed");
|
|
52
|
-
const version = requiredText(meta["version"], "version");
|
|
53
|
-
const asOf = requiredText(meta["as_of"], "as-of date");
|
|
54
|
-
const publishedAt = Date.parse(asOf);
|
|
55
|
-
if (!Number.isSafeInteger(publishedAt) || publishedAt <= 0) throw new Error("OpenRouter benchmark publication date schema changed");
|
|
56
|
-
const upstreamUrl = new URL(requiredText(meta["source_url"], "source URL"));
|
|
57
|
-
if (upstreamUrl.protocol !== "https:") throw new Error("OpenRouter benchmark source URL must use HTTPS");
|
|
58
|
-
const retrievedAt = this.clock();
|
|
59
|
-
if (!Number.isSafeInteger(retrievedAt) || retrievedAt <= 0) throw new Error("benchmark retrieval time is invalid");
|
|
60
|
-
const revision = `${version}:${asOf}`;
|
|
61
|
-
const observations = root["data"].flatMap((value): BenchmarkObservation[] => {
|
|
62
|
-
const row = contractRecord(value, "benchmark row");
|
|
63
|
-
if (requiredText(row["source"], "row source") !== "artificial-analysis") throw new Error("OpenRouter benchmark row source schema changed");
|
|
64
|
-
const permaslug = requiredText(row["model_permaslug"], "model permaslug");
|
|
65
|
-
const separator = permaslug.indexOf("/");
|
|
66
|
-
if (separator <= 0 || separator === permaslug.length - 1) throw new Error("OpenRouter benchmark model identity schema changed");
|
|
67
|
-
const model = normalizeModelIdentity(permaslug.slice(0, separator), permaslug.slice(separator + 1), [`openrouter/${permaslug}`]);
|
|
68
|
-
const pricing = contractRecord(row["pricing"], "benchmark pricing");
|
|
69
|
-
const provenance = {
|
|
70
|
-
sourceId: SOURCE_ID,
|
|
71
|
-
sourceType: "independent" as const,
|
|
72
|
-
publisher: "Artificial Analysis via OpenRouter",
|
|
73
|
-
url: ENDPOINT,
|
|
74
|
-
revision,
|
|
75
|
-
publishedAt,
|
|
76
|
-
retrievedAt,
|
|
77
|
-
freshUntil: retrievedAt + BENCHMARK_REFRESH_INTERVAL_MS,
|
|
78
|
-
license: "OpenRouter API terms; upstream terms apply",
|
|
79
|
-
confidence: 0.8,
|
|
80
|
-
};
|
|
81
|
-
const common = { model, provenance };
|
|
82
|
-
const methodology = { basis: "Artificial Analysis index via OpenRouter", upstreamUrl: upstreamUrl.toString(), version, asOf };
|
|
83
|
-
return [
|
|
84
|
-
validateBenchmarkObservation({ ...common, dimension: "quality-coding", value: requiredNumber(row["coding_index"], "coding index"), unit: "ratio", methodology }),
|
|
85
|
-
validateBenchmarkObservation({ ...common, dimension: "quality-general", value: requiredNumber(row["intelligence_index"], "intelligence index"), unit: "ratio", methodology }),
|
|
86
|
-
// agentic_index measures tool-use/agentic execution style, an activity (type), not a subject-matter domain.
|
|
87
|
-
validateBenchmarkObservation({ ...common, dimension: "quality-type-planning", value: requiredNumber(row["agentic_index"], "agentic index"), unit: "ratio", methodology }),
|
|
88
|
-
validateBenchmarkObservation({ ...common, dimension: "price-input", value: price(pricing["prompt"], "prompt pricing"), unit: "usd", methodology: { ...methodology, basis: "OpenRouter USD per input token" } }),
|
|
89
|
-
validateBenchmarkObservation({ ...common, dimension: "price-output", value: price(pricing["completion"], "completion pricing"), unit: "usd", methodology: { ...methodology, basis: "OpenRouter USD per output token" } }),
|
|
90
|
-
];
|
|
91
|
-
});
|
|
92
|
-
return { sourceId: this.id, snapshotId: `${this.id}:${revision}`, retrievedAt, observations };
|
|
93
|
-
}
|
|
94
|
-
}
|