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>
|
||||
{modelEntries.map(([model, cost]) => {
|
||||
const stats = tokensByModel[model];
|
||||
const isExpanded = expandedModel === model;
|
||||
const isExpanded = expandedModel === model && !!stats;
|
||||
const pricing = getPricing(model);
|
||||
return (
|
||||
<Fragment key={model}>
|
||||
<tr
|
||||
className="border-border/50 hover:bg-surface-raised/50 cursor-pointer border-b"
|
||||
onClick={() => setExpandedModel(isExpanded ? null : model)}
|
||||
className={`border-border/50 border-b ${stats ? 'hover:bg-surface-raised/50 cursor-pointer' : ''}`}
|
||||
onClick={() => stats && setExpandedModel(isExpanded ? null : model)}
|
||||
>
|
||||
<td className="py-1.5 pr-4 text-text">
|
||||
<span className="mr-1.5 inline-block w-3 text-text-muted">
|
||||
{isExpanded ? '\u25BC' : '\u25B6'}
|
||||
{stats ? (isExpanded ? '\u25BC' : '\u25B6') : ''}
|
||||
</span>
|
||||
{model}
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -387,6 +387,7 @@ export const createTabSlice: StateCreator<AppState, [], [], TabSlice> = (set, ge
|
|||
const allTabs = getAllTabs(state.paneLayout);
|
||||
const sourceTab = allTabs.find((t) => t.id === sourceTabId);
|
||||
if (sourceTab?.type !== 'session') return;
|
||||
if (!sourceTab.sessionId || !sourceTab.projectId) return;
|
||||
|
||||
const tabData = state.tabSessionData[sourceTabId];
|
||||
const firstMsg = tabData?.sessionDetail?.session.firstMessage;
|
||||
|
|
|
|||
|
|
@ -104,9 +104,10 @@ const DEFAULT_PRICING: 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)) {
|
||||
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;
|
||||
}
|
||||
|
|
@ -454,7 +455,9 @@ export function analyzeSession(detail: SessionDetail): SessionReport {
|
|||
}
|
||||
|
||||
// --- 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 u = m.usage;
|
||||
const inpTok = u.input_tokens ?? 0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue