fix(combo): keep nested combos as single units and stop 0-token detail rows
Nested combos (comboA lists comboB, comboC, …) now stay one slot each: the inner combo always runs as fallback to produce a single answer. Failed hops are no longer written to Details/usage, and streaming no longer inserts a 0-token placeholder row. - chat.js: comboStack cycle detection; nested combos forced to fallback; persistUsage="success-only" for combo hops - combo.js: discardResponse() cancels unused bodies (fusion timeout / fallback) so dropped streams fire onStreamComplete; getComboModelsFromData keeps nested names and honors enabled=false - requestDetail.js: tokensForDetail() canonicalizes Claude/Gemini usage; shouldPersistRequestDetail() skips streaming-start and non-success hops - streamingHandler.js: drop the 0-token streaming placeholder write - RequestDetailsTab.js: read Gemini/Claude token names; show "streaming" status in amber - tests: add combo-nested.test.js (13 cases) - gitignore: ignore local .vitest/ artifacts Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
This commit is contained in:
@@ -110,6 +110,29 @@ export function formatDoneLine({ usage, latency }) {
|
||||
return `DONE ${latency?.total ?? 0}ms${ttftStr} · ${inStr} · OUT ${outTok}`;
|
||||
}
|
||||
|
||||
// Request-details storage convention: always prompt_tokens / completion_tokens.
|
||||
// Translators often hand Claude `{input_tokens, output_tokens}` (or Gemini
|
||||
// counts) to onStreamComplete; the Details tab only reads the OpenAI names,
|
||||
// so an uncanonicalized object shows up as input=0 / output=0.
|
||||
export function tokensForDetail(usage) {
|
||||
if (!usage || typeof usage !== "object") {
|
||||
return { prompt_tokens: 0, completion_tokens: 0 };
|
||||
}
|
||||
return canonicalizeUsage(usage) || {
|
||||
prompt_tokens: usage.prompt_tokens ?? usage.input_tokens ?? 0,
|
||||
completion_tokens: usage.completion_tokens ?? usage.output_tokens ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
// Combo fallback/account hops must not inflate Details with 0-token rows.
|
||||
// `streaming-start` is never persisted: the placeholder was status=success at
|
||||
// tokens=0, and nested/fusion paths often abandon the stream before complete.
|
||||
export function shouldPersistRequestDetail(persistUsage, kind) {
|
||||
if (kind === "streaming-start") return false;
|
||||
if (persistUsage === "success-only") return kind === "success";
|
||||
return true;
|
||||
}
|
||||
|
||||
export function saveUsageStats({ provider, model, tokens, connectionId, apiKey, endpoint, label = "USAGE", silent = false }) {
|
||||
if (!tokens || typeof tokens !== "object") return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user