Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions benchmarks/oracle-deviation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ providers:
p99: quantile_over_time(0.99, ocb_oracle_max_deviation_pct{pair="BTC/USD"}[24h]) * 100
mean: avg_over_time(ocb_oracle_max_deviation_pct{pair="BTC/USD"}[24h]) * 100
success: clamp_min(clamp_max(1 - sum(rate(ocb_oracle_scrape_errors_total{pair="BTC/USD"}[24h])) / (4 * (1/30)), 1), 0)
sample_size: count(count by (source) (ocb_oracle_price{pair="BTC/USD"}))
sample_size: sum(count_over_time(ocb_oracle_price{pair="BTC/USD"}[24h]))
series: ocb_oracle_max_deviation_pct{pair="BTC/USD"} * 100

- slug: eth-usd
Expand All @@ -148,7 +148,7 @@ providers:
p99: quantile_over_time(0.99, ocb_oracle_max_deviation_pct{pair="ETH/USD"}[24h]) * 100
mean: avg_over_time(ocb_oracle_max_deviation_pct{pair="ETH/USD"}[24h]) * 100
success: clamp_min(clamp_max(1 - sum(rate(ocb_oracle_scrape_errors_total{pair="ETH/USD"}[24h])) / (4 * (1/30)), 1), 0)
sample_size: count(count by (source) (ocb_oracle_price{pair="ETH/USD"}))
sample_size: sum(count_over_time(ocb_oracle_price{pair="ETH/USD"}[24h]))
series: ocb_oracle_max_deviation_pct{pair="ETH/USD"} * 100

- slug: sol-usd
Expand All @@ -161,7 +161,7 @@ providers:
p99: quantile_over_time(0.99, ocb_oracle_max_deviation_pct{pair="SOL/USD"}[24h]) * 100
mean: avg_over_time(ocb_oracle_max_deviation_pct{pair="SOL/USD"}[24h]) * 100
success: clamp_min(clamp_max(1 - sum(rate(ocb_oracle_scrape_errors_total{pair="SOL/USD"}[24h])) / (4 * (1/30)), 1), 0)
sample_size: count(count by (source) (ocb_oracle_price{pair="SOL/USD"}))
sample_size: sum(count_over_time(ocb_oracle_price{pair="SOL/USD"}[24h]))
series: ocb_oracle_max_deviation_pct{pair="SOL/USD"} * 100

- slug: bnb-usd
Expand All @@ -174,7 +174,7 @@ providers:
p99: quantile_over_time(0.99, ocb_oracle_max_deviation_pct{pair="BNB/USD"}[24h]) * 100
mean: avg_over_time(ocb_oracle_max_deviation_pct{pair="BNB/USD"}[24h]) * 100
success: clamp_min(clamp_max(1 - sum(rate(ocb_oracle_scrape_errors_total{pair="BNB/USD"}[24h])) / (4 * (1/30)), 1), 0)
sample_size: count(count by (source) (ocb_oracle_price{pair="BNB/USD"}))
sample_size: sum(count_over_time(ocb_oracle_price{pair="BNB/USD"}[24h]))
series: ocb_oracle_max_deviation_pct{pair="BNB/USD"} * 100

- slug: avax-usd
Expand All @@ -187,7 +187,7 @@ providers:
p99: quantile_over_time(0.99, ocb_oracle_max_deviation_pct{pair="AVAX/USD"}[24h]) * 100
mean: avg_over_time(ocb_oracle_max_deviation_pct{pair="AVAX/USD"}[24h]) * 100
success: clamp_min(clamp_max(1 - sum(rate(ocb_oracle_scrape_errors_total{pair="AVAX/USD"}[24h])) / (4 * (1/30)), 1), 0)
sample_size: count(count by (source) (ocb_oracle_price{pair="AVAX/USD"}))
sample_size: sum(count_over_time(ocb_oracle_price{pair="AVAX/USD"}[24h]))
series: ocb_oracle_max_deviation_pct{pair="AVAX/USD"} * 100

- slug: link-usd
Expand All @@ -200,7 +200,7 @@ providers:
p99: quantile_over_time(0.99, ocb_oracle_max_deviation_pct{pair="LINK/USD"}[24h]) * 100
mean: avg_over_time(ocb_oracle_max_deviation_pct{pair="LINK/USD"}[24h]) * 100
success: clamp_min(clamp_max(1 - sum(rate(ocb_oracle_scrape_errors_total{pair="LINK/USD"}[24h])) / (4 * (1/30)), 1), 0)
sample_size: count(count by (source) (ocb_oracle_price{pair="LINK/USD"}))
sample_size: sum(count_over_time(ocb_oracle_price{pair="LINK/USD"}[24h]))
series: ocb_oracle_max_deviation_pct{pair="LINK/USD"} * 100

- slug: matic-usd
Expand All @@ -213,7 +213,7 @@ providers:
p99: quantile_over_time(0.99, ocb_oracle_max_deviation_pct{pair="MATIC/USD"}[24h]) * 100
mean: avg_over_time(ocb_oracle_max_deviation_pct{pair="MATIC/USD"}[24h]) * 100
success: clamp_min(clamp_max(1 - sum(rate(ocb_oracle_scrape_errors_total{pair="MATIC/USD"}[24h])) / (4 * (1/30)), 1), 0)
sample_size: count(count by (source) (ocb_oracle_price{pair="MATIC/USD"}))
sample_size: sum(count_over_time(ocb_oracle_price{pair="MATIC/USD"}[24h]))
series: ocb_oracle_max_deviation_pct{pair="MATIC/USD"} * 100

- slug: xrp-usd
Expand All @@ -226,7 +226,7 @@ providers:
p99: quantile_over_time(0.99, ocb_oracle_max_deviation_pct{pair="XRP/USD"}[24h]) * 100
mean: avg_over_time(ocb_oracle_max_deviation_pct{pair="XRP/USD"}[24h]) * 100
success: clamp_min(clamp_max(1 - sum(rate(ocb_oracle_scrape_errors_total{pair="XRP/USD"}[24h])) / (3 * (1/30)), 1), 0)
sample_size: count(count by (source) (ocb_oracle_price{pair="XRP/USD"}))
sample_size: sum(count_over_time(ocb_oracle_price{pair="XRP/USD"}[24h]))
series: ocb_oracle_max_deviation_pct{pair="XRP/USD"} * 100

- slug: ada-usd
Expand All @@ -239,7 +239,7 @@ providers:
p99: quantile_over_time(0.99, ocb_oracle_max_deviation_pct{pair="ADA/USD"}[24h]) * 100
mean: avg_over_time(ocb_oracle_max_deviation_pct{pair="ADA/USD"}[24h]) * 100
success: clamp_min(clamp_max(1 - sum(rate(ocb_oracle_scrape_errors_total{pair="ADA/USD"}[24h])) / (3 * (1/30)), 1), 0)
sample_size: count(count by (source) (ocb_oracle_price{pair="ADA/USD"}))
sample_size: sum(count_over_time(ocb_oracle_price{pair="ADA/USD"}[24h]))
series: ocb_oracle_max_deviation_pct{pair="ADA/USD"} * 100

- slug: doge-usd
Expand All @@ -252,5 +252,5 @@ providers:
p99: quantile_over_time(0.99, ocb_oracle_max_deviation_pct{pair="DOGE/USD"}[24h]) * 100
mean: avg_over_time(ocb_oracle_max_deviation_pct{pair="DOGE/USD"}[24h]) * 100
success: clamp_min(clamp_max(1 - sum(rate(ocb_oracle_scrape_errors_total{pair="DOGE/USD"}[24h])) / (3 * (1/30)), 1), 0)
sample_size: count(count by (source) (ocb_oracle_price{pair="DOGE/USD"}))
sample_size: sum(count_over_time(ocb_oracle_price{pair="DOGE/USD"}[24h]))
series: ocb_oracle_max_deviation_pct{pair="DOGE/USD"} * 100
11 changes: 6 additions & 5 deletions src/app/api/llm-context/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
headlineSentence,
isInsufficient,
leader,
rankedCandidates,
} from "@/lib/citation";
import { clientKey, rateLimit, tooManyRequests } from "@/lib/rate-limit";

Expand Down Expand Up @@ -84,11 +85,11 @@ export async function GET(req: Request) {
lines.push(`- Headline: ${headlineSentence(b)}`);
lines.push("");
lines.push(`**Rankings (p50, 24h):**`);
const ranked = [...b.results]
.filter((r) => r.ms.p50 > 0)
.sort((a, c) =>
b.higherIsBetter ? c.ms.p50 - a.ms.p50 : a.ms.p50 - c.ms.p50,
);
// Shares `rankedCandidates` with `leader()` so the numbered list
// below matches the Headline sentence above. Without the shared
// filter, an LLM pasting this block would see e.g. "Etherscan
// leads" then a rankings list with Owlracle at #1.
const ranked = rankedCandidates(b);
for (let i = 0; i < ranked.length; i++) {
const r = ranked[i];
lines.push(
Expand Down
31 changes: 13 additions & 18 deletions src/app/api/mcp/[transport]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
headlineSentence,
isInsufficient,
leader,
rankedCandidates,
sparklineFor,
} from "@/lib/citation";
import { fmtUnit } from "@/lib/format";
Expand Down Expand Up @@ -297,17 +298,12 @@ const mcpHandler = createMcpHandler(
ms: { p50: null, p90: null, p99: null, mean: null },
successRate: r.successRate,
}))
: b.results
.filter((r) => r.ms.p50 > 0)
.sort((a, c) =>
b.higherIsBetter ? c.ms.p50 - a.ms.p50 : a.ms.p50 - c.ms.p50,
)
.map((r) => ({
name: r.name,
slug: r.slug,
ms: r.ms,
successRate: r.successRate,
}));
: rankedCandidates(b).map((r) => ({
name: r.name,
slug: r.slug,
ms: r.ms,
successRate: r.successRate,
}));
const payload = {
slug: b.slug,
title: b.title,
Expand Down Expand Up @@ -480,13 +476,12 @@ const mcpHandler = createMcpHandler(
}
const insufficient = isInsufficient(b);
const top = insufficient ? null : leader(b);
const ranked = insufficient
? []
: b.results
.filter((r) => r.ms.p50 > 0)
.sort((a, c) =>
b.higherIsBetter ? c.ms.p50 - a.ms.p50 : a.ms.p50 - c.ms.p50,
);
// Shares `rankedCandidates` with `leader()` so the Markdown
// Rankings list matches the Headline sentence above and the
// `rankings` field on the JSON tool response below. Without
// this, an agent reading this resource would see e.g.
// "Etherscan leads" then a numbered list with Owlracle at #1.
const ranked = insufficient ? [] : rankedCandidates(b);

const md: string[] = [];
md.push(`# ${b.title}`);
Expand Down
30 changes: 14 additions & 16 deletions src/app/api/stat/[slug]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
fieldValue,
headlineSentence,
leader,
rankedCandidates,
sparklineFor,
} from "@/lib/citation";
import { valueInDeclaredUnit } from "@/lib/format";
Expand Down Expand Up @@ -98,22 +99,19 @@ export async function GET(
insufficient || !top
? null
: { ...top, value: valueInDeclaredUnit(top.value, b.unit) },
rankings: b.results
.filter((r) => r.ms.p50 > 0)
// Drop "insufficient" rows from the machine-readable ranking too:
// a row that the page hides from the leaderboard must not surface
// here either.
.filter((r) => r.dataConfidence !== "insufficient")
.sort((a, c) => (b.higherIsBetter ? c.ms.p50 - a.ms.p50 : a.ms.p50 - c.ms.p50))
.map((r) => ({
name: r.name,
slug: r.slug,
ms: r.ms,
successRate: r.successRate,
sampleSize: r.sampleSize,
sampleHealth: r.sampleHealth,
dataConfidence: r.dataConfidence,
})),
// Shares `rankedCandidates` with `leader()` so `rankings[0]`
// stays consistent with the `leader` field on the same JSON blob:
// a document that names Etherscan as leader must not also list
// Owlracle first here.
rankings: rankedCandidates(b).map((r) => ({
name: r.name,
slug: r.slug,
ms: r.ms,
successRate: r.successRate,
sampleSize: r.sampleSize,
sampleHealth: r.sampleHealth,
dataConfidence: r.dataConfidence,
})),
sparkline: sparklineFor(b, top?.slug),
sampleSize: b.sampleSize,
expectedN: b.expectedN,
Expand Down
12 changes: 9 additions & 3 deletions src/components/benchmark-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,18 @@ export function BenchmarkCard({ benchmark }: { benchmark: BenchmarkCardData }) {
const insufficient = !isDraft && isInsufficient(b);
const catColor = CATEGORY_COLOR[b.category] ?? "var(--color-ink-muted)";

// Field composite p50: best-of-class (or worst if higher-is-better is
// false). We pick the leader, same heuristic as the table.
// Prefer the canonical leader from citation.ts (reliability floor +
// insufficient-sample filter applied at projection). Falls back to
// the raw best of the projected results when the projection never
// returned a leader (draft, insufficient, or every provider filtered
// out) so the card still surfaces a headline value instead of "n/a"
// whenever any result is present.
const sorted = [...b.results].sort(
b.higherIsBetter ? (a, x) => x.ms.p50 - a.ms.p50 : (a, x) => a.ms.p50 - x.ms.p50,
);
const leader = sorted[0];
const leader = b.leaderSlug
? (sorted.find((r) => r.slug === b.leaderSlug) ?? sorted[0])
: sorted[0];
const headlineValue =
!isDraft && !insufficient && leader ? fmtValue(leader.ms.p50, b.unit) : "n/a";
// Pass the leader's p50 so unitSuffix mirrors fmtUnit's auto-conversion
Expand Down
10 changes: 10 additions & 0 deletions src/data/benchmarks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { promises as fs } from "node:fs";
import path from "node:path";
import yaml from "js-yaml";
import { cache } from "react";
import { leader } from "@/lib/citation";
import { downsample, MINI_CHART_POINTS } from "@/lib/downsample";
import type { Benchmark } from "@/types/benchmark";
import {
Expand Down Expand Up @@ -51,6 +52,14 @@ export type BenchmarkCardData = {
sampleSize: number;
lastRunAt: string;
metric: string;
/** Slug of the citation-eligible leader for this bench (result of
* `leader(b)` in citation.ts). Passed at the projection boundary so
* the hub card can render the same headline the bench page names
* without duplicating the reliability + insufficient-sample filter
* logic. Null when the bench has no defensible leader (draft,
* insufficient, or every provider below the reliability floor with
* an empty live pool). */
leaderSlug: string | null;
results: {
slug: string;
name: string;
Expand All @@ -76,6 +85,7 @@ export function toBenchmarkCardData(b: Benchmark): BenchmarkCardData {
sampleSize: b.sampleSize,
lastRunAt: b.lastRunAt,
metric: b.metric,
leaderSlug: leader(b)?.slug ?? null,
results: b.results.map((r) => ({
slug: r.slug,
name: r.name,
Expand Down
13 changes: 12 additions & 1 deletion src/lib/bench-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

import type { Benchmark, ProviderResult } from "@/types/benchmark";
import { liveResults } from "@/lib/provider-filters";
import { citationCandidates } from "@/lib/citation";
import { rankResults } from "@/lib/ranking";
import { fmtUnit } from "@/lib/format";

Expand Down Expand Up @@ -87,8 +88,18 @@ function worstForChain(b: Benchmark, chain: string): ProviderResult | undefined

export function renderTemplate(text: string, benchmark: Benchmark): string {
if (!text || text.indexOf("{{") === -1) return text;
// `live` still drives per-slug lookups so callers of {{p50:some-slug}}
// can still address unreliable providers by name (the token is
// explicit). `bestPool` applies the same reliability floor as
// `leader()` so {{best_name}} and {{best_p50}} tokens in bench copy
// never elevate a provider that would be filtered out of the
// citation headline.
const live = liveResults(benchmark.results);
const sorted = rankResults(live, benchmark.higherIsBetter);
const bestPool = citationCandidates(benchmark);
const sorted = rankResults(
bestPool.length > 0 ? bestPool : live,
benchmark.higherIsBetter,
);
const best = sorted[0];
const worst = sorted[sorted.length - 1];

Expand Down
Loading
Loading