diff --git a/changelog.d/fixes/11595-combo-success-rate-scale.md b/changelog.d/fixes/11595-combo-success-rate-scale.md new file mode 100644 index 0000000000..b9802ee5a5 --- /dev/null +++ b/changelog.d/fixes/11595-combo-success-rate-scale.md @@ -0,0 +1 @@ +- **fix(dashboard):** Provider Stats now displays combo success rates on their native 0–100 percentage scale instead of multiplying them twice ([#11595](https://github.com/diegosouzapw/OmniRoute/pull/11595)) — thanks @pacocartones diff --git a/src/app/(dashboard)/dashboard/provider-stats/page.tsx b/src/app/(dashboard)/dashboard/provider-stats/page.tsx index bdc1e54cee..96e22cf9b5 100644 --- a/src/app/(dashboard)/dashboard/provider-stats/page.tsx +++ b/src/app/(dashboard)/dashboard/provider-stats/page.tsx @@ -9,8 +9,10 @@ import { useState, useEffect, useCallback, Fragment } from "react"; import { useTranslations } from "next-intl"; -import { Card } from "@/shared/components"; + import { useProviderNodeMap, resolveProviderName } from "@/lib/display/useProviderNodeMap"; +import { formatComboSuccessRate } from "@/lib/usage/providerStatsFormatters"; +import { Card } from "@/shared/components"; interface ProviderStat { provider: string; @@ -493,7 +495,7 @@ export default function ProviderStatsPage() { {typeof m.successRate === "number" - ? `${(m.successRate * 100).toFixed(1)}%` + ? formatComboSuccessRate(m.successRate) : "—"} diff --git a/src/lib/usage/providerStatsFormatters.ts b/src/lib/usage/providerStatsFormatters.ts new file mode 100644 index 0000000000..d61622e852 --- /dev/null +++ b/src/lib/usage/providerStatsFormatters.ts @@ -0,0 +1,3 @@ +export function formatComboSuccessRate(successRatePercent: number): string { + return `${successRatePercent.toFixed(1)}%`; +} diff --git a/tests/unit/provider-stats-formatters.test.ts b/tests/unit/provider-stats-formatters.test.ts new file mode 100644 index 0000000000..7f36e5a1fb --- /dev/null +++ b/tests/unit/provider-stats-formatters.test.ts @@ -0,0 +1,38 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; +import test from "node:test"; + +import { + getComboMetrics, + recordComboRequest, + resetAllComboMetrics, +} from "../../open-sse/services/comboMetrics.ts"; +import { formatComboSuccessRate } from "../../src/lib/usage/providerStatsFormatters.ts"; + +test.after(resetAllComboMetrics); + +test("formatComboSuccessRate preserves the producer's 0-100 percentage scale", () => { + resetAllComboMetrics(); + recordComboRequest("format-test", "openai/gpt-4o-mini", { + success: true, + latencyMs: 10, + }); + + const metrics = getComboMetrics("format-test"); + assert.ok(metrics); + assert.equal(metrics.successRate, 100); + assert.equal(formatComboSuccessRate(metrics.successRate), "100.0%"); + assert.equal(formatComboSuccessRate(50), "50.0%"); + assert.equal(formatComboSuccessRate(0), "0.0%"); +}); + +test("Provider Stats renders combo metrics through the percentage-scale formatter", () => { + const source = fs.readFileSync( + path.resolve("src/app/(dashboard)/dashboard/provider-stats/page.tsx"), + "utf8" + ); + + assert.match(source, /formatComboSuccessRate\(m\.successRate\)/); + assert.doesNotMatch(source, /m\.successRate\s*\*\s*100/); +});