fix(logs): avoid giant provider pills for failed auto family requests (#8867)

* fix(logs): avoid giant provider pills for failed auto family requests

* refactor(logs): extract resolveRejectedComboProvider + cover it

The provider label was decided inline in handleChat, which has no test harness —
the change shipped untested and pushed chat.ts over its frozen size (1848 > 1845).

Moved to rejectedRequestUsage.ts next to summarizeComboAttemptedModels, the helper
it replaces on this path. chat.ts shrinks back under its baseline (no rebaseline
needed) and the logic gets three cases in the suite that already covers its sibling:
auto/* collapses to "auto", a named combo keeps its name, and bare "auto" (no
slash) is NOT collapsed — that one is a combo request, not a family request.

Also rebased on the current release tip and added the changelog fragment.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: rafaeldrincon <rafaeldrincon@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
This commit is contained in:
rafaeldrincon
2026-07-28 14:07:07 -04:00
committed by GitHub
parent 43625ecc32
commit 66997a6404
4 changed files with 42 additions and 3 deletions

View File

@@ -0,0 +1 @@
- **Logs**: a failed `auto/<family>` request no longer writes every attempted model into `call_logs.provider` — the logs page builds its quick-filter pills from that column, so one failure produced a giant chip that flooded the filter row. Rejected combo requests are now labelled `auto` (for `auto/*`) or by the combo's own name

View File

@@ -908,13 +908,13 @@ export async function handleChat(
// (success:false) so gate/breaker-rejected traffic is counted per key — support-mesh 2026-07-08.
if (!response.ok) {
try {
const { recordRejectedRequestUsage, summarizeComboAttemptedModels } =
const { recordRejectedRequestUsage, resolveRejectedComboProvider } =
await import("./rejectedRequestUsage");
await recordRejectedRequestUsage({
status: response.status,
model: body?.model || resolvedModelStr,
requestedModel: body?.model || resolvedModelStr,
provider: summarizeComboAttemptedModels(combo?.models),
provider: resolveRejectedComboProvider(body?.model || resolvedModelStr, combo.name),
endpoint: clientRawRequest?.endpoint,
error: await getComboFailureLogError(response, combo.name),
comboName: combo.name,

View File

@@ -127,3 +127,20 @@ export function summarizeComboAttemptedModels(models: unknown): string {
.filter((entry): entry is string => Boolean(entry));
return modelStrings.length > 0 ? modelStrings.join(", ") : "-";
}
/**
* Provider label for a REJECTED combo request (#8867).
*
* summarizeComboAttemptedModels() joins every attempted model, which is right for
* diagnostics but wrong for `call_logs.provider`: the logs page builds its quick-filter
* pills from that column, so one failed `auto/gemma` turned into a chip listing a dozen
* models and flooded the filter row. Keep it short and categorical instead — `auto` for
* auto/* requests, otherwise the combo's own name.
*/
export function resolveRejectedComboProvider(
model: string | null | undefined,
comboName: string | null | undefined
): string {
if (typeof model === "string" && model.startsWith("auto/")) return "auto";
return comboName || "combo";
}

View File

@@ -24,7 +24,7 @@ process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const usageHistory = await import("../../src/lib/usage/usageHistory.ts");
const callLogs = await import("../../src/lib/usage/callLogs.ts");
const { recordRejectedRequestUsage, summarizeComboAttemptedModels } =
const { recordRejectedRequestUsage, summarizeComboAttemptedModels, resolveRejectedComboProvider } =
await import("../../src/sse/handlers/rejectedRequestUsage.ts");
test.beforeEach(() => {
@@ -175,3 +175,24 @@ test("summarizeComboAttemptedModels falls back to '-' for empty, missing, or inv
assert.equal(summarizeComboAttemptedModels("not-an-array"), "-");
assert.equal(summarizeComboAttemptedModels([{ kind: "combo-ref" }, { foo: "bar" }]), "-");
});
test("#8867: resolveRejectedComboProvider keeps the provider label short for auto/* failures", () => {
// The logs page builds quick-filter pills from call_logs.provider — a failed
// auto/<family> used to write every attempted model there and flood the filter row.
assert.equal(resolveRejectedComboProvider("auto/gemma", "my-combo"), "auto");
assert.equal(resolveRejectedComboProvider("auto/gemini", null), "auto");
assert.equal(resolveRejectedComboProvider("auto/anything-at-all", undefined), "auto");
});
test("#8867: a non-auto combo failure is labelled with the combo name", () => {
assert.equal(resolveRejectedComboProvider("gpt-5.6-sol", "balanced-load"), "balanced-load");
assert.equal(resolveRejectedComboProvider(null, "balanced-load"), "balanced-load");
});
test("#8867: falls back to 'combo' when there is no name, and 'auto' never leaks into it", () => {
assert.equal(resolveRejectedComboProvider("gpt-5.6-sol", null), "combo");
assert.equal(resolveRejectedComboProvider(undefined, undefined), "combo");
assert.equal(resolveRejectedComboProvider("", ""), "combo");
// bare "auto" (no slash) is NOT a family request — it must not be collapsed
assert.equal(resolveRejectedComboProvider("auto", "named-combo"), "named-combo");
});