personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

tokens: revamp summary header to clean two-row table

Replace the oversized summary banner (4 big metrics + type table) with a
single compact table showing Generate and Cogitate rows. Last column is
now $/Seg (per-segment average) instead of per-request average. Remove
dead _parse_context_prefix function, stale comment, and unused
tokens:daychange event listener.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+36 -109
+6 -16
apps/tokens/routes.py
··· 23 23 ) 24 24 25 25 26 - def _parse_context_prefix(context: str) -> str: 27 - """Return full context string for grouping.""" 28 - if not context: 29 - return "unknown" 30 - return context 31 - 32 - 33 26 def _aggregate_token_data(day: str) -> Dict[str, Any]: 34 27 """ 35 28 Read and aggregate token usage data for a given day. ··· 152 145 by_model[model]["provider"] = provider 153 146 154 147 # Update context breakdown 155 - context_prefix = _parse_context_prefix(context) 148 + context_prefix = context or "unknown" 156 149 by_context[context_prefix]["requests"] += 1 157 150 by_context[context_prefix]["tokens"] += total_entry_tokens 158 151 by_context[context_prefix]["cost"] += entry_cost ··· 237 230 ] 238 231 context_list.sort(key=lambda x: x["cost"], reverse=True) 239 232 240 - # Build segment list (exclude [unattributed] from avg calculation) 233 + # Build segment list 241 234 segment_list = [ 242 235 { 243 236 "segment": seg, ··· 253 246 ] 254 247 segment_list.sort(key=lambda x: x["cost"], reverse=True) 255 248 256 - # Calculate segment average (excluding unattributed) 257 - attributed_segments = [s for s in segment_list if s["segment"] != "[unattributed]"] 258 - segment_count = len(attributed_segments) 259 - segment_total_cost = sum(s["cost"] for s in attributed_segments) 260 - segment_avg_cost = ( 261 - round(segment_total_cost / segment_count, 6) if segment_count > 0 else 0.0 249 + # Count attributed segments (excluding [unattributed]) 250 + segment_count = sum( 251 + 1 for s in segment_list if s["segment"] != "[unattributed]" 262 252 ) 263 253 264 254 # Calculate cached/reasoning percentages for display annotations ··· 303 293 "requests": total_requests, 304 294 "tokens": total_tokens, 305 295 "cost": round(total_cost, 6), 306 - "segment_avg_cost": segment_avg_cost, 296 + "segment_count": segment_count, 307 297 }, 308 298 "by_provider": provider_list, 309 299 "by_model": model_list,
+30 -93
apps/tokens/workspace.html
··· 6 6 <div class="dashboard-content" id="dashboard" style="display: none;"> 7 7 <!-- Daily Summary Card --> 8 8 <div class="summary-card"> 9 - <div class="summary-row"> 10 - <div class="summary-item"> 11 - <div class="summary-label">Daily Total</div> 12 - <div class="summary-value" id="total-cost">$0.00</div> 13 - </div> 14 - <div class="summary-item"> 15 - <div class="summary-label">Total Tokens</div> 16 - <div class="summary-value" id="total-tokens">0</div> 17 - </div> 18 - <div class="summary-item"> 19 - <div class="summary-label">Requests</div> 20 - <div class="summary-value" id="total-requests">0</div> 21 - </div> 22 - <div class="summary-item"> 23 - <div class="summary-label">Avg/Segment</div> 24 - <div class="summary-value" id="segment-avg-cost">-</div> 25 - </div> 26 - </div> 27 - <table class="summary-type-table"> 9 + <table class="summary-table"> 28 10 <thead> 29 11 <tr> 30 12 <th></th> 31 13 <th>Cost</th> 32 14 <th>Tokens</th> 33 15 <th>Requests</th> 34 - <th>Avg/Req</th> 16 + <th>$/Seg</th> 35 17 </tr> 36 18 </thead> 37 19 <tbody> 38 20 <tr> 39 - <td>Generate</td> 40 - <td id="generate-cost">$0.00</td> 41 - <td id="generate-tokens">0</td> 42 - <td id="generate-requests">0</td> 43 - <td id="generate-avg">-</td> 21 + <td class="row-label">Generate</td> 22 + <td id="generate-cost">-</td> 23 + <td id="generate-tokens">-</td> 24 + <td id="generate-requests">-</td> 25 + <td id="generate-seg-avg">-</td> 44 26 </tr> 45 27 <tr> 46 - <td>Cogitate</td> 47 - <td id="cogitate-cost">$0.00</td> 48 - <td id="cogitate-tokens">0</td> 49 - <td id="cogitate-requests">0</td> 50 - <td id="cogitate-avg">-</td> 28 + <td class="row-label">Cogitate</td> 29 + <td id="cogitate-cost">-</td> 30 + <td id="cogitate-tokens">-</td> 31 + <td id="cogitate-requests">-</td> 32 + <td id="cogitate-seg-avg">-</td> 51 33 </tr> 52 34 </tbody> 53 35 </table> ··· 193 175 } 194 176 195 177 .summary-card { 196 - display: flex; 197 - flex-direction: column; 198 - align-items: center; 199 178 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 200 179 color: white; 201 180 border-radius: 12px; 202 - padding: 2em; 181 + padding: 1.2em 2em; 203 182 margin-bottom: 2em; 204 183 box-shadow: 0 4px 15px rgba(0,0,0,0.1); 205 184 } 206 185 207 - .summary-row { 208 - display: flex; 209 - gap: 2em; 210 - justify-content: center; 186 + .summary-table { 211 187 width: 100%; 188 + border-collapse: collapse; 212 189 } 213 190 214 - .summary-item { 215 - text-align: center; 191 + .summary-table th, 192 + .summary-table td { 193 + text-align: right; 194 + padding: 0.4em 1em; 216 195 } 217 196 218 - .summary-label { 219 - font-size: 0.85em; 220 - opacity: 0.9; 221 - margin-bottom: 0.5em; 222 - } 223 - 224 - .summary-value { 225 - font-size: 2em; 226 - font-weight: bold; 227 - } 228 - 229 - .summary-type-table { 230 - width: 100%; 231 - border-collapse: collapse; 232 - margin-top: 1.5em; 233 - border-top: 1px solid rgba(255,255,255,0.2); 234 - } 235 - 236 - .summary-type-table th { 237 - text-align: right; 238 - font-size: 0.75em; 197 + .summary-table th { 198 + font-size: 0.8em; 199 + font-weight: normal; 239 200 opacity: 0.7; 240 - padding: 0.3em 1em; 241 - font-weight: normal; 242 201 } 243 202 244 - .summary-type-table th:first-child { 203 + .summary-table th:first-child, 204 + .summary-table td.row-label { 245 205 text-align: left; 246 206 } 247 207 248 - .summary-type-table td { 249 - text-align: right; 208 + .summary-table td { 250 209 font-size: 1.3em; 251 210 font-weight: bold; 252 - padding: 0.3em 1em; 253 211 } 254 212 255 - .summary-type-table td:first-child { 256 - text-align: left; 257 - font-size: 0.9em; 213 + .summary-table td.row-label { 214 + font-size: 0.95em; 258 215 font-weight: 600; 259 216 opacity: 0.9; 260 217 } ··· 451 408 452 409 // Render dashboard 453 410 function renderDashboard(data) { 454 - // Update summary 455 - document.getElementById('total-cost').textContent = formatCost(data.total.cost); 456 - document.getElementById('total-tokens').textContent = formatNumber(data.total.tokens); 457 - document.getElementById('total-requests').textContent = formatNumber(data.total.requests); 458 - 459 - // Update segment avg cost (show "-" if no segment data) 460 - const segmentAvgEl = document.getElementById('segment-avg-cost'); 461 - if (data.total.segment_avg_cost > 0) { 462 - segmentAvgEl.textContent = formatCost(data.total.segment_avg_cost); 463 - } else { 464 - segmentAvgEl.textContent = '-'; 465 - } 466 - 467 411 // Update type breakdown rows 468 412 const byType = data.by_type || {}; 413 + const segCount = data.total.segment_count || 0; 469 414 ['generate', 'cogitate'].forEach(type => { 470 415 const d = byType[type] || {}; 471 416 const cost = d.cost || 0; 472 417 const tokens = d.tokens || 0; 473 418 const requests = d.requests || 0; 474 - const avg = requests > 0 ? cost / requests : 0; 419 + const segAvg = segCount > 0 ? cost / segCount : 0; 475 420 document.getElementById(`${type}-cost`).textContent = formatCost(cost); 476 421 document.getElementById(`${type}-tokens`).textContent = formatNumber(tokens); 477 422 document.getElementById(`${type}-requests`).textContent = formatNumber(requests); 478 - document.getElementById(`${type}-avg`).textContent = requests > 0 ? formatCost(avg) : '-'; 423 + document.getElementById(`${type}-seg-avg`).textContent = segCount > 0 ? formatCost(segAvg) : '-'; 479 424 }); 480 425 481 426 // Render provider table ··· 661 606 }); 662 607 663 608 // Load initial data 664 - loadTokenData(currentDay); 665 - }); 666 - 667 - // Listen for day changes from app bar 668 - window.addEventListener('tokens:daychange', (e) => { 669 - currentDay = e.detail.day; 670 - document.getElementById('loading').style.display = 'block'; 671 - document.getElementById('dashboard').style.display = 'none'; 672 609 loadTokenData(currentDay); 673 610 }); 674 611 </script>