Files
OmniRoute/tests/unit/freeProviderRankings-reliability-sort.test.ts
Dizzle 5253b93b89 feat(rankings): order free providers by measured reliability (#12218)
Free Provider Rankings sorted by the top model's Arena score, so a provider stayed first even if it failed every call — the reliability column from #11546 already showed what each one actually served, but ordering ignored it.

Adds an opt-in ?sortBy=reliability (API) and a "Most reliable first" toggle (page) sharing one comparator in freeProviderRankingsUsage.ts: measured providers first by successRate desc with ELO on ties, then unmeasured in their incoming order. The default is unchanged and locked by tests. successRate is null below MIN_USAGE_REQUESTS = 5 (existing, never zero), and ordering runs before slice(0, limit) so limit counts in the requested order. The toggle composes with the existing sortTypeFirst/groupByType grouping — a stable sort keeps reliability order within each group.

Opt-in is the right default here: the page is for discovery, including providers never called.

13 tests across three files (6 new for the comparator in isolation, plus filter and route coverage including unknown sortBy → 400 and the default path staying off call_logs).

Verified in a combined batch worktree with all 11 PRs of this batch: 174/174 focused tests, typecheck:core clean, complexity 2706/3218, cognitive-complexity 1221/1437, check:cycles and check:docs-counts green. The 42-locale i18n pass was checked with the CI gates: check-ui-keys-coverage PASS (all 42 locales at or above 65%) and check-ui-value-drift PASS against the release tip.

Thanks @maxmad64bis.
2026-09-01 11:58:15 -03:00

103 lines
2.6 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { sortRankingsByReliability } from "../../src/lib/freeProviderRankingsUsage.ts";
import type { FreeProviderRanking } from "../../src/lib/freeProviderRankings.ts";
function ranking(
id: string,
elo: number,
successRate: number | null | undefined
): FreeProviderRanking {
const base = {
id,
name: id,
category: "apikey",
topModel: { id: `${id}/model`, name: "M", score: elo },
averageScore: elo,
modelCount: 1,
} as unknown as FreeProviderRanking;
if (successRate === undefined) return base;
return {
...base,
reliability: {
connections: [],
state: "healthy",
usage: {
requests: 100,
successes: successRate === null ? 0 : Math.round(successRate * 100),
successRate,
avgLatencyMs: null,
lastRequestAt: null,
windowHours: 24,
},
},
} as unknown as FreeProviderRanking;
}
test("measured providers come before unmeasured ones", () => {
const sorted = sortRankingsByReliability([
ranking("no-traffic", 1400, undefined),
ranking("measured", 1000, 0.9),
]);
assert.deepEqual(
sorted.map((r) => r.id),
["measured", "no-traffic"]
);
});
test("measured providers are ordered by success rate, highest first", () => {
const sorted = sortRankingsByReliability([
ranking("mid", 1400, 0.5),
ranking("best", 1000, 0.99),
ranking("worst", 1500, 0.1),
]);
assert.deepEqual(
sorted.map((r) => r.id),
["best", "mid", "worst"]
);
});
test("equal success rates fall back to the ELO order", () => {
const sorted = sortRankingsByReliability([
ranking("low-elo", 1000, 0.9),
ranking("high-elo", 1500, 0.9),
]);
assert.deepEqual(
sorted.map((r) => r.id),
["high-elo", "low-elo"]
);
});
test("a sample too small to state a rate counts as unmeasured, never as zero", () => {
const sorted = sortRankingsByReliability([
ranking("too-few", 1500, null),
ranking("measured", 1000, 0.4),
]);
assert.deepEqual(
sorted.map((r) => r.id),
["measured", "too-few"]
);
});
test("unmeasured providers keep their incoming order", () => {
const sorted = sortRankingsByReliability([
ranking("second", 1000, undefined),
ranking("first", 1500, undefined),
]);
assert.deepEqual(
sorted.map((r) => r.id),
["second", "first"]
);
});
test("the input array is not mutated", () => {
const input = [ranking("a", 1000, undefined), ranking("b", 1500, 0.9)];
const before = input.map((r) => r.id);
sortRankingsByReliability(input);
assert.deepEqual(
input.map((r) => r.id),
before
);
});