@danypops/jittor 0.16.3 → 0.18.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.
Files changed (97) hide show
  1. package/README.md +38 -13
  2. package/docs/COMPACTION_EFFECTIVENESS.md +34 -0
  3. package/docs/CONTEXT_SNAPSHOTS.md +53 -0
  4. package/docs/MODEL_CATALOG.md +43 -0
  5. package/docs/OTLP_EXPORT.md +63 -0
  6. package/docs/PROVIDER_RESEARCH.md +1 -1
  7. package/docs/TOKEN_MEASUREMENT.md +53 -0
  8. package/docs/USAGE_BACKFILL.md +35 -0
  9. package/package.json +5 -4
  10. package/src/{providers/anthropic-contracts.ts → anthropic/rate-limits.ts} +1 -1
  11. package/src/{adapters/artificial-analysis-direct-source.ts → artificial-analysis/benchmark-source.ts} +3 -3
  12. package/src/cli-commands/backfill.ts +51 -0
  13. package/src/cli-commands/benchmarks.ts +4 -4
  14. package/src/cli-commands/cache.ts +120 -0
  15. package/src/cli-commands/catalog.ts +123 -0
  16. package/src/cli-commands/compaction.ts +1 -1
  17. package/src/cli-commands/context.ts +77 -4
  18. package/src/cli-commands/export.ts +30 -0
  19. package/src/cli-commands/metrics.ts +8 -2
  20. package/src/cli-commands/op.ts +1 -1
  21. package/src/cli-commands/route-args.ts +2 -2
  22. package/src/cli-commands/router.ts +2 -2
  23. package/src/cli-commands/support.ts +2 -2
  24. package/src/cli.ts +59 -21
  25. package/src/{providers/codex-contracts.ts → codex/contracts.ts} +1 -1
  26. package/src/codex/source.ts +53 -0
  27. package/src/{providers/codex.ts → codex/telemetry.ts} +2 -2
  28. package/src/constants.ts +31 -0
  29. package/src/daemon.ts +53 -23
  30. package/src/{providers/google-vertex-budget-contracts.ts → google-vertex/budget-contracts.ts} +3 -3
  31. package/src/{providers/google-vertex-budget.ts → google-vertex/budget-telemetry.ts} +6 -6
  32. package/src/{providers/google-vertex-contracts.ts → google-vertex/failures.ts} +1 -1
  33. package/src/google-vertex/source.ts +35 -0
  34. package/src/index.ts +149 -55
  35. package/src/{adapters/lmarena-hf-source.ts → lmarena/benchmark-source.ts} +3 -3
  36. package/src/observability/budget.ts +14 -0
  37. package/src/observability/cache-economics.ts +623 -0
  38. package/src/observability/context-delta.ts +309 -0
  39. package/src/{domain → observability}/context-hub.ts +19 -2
  40. package/src/observability/context-snapshot-history.ts +281 -0
  41. package/src/{domain → observability}/context-telemetry.ts +232 -4
  42. package/src/observability/model-identity.ts +42 -0
  43. package/src/{domain → observability}/model-observation.ts +26 -11
  44. package/src/{ports/metric-store.ts → observability/store.ts} +2 -2
  45. package/src/{ports → observability}/telemetry-source.ts +2 -2
  46. package/src/observability/token-measurement.ts +194 -0
  47. package/src/observability/usage-import.ts +122 -0
  48. package/src/openai/token-counter.ts +63 -0
  49. package/src/{adapters/openrouter-benchmark-source.ts → openrouter/benchmark-source.ts} +3 -3
  50. package/src/{providers/openrouter-contracts.ts → openrouter/contracts.ts} +1 -1
  51. package/src/{adapters/openrouter-design-arena-source.ts → openrouter/design-arena-source.ts} +4 -4
  52. package/src/openrouter/source.ts +20 -0
  53. package/src/{providers/openrouter.ts → openrouter/telemetry.ts} +2 -2
  54. package/src/{domain → optimization/model-selection}/benchmark.ts +9 -27
  55. package/src/optimization/model-selection/catalog-store.ts +97 -0
  56. package/src/optimization/model-selection/catalog.ts +478 -0
  57. package/src/{ports/benchmark-controller.ts → optimization/model-selection/controller.ts} +1 -1
  58. package/src/{adapters/metric-benchmark-store.ts → optimization/model-selection/observation-store.ts} +5 -5
  59. package/src/{domain/model-ranking-service.ts → optimization/model-selection/ranker.ts} +6 -12
  60. package/src/{domain/model-ranking.ts → optimization/model-selection/ranking.ts} +8 -2
  61. package/src/{ports/benchmark-source.ts → optimization/model-selection/source.ts} +1 -1
  62. package/src/{ports/benchmark-store.ts → optimization/model-selection/store.ts} +1 -1
  63. package/src/{domain/codex-recovery.ts → optimization/recovery/codex.ts} +1 -1
  64. package/src/{ports/router-controller.ts → optimization/routing/controller.ts} +1 -1
  65. package/src/{policy.ts → optimization/routing/policy.ts} +4 -14
  66. package/src/{router.ts → optimization/routing/router.ts} +4 -10
  67. package/src/otlp/exporter.ts +354 -0
  68. package/src/pi/session-usage-source.ts +203 -0
  69. package/src/{session-identity-service.ts → sessions/identity.ts} +2 -2
  70. package/src/{operations/session-scope.ts → sessions/router-authorization.ts} +1 -1
  71. package/src/{db.ts → sqlite/database.ts} +14 -1
  72. package/src/{adapters/sqlite-metric-store.ts → sqlite/metric-store.ts} +4 -4
  73. package/src/{adapters/sqlite-session-identity-store.ts → sqlite/session-store.ts} +1 -1
  74. package/src/sqlite/usage-import-store.ts +133 -0
  75. package/src/telemetry-export/exporter.ts +84 -0
  76. package/src/{operations → vehicle}/benchmark-operations.ts +3 -3
  77. package/src/vehicle/cache-operations.ts +95 -0
  78. package/src/vehicle/catalog-operations.ts +10 -0
  79. package/src/{client.ts → vehicle/client.ts} +1 -1
  80. package/src/{operations → vehicle}/context-operations.ts +13 -5
  81. package/src/vehicle/export-operations.ts +12 -0
  82. package/src/{operations/metrics-operations.ts → vehicle/metric-operations.ts} +4 -4
  83. package/src/{operations → vehicle}/model-ranking-operations.ts +3 -3
  84. package/src/{vehicle-registration.ts → vehicle/registration.ts} +21 -4
  85. package/src/{operations/router-operations.ts → vehicle/routing-operations.ts} +4 -4
  86. package/src/{service.ts → vehicle/service.ts} +114 -26
  87. package/src/{operations/session-identity-operations.ts → vehicle/session-operations.ts} +3 -3
  88. package/src/vehicle/usage-import-operations.ts +10 -0
  89. package/src/providers/telemetry-sources.ts +0 -108
  90. /package/src/{providers/google-adc-auth.ts → google-vertex/auth.ts} +0 -0
  91. /package/src/{domain → observability}/metric.ts +0 -0
  92. /package/src/{domain → observability}/task-cost.ts +0 -0
  93. /package/src/{domain → observability}/task-focus.ts +0 -0
  94. /package/src/{domain → observability}/usage.ts +0 -0
  95. /package/src/{config.ts → optimization/routing/config.ts} +0 -0
  96. /package/src/{ports/session-identity-store.ts → sessions/store.ts} +0 -0
  97. /package/src/{operations/types.ts → vehicle/operation-types.ts} +0 -0
@@ -0,0 +1,122 @@
1
+ export interface HistoricalUsageRecord {
2
+ identity: string;
3
+ observedAt: number;
4
+ provider: string;
5
+ model: string;
6
+ thinking: string | null;
7
+ inputTokens: number;
8
+ outputTokens: number;
9
+ cacheReadTokens: number;
10
+ cacheWriteTokens: number;
11
+ costUsd: number;
12
+ }
13
+
14
+ export interface HistoricalUsageScan {
15
+ records: HistoricalUsageRecord[];
16
+ filesScanned: number;
17
+ entriesScanned: number;
18
+ bytesScanned: number;
19
+ malformedEntries: number;
20
+ truncated: boolean;
21
+ canceled: boolean;
22
+ cursor: string | null;
23
+ }
24
+
25
+ export interface HistoricalUsageSource {
26
+ scan(canceled: () => boolean): Promise<HistoricalUsageScan>;
27
+ }
28
+
29
+ export interface UsageImportResult {
30
+ dryRun: boolean;
31
+ discovered: number;
32
+ imported: number;
33
+ duplicates: number;
34
+ filesScanned: number;
35
+ entriesScanned: number;
36
+ bytesScanned: number;
37
+ malformedEntries: number;
38
+ truncated: boolean;
39
+ canceled: boolean;
40
+ cursor: string | null;
41
+ completedAt: number;
42
+ }
43
+
44
+ export interface UsageImportStatus {
45
+ running: boolean;
46
+ cancelRequested: boolean;
47
+ lastResult: UsageImportResult | null;
48
+ }
49
+
50
+ export interface UsageImportStore {
51
+ import(records: HistoricalUsageRecord[]): { imported: number; duplicates: number };
52
+ preview(records: HistoricalUsageRecord[]): { duplicates: number };
53
+ status(): UsageImportStatus;
54
+ saveStatus(status: UsageImportStatus): void;
55
+ }
56
+
57
+ export interface UsageImportController {
58
+ run(input?: { dryRun?: boolean }): Promise<UsageImportResult>;
59
+ status(): UsageImportStatus;
60
+ cancel(): UsageImportStatus;
61
+ }
62
+
63
+ export class HistoricalUsageImporter implements UsageImportController {
64
+ private cancelRequested = false;
65
+ private active = 0;
66
+
67
+ constructor(
68
+ private readonly source: HistoricalUsageSource,
69
+ private readonly store: UsageImportStore,
70
+ private readonly clock: () => number = Date.now,
71
+ ) {}
72
+
73
+ async run(input: { dryRun?: boolean } = {}): Promise<UsageImportResult> {
74
+ this.cancelRequested = false;
75
+ this.active += 1;
76
+ this.persistStatus();
77
+ try {
78
+ const scan = await this.source.scan(() => this.cancelRequested);
79
+ const dryRun = input.dryRun === true;
80
+ const outcome = dryRun ? { imported: 0, duplicates: this.store.preview(scan.records).duplicates } : this.store.import(scan.records);
81
+ const result: UsageImportResult = {
82
+ dryRun,
83
+ discovered: scan.records.length,
84
+ ...outcome,
85
+ filesScanned: scan.filesScanned,
86
+ entriesScanned: scan.entriesScanned,
87
+ bytesScanned: scan.bytesScanned,
88
+ malformedEntries: scan.malformedEntries,
89
+ truncated: scan.truncated,
90
+ canceled: scan.canceled,
91
+ cursor: scan.cursor,
92
+ completedAt: this.clock(),
93
+ };
94
+ this.active -= 1;
95
+ this.store.saveStatus({ running: this.active > 0, cancelRequested: this.cancelRequested, lastResult: result });
96
+ return result;
97
+ } catch (error) {
98
+ this.active -= 1;
99
+ this.persistStatus();
100
+ throw error;
101
+ }
102
+ }
103
+
104
+ status(): UsageImportStatus {
105
+ const persisted = this.store.status();
106
+ return { ...persisted, running: this.active > 0, cancelRequested: this.cancelRequested };
107
+ }
108
+
109
+ cancel(): UsageImportStatus {
110
+ this.cancelRequested = true;
111
+ this.persistStatus();
112
+ return this.status();
113
+ }
114
+
115
+ private persistStatus(): void {
116
+ this.store.saveStatus({
117
+ running: this.active > 0,
118
+ cancelRequested: this.cancelRequested,
119
+ lastResult: this.store.status().lastResult,
120
+ });
121
+ }
122
+ }
@@ -0,0 +1,63 @@
1
+ import { normalizeModelIdentity } from "../observability/model-identity.ts";
2
+ import type { TextTokenCounter, TextTokenCountInput, TokenMeasurement } from "../observability/token-measurement.ts";
3
+
4
+ type OpenAiEncoding = "cl100k_base" | "o200k_base" | "o200k_harmony";
5
+ type EncodeText = (text: string) => ArrayLike<number>;
6
+
7
+ function openAiModelId(provider: string | undefined, model: string | undefined): string | null {
8
+ if (provider === undefined || model === undefined) return null;
9
+ const normalizedProvider = provider.toLowerCase();
10
+ const normalizedModel = model.toLowerCase();
11
+ if (["openai", "openai-codex", "azure-openai"].includes(normalizedProvider)) return normalizedModel;
12
+ if (normalizedProvider === "openrouter" && normalizedModel.startsWith("openai/")) return normalizedModel.slice("openai/".length);
13
+ return null;
14
+ }
15
+
16
+ /** Deliberately conservative: an unknown future model falls back instead of guessing an encoding and claiming exact text tokenization. */
17
+ function encodingForOpenAiModel(model: string): OpenAiEncoding | null {
18
+ if (model.startsWith("gpt-oss-")) return "o200k_harmony";
19
+ if (/^(?:gpt-5|gpt-4\.1|gpt-4o|chatgpt-4o|o[134](?:-|$))/.test(model)) return "o200k_base";
20
+ if (/^(?:gpt-4(?:-|$)|gpt-3\.5(?:-|$)|text-embedding-(?:3|ada-002))/.test(model)) return "cl100k_base";
21
+ return null;
22
+ }
23
+
24
+ /**
25
+ * Exact BPE count for the supplied text only. It intentionally does not encode a provider request
26
+ * envelope, tools, images, cache controls, or chat template, so its provenance is never presented
27
+ * as exact request usage.
28
+ */
29
+ export class OpenAiTextTokenCounter implements TextTokenCounter {
30
+ constructor(
31
+ private readonly encoding: OpenAiEncoding,
32
+ private readonly encode: EncodeText,
33
+ ) {}
34
+
35
+ countText(input: TextTokenCountInput): TokenMeasurement | null {
36
+ const modelId = openAiModelId(input.provider, input.model);
37
+ if (!modelId || encodingForOpenAiModel(modelId) !== this.encoding) return null;
38
+ const identity = normalizeModelIdentity(input.provider!, input.model!);
39
+ return {
40
+ tokens: this.encode(input.text).length,
41
+ scope: input.scope,
42
+ provenance: "tokenizer-exact-text",
43
+ method: `gpt-tokenizer:${this.encoding}`,
44
+ provider: identity.provider,
45
+ model: identity.model,
46
+ };
47
+ }
48
+ }
49
+
50
+ /** Loads only the one BPE table required by the requested model, and returns null rather than guessing for an unknown mapping. */
51
+ export async function loadOpenAiTextTokenCounter(provider: string, model: string): Promise<OpenAiTextTokenCounter | null> {
52
+ const modelId = openAiModelId(provider, model);
53
+ if (!modelId) return null;
54
+ const encoding = encodingForOpenAiModel(modelId);
55
+ if (!encoding) return null;
56
+ const module =
57
+ encoding === "o200k_base"
58
+ ? await import("gpt-tokenizer/encoding/o200k_base")
59
+ : encoding === "o200k_harmony"
60
+ ? await import("gpt-tokenizer/encoding/o200k_harmony")
61
+ : await import("gpt-tokenizer/encoding/cl100k_base");
62
+ return new OpenAiTextTokenCounter(encoding, module.encode);
63
+ }
@@ -9,9 +9,9 @@ import {
9
9
  type BenchmarkSourceSnapshot,
10
10
  normalizeModelIdentity,
11
11
  validateBenchmarkObservation,
12
- } from "../domain/benchmark.ts";
13
- import type { BenchmarkSource } from "../ports/benchmark-source.ts";
14
- import { contractRecord, type OpenRouterModel, parseOpenRouterModels } from "../providers/openrouter-contracts.ts";
12
+ } from "../optimization/model-selection/benchmark.ts";
13
+ import type { BenchmarkSource } from "../optimization/model-selection/source.ts";
14
+ import { contractRecord, type OpenRouterModel, parseOpenRouterModels } from "./contracts.ts";
15
15
 
16
16
  const OPENROUTER_MODELS_BASE_URL = "https://openrouter.ai/api/v1/models";
17
17
  const OPENROUTER_MODELS_URL = `${OPENROUTER_MODELS_BASE_URL}?limit=${BENCHMARK_MAX_MODELS_PER_SOURCE}`;
@@ -1,4 +1,4 @@
1
- import type { MetricObservation } from "../domain/metric.ts";
1
+ import type { MetricObservation } from "../observability/metric.ts";
2
2
 
3
3
  export interface OpenRouterUsage {
4
4
  promptTokens: number;
@@ -4,10 +4,10 @@ import {
4
4
  type BenchmarkSourceSnapshot,
5
5
  normalizeModelIdentity,
6
6
  validateBenchmarkObservation,
7
- } from "../domain/benchmark.ts";
8
- import type { BenchmarkSource } from "../ports/benchmark-source.ts";
9
- import { contractRecord } from "../providers/openrouter-contracts.ts";
10
- import type { OpenRouterBenchmarkTransport } from "./openrouter-benchmark-source.ts";
7
+ } from "../optimization/model-selection/benchmark.ts";
8
+ import type { BenchmarkSource } from "../optimization/model-selection/source.ts";
9
+ import type { OpenRouterBenchmarkTransport } from "./benchmark-source.ts";
10
+ import { contractRecord } from "./contracts.ts";
11
11
 
12
12
  const SOURCE_ID = "openrouter-design-arena";
13
13
  const BASE_URL = `https://openrouter.ai/api/v1/benchmarks?source=design-arena&max_results=${BENCHMARK_MAX_MODELS_PER_SOURCE}`;
@@ -0,0 +1,20 @@
1
+ import type { TelemetryBatch, TelemetrySource } from "../observability/telemetry-source.ts";
2
+ import { OpenRouterTelemetryAdapter, type OpenRouterTransport } from "./telemetry.ts";
3
+
4
+ export class OpenRouterTelemetrySource implements TelemetrySource {
5
+ readonly id = "openrouter";
6
+ readonly provider = "openrouter";
7
+ readonly required = false;
8
+
9
+ constructor(
10
+ private readonly apiKey: string,
11
+ private readonly transport: OpenRouterTransport = fetch,
12
+ private readonly clock: () => number = Date.now,
13
+ ) {}
14
+
15
+ async poll(): Promise<TelemetryBatch> {
16
+ const observedAt = this.clock();
17
+ const snapshot = await new OpenRouterTelemetryAdapter(this.apiKey, this.transport).readKey(observedAt);
18
+ return { observedAt, metrics: snapshot.metrics, windows: [] };
19
+ }
20
+ }
@@ -7,7 +7,7 @@ import {
7
7
  parseOpenRouterGeneration,
8
8
  parseOpenRouterKey,
9
9
  parseOpenRouterModels,
10
- } from "./openrouter-contracts.ts";
10
+ } from "./contracts.ts";
11
11
 
12
12
  export {
13
13
  type OpenRouterAnalyticsResult,
@@ -18,7 +18,7 @@ export {
18
18
  type OpenRouterUsageContext,
19
19
  openRouterUsageMetrics,
20
20
  parseOpenRouterUsage,
21
- } from "./openrouter-contracts.ts";
21
+ } from "./contracts.ts";
22
22
 
23
23
  const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
24
24
  export type OpenRouterTransport = (request: Request) => Promise<Response>;
@@ -5,21 +5,17 @@ import {
5
5
  BENCHMARK_MAX_QUERY_LIMIT,
6
6
  BENCHMARK_MAX_TEXT_CHARACTERS,
7
7
  BENCHMARK_REFRESH_INTERVAL_MS,
8
- } from "../constants.ts";
9
- import type { BenchmarkController } from "../ports/benchmark-controller.ts";
10
- import type { BenchmarkSource as BenchmarkSourcePort } from "../ports/benchmark-source.ts";
11
- import type { BenchmarkStore } from "../ports/benchmark-store.ts";
12
- import { METRIC_UNITS, type MetricUnit } from "./metric.ts";
8
+ } from "../../constants.ts";
9
+ import { METRIC_UNITS, type MetricUnit } from "../../observability/metric.ts";
10
+ import { type ModelIdentity, normalizeModelIdentity } from "../../observability/model-identity.ts";
11
+ import type { BenchmarkController } from "./controller.ts";
12
+ import type { BenchmarkSource as BenchmarkSourcePort } from "./source.ts";
13
+ import type { BenchmarkStore } from "./store.ts";
13
14
 
14
- export type BenchmarkSourceType = "creator" | "marketplace" | "independent" | "operational" | "preference" | "local";
15
+ export type { ModelIdentity };
16
+ export { normalizeModelIdentity };
15
17
 
16
- export interface ModelIdentity {
17
- provider: string;
18
- model: string;
19
- version: string | null;
20
- canonical: string;
21
- aliases: string[];
22
- }
18
+ export type BenchmarkSourceType = "creator" | "marketplace" | "independent" | "operational" | "preference" | "local";
23
19
 
24
20
  export interface BenchmarkProvenance {
25
21
  sourceId: string;
@@ -89,7 +85,6 @@ export interface BenchmarkCatalogOptions {
89
85
 
90
86
  export type BenchmarkSource = BenchmarkSourcePort;
91
87
 
92
- const VERSION_SUFFIX = /-(\d{4}-\d{2}-\d{2})$/;
93
88
  const SOURCE_TYPES = new Set<BenchmarkSourceType>(["creator", "marketplace", "independent", "operational", "preference", "local"]);
94
89
 
95
90
  function boundedText(value: unknown, name: string, maximum = BENCHMARK_MAX_TEXT_CHARACTERS): string {
@@ -107,19 +102,6 @@ function identityPart(value: string, name: string, allowPath = false): string {
107
102
  return normalized;
108
103
  }
109
104
 
110
- export function normalizeModelIdentity(provider: string, model: string, aliases: string[] = []): ModelIdentity {
111
- const normalizedProvider = identityPart(provider, "provider");
112
- const normalizedModel = identityPart(model, "model", true);
113
- const version = VERSION_SUFFIX.exec(normalizedModel)?.[1] ?? null;
114
- const canonical = `${normalizedProvider}/${normalizedModel}`;
115
- const normalizedAliases = [
116
- ...new Set(aliases.map((alias) => boundedText(alias, "alias", BENCHMARK_IDENTITY_MAX_CHARACTERS).toLowerCase())),
117
- ]
118
- .filter((alias) => alias !== canonical)
119
- .sort();
120
- return { provider: normalizedProvider, model: normalizedModel, version, canonical, aliases: normalizedAliases };
121
- }
122
-
123
105
  function validateIdentity(value: unknown): ModelIdentity {
124
106
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("model identity is required");
125
107
  const input = value as Record<string, unknown>;
@@ -0,0 +1,97 @@
1
+ import { MODEL_CATALOG_MAX_ENTRIES, MODEL_CATALOG_STORE_QUERY_LIMIT } from "../../constants.ts";
2
+ import type { MetricObservation } from "../../observability/metric.ts";
3
+ import type { MetricStore } from "../../observability/store.ts";
4
+ import type { ModelCatalogEntry, ModelCatalogSnapshot, ModelCatalogStore } from "./catalog.ts";
5
+
6
+ const SOURCE = "catalog:models.dev";
7
+ const SNAPSHOT_SCOPE = "snapshot";
8
+ const COMPLETE_METRIC = "snapshot-complete";
9
+ const ENTRY_METRIC = "model";
10
+ const BATCH_SIZE = 100;
11
+
12
+ function validEntry(value: unknown): value is ModelCatalogEntry {
13
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
14
+ const entry = value as Partial<ModelCatalogEntry>;
15
+ return (
16
+ typeof entry.provider === "string" &&
17
+ typeof entry.model === "string" &&
18
+ typeof entry.canonical === "string" &&
19
+ Array.isArray(entry.aliases) &&
20
+ typeof entry.name === "string" &&
21
+ typeof entry.limits?.context === "number" &&
22
+ typeof entry.limits?.output === "number" &&
23
+ typeof entry.fieldAuthority === "object" &&
24
+ entry.fieldAuthority !== null
25
+ );
26
+ }
27
+
28
+ function entryObservation(snapshot: ModelCatalogSnapshot, entry: ModelCatalogEntry): MetricObservation {
29
+ return {
30
+ source: SOURCE,
31
+ scope: entry.canonical,
32
+ metric: ENTRY_METRIC,
33
+ value: 1,
34
+ unit: "count",
35
+ observedAt: snapshot.provenance.retrievedAt,
36
+ attributes: { snapshotId: snapshot.snapshotId, entry },
37
+ };
38
+ }
39
+
40
+ /** Complete-marker snapshot store: failed/partial writes remain invisible because the marker lands last. */
41
+ export class MetricModelCatalogStore implements ModelCatalogStore {
42
+ constructor(private readonly metrics: MetricStore) {}
43
+
44
+ publish(snapshot: ModelCatalogSnapshot): ModelCatalogSnapshot {
45
+ if (snapshot.entries.length === 0 || snapshot.entries.length > MODEL_CATALOG_MAX_ENTRIES)
46
+ throw new Error("model catalog snapshot exceeds the entry limit");
47
+ const existing = this.latest();
48
+ if (existing?.snapshotId === snapshot.snapshotId) return existing;
49
+ const rows = snapshot.entries.map((entry) => entryObservation(snapshot, entry));
50
+ for (let offset = 0; offset < rows.length; offset += BATCH_SIZE) this.metrics.recordBatch(rows.slice(offset, offset + BATCH_SIZE));
51
+ this.metrics.record({
52
+ source: SOURCE,
53
+ scope: SNAPSHOT_SCOPE,
54
+ metric: COMPLETE_METRIC,
55
+ value: rows.length,
56
+ unit: "count",
57
+ observedAt: snapshot.provenance.retrievedAt,
58
+ attributes: {
59
+ snapshotId: snapshot.snapshotId,
60
+ provenance: snapshot.provenance,
61
+ },
62
+ });
63
+ return structuredClone(snapshot);
64
+ }
65
+
66
+ latest(): ModelCatalogSnapshot | null {
67
+ const marker = this.metrics.query({ source: SOURCE, scope: SNAPSHOT_SCOPE, metric: COMPLETE_METRIC, order: "desc", limit: 1 })[0];
68
+ if (!marker || typeof marker.value !== "number" || !Number.isSafeInteger(marker.value) || marker.value < 1) return null;
69
+ const snapshotId = marker.attributes.snapshotId;
70
+ const provenance = marker.attributes.provenance;
71
+ if (typeof snapshotId !== "string" || typeof provenance !== "object" || provenance === null || Array.isArray(provenance)) return null;
72
+ const rows = this.metrics.query({ source: SOURCE, until: marker.observedAt, order: "desc", limit: MODEL_CATALOG_STORE_QUERY_LIMIT });
73
+ const markerIndex = rows.findIndex((row) => row.id === marker.id);
74
+ if (markerIndex < 0) return null;
75
+ const entries = rows
76
+ .slice(markerIndex + 1)
77
+ .filter((row) => row.metric === ENTRY_METRIC && row.attributes.snapshotId === snapshotId)
78
+ .slice(0, marker.value)
79
+ .map((row) => row.attributes.entry);
80
+ if (entries.length !== marker.value || !entries.every(validEntry)) return null;
81
+ const p = provenance as Record<string, unknown>;
82
+ if (
83
+ p.sourceId !== "models.dev" ||
84
+ typeof p.sourceUrl !== "string" ||
85
+ typeof p.revision !== "string" ||
86
+ typeof p.retrievedAt !== "number" ||
87
+ typeof p.freshUntil !== "number" ||
88
+ p.license !== "MIT"
89
+ )
90
+ return null;
91
+ return {
92
+ snapshotId,
93
+ provenance: p as unknown as ModelCatalogSnapshot["provenance"],
94
+ entries: (entries as ModelCatalogEntry[]).reverse().map((entry) => structuredClone(entry)),
95
+ };
96
+ }
97
+ }