fix(report): address CodeRabbit review — pricing match, sidechain filter, UX guards
- getPricing: use tokenized order-insensitive matching so Claude 3 era models (e.g. "claude-3-opus-20240229") correctly match pricing key "opus-3" - sessionAnalyzer: skip isSidechain messages in parentCost loop to prevent double-counting with processSubagentCost - CostSection: disable row expansion when token stats are missing - tabSlice: guard against undefined sessionId/projectId in openSessionReport Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4fda90bc3e
commit
586cb00174
3 changed files with 11 additions and 7 deletions
|
|
@ -182,17 +182,17 @@ export const CostSection = ({
|
||||||
<tbody>
|
<tbody>
|
||||||
{modelEntries.map(([model, cost]) => {
|
{modelEntries.map(([model, cost]) => {
|
||||||
const stats = tokensByModel[model];
|
const stats = tokensByModel[model];
|
||||||
const isExpanded = expandedModel === model;
|
const isExpanded = expandedModel === model && !!stats;
|
||||||
const pricing = getPricing(model);
|
const pricing = getPricing(model);
|
||||||
return (
|
return (
|
||||||
<Fragment key={model}>
|
<Fragment key={model}>
|
||||||
<tr
|
<tr
|
||||||
className="border-border/50 hover:bg-surface-raised/50 cursor-pointer border-b"
|
className={`border-border/50 border-b ${stats ? 'hover:bg-surface-raised/50 cursor-pointer' : ''}`}
|
||||||
onClick={() => setExpandedModel(isExpanded ? null : model)}
|
onClick={() => stats && setExpandedModel(isExpanded ? null : model)}
|
||||||
>
|
>
|
||||||
<td className="py-1.5 pr-4 text-text">
|
<td className="py-1.5 pr-4 text-text">
|
||||||
<span className="mr-1.5 inline-block w-3 text-text-muted">
|
<span className="mr-1.5 inline-block w-3 text-text-muted">
|
||||||
{isExpanded ? '\u25BC' : '\u25B6'}
|
{stats ? (isExpanded ? '\u25BC' : '\u25B6') : ''}
|
||||||
</span>
|
</span>
|
||||||
{model}
|
{model}
|
||||||
</td>
|
</td>
|
||||||
|
|
|
||||||
|
|
@ -387,6 +387,7 @@ export const createTabSlice: StateCreator<AppState, [], [], TabSlice> = (set, ge
|
||||||
const allTabs = getAllTabs(state.paneLayout);
|
const allTabs = getAllTabs(state.paneLayout);
|
||||||
const sourceTab = allTabs.find((t) => t.id === sourceTabId);
|
const sourceTab = allTabs.find((t) => t.id === sourceTabId);
|
||||||
if (sourceTab?.type !== 'session') return;
|
if (sourceTab?.type !== 'session') return;
|
||||||
|
if (!sourceTab.sessionId || !sourceTab.projectId) return;
|
||||||
|
|
||||||
const tabData = state.tabSessionData[sourceTabId];
|
const tabData = state.tabSessionData[sourceTabId];
|
||||||
const firstMsg = tabData?.sessionDetail?.session.firstMessage;
|
const firstMsg = tabData?.sessionDetail?.session.firstMessage;
|
||||||
|
|
|
||||||
|
|
@ -104,9 +104,10 @@ const DEFAULT_PRICING: ModelPricing = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getPricing(modelName: string): ModelPricing {
|
export function getPricing(modelName: string): ModelPricing {
|
||||||
const name = modelName.toLowerCase();
|
const nameTokens: string[] = modelName.toLowerCase().match(/[a-z0-9]+/g) ?? [];
|
||||||
for (const [key, pricing] of Object.entries(MODEL_PRICING)) {
|
for (const [key, pricing] of Object.entries(MODEL_PRICING)) {
|
||||||
if (name.includes(key)) return pricing;
|
const keyTokens: string[] = key.match(/[a-z0-9]+/g) ?? [];
|
||||||
|
if (keyTokens.every((t) => nameTokens.includes(t))) return pricing;
|
||||||
}
|
}
|
||||||
return DEFAULT_PRICING;
|
return DEFAULT_PRICING;
|
||||||
}
|
}
|
||||||
|
|
@ -454,7 +455,9 @@ export function analyzeSession(detail: SessionDetail): SessionReport {
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Token usage, cache economics, and cost ---
|
// --- Token usage, cache economics, and cost ---
|
||||||
if (m.usage && m.model) {
|
// Skip sidechain messages to avoid double-counting (subagent costs are
|
||||||
|
// accounted for separately via processSubagentCost).
|
||||||
|
if (m.usage && m.model && !m.isSidechain) {
|
||||||
const model = m.model;
|
const model = m.model;
|
||||||
const u = m.usage;
|
const u = m.usage;
|
||||||
const inpTok = u.input_tokens ?? 0;
|
const inpTok = u.input_tokens ?? 0;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue