[Superseded] ⚡ Bolt: O(N*M) timeline rendering optimization - #337
[Superseded] ⚡ Bolt: O(N*M) timeline rendering optimization#337seonghobae wants to merge 4 commits into
Conversation
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
Warning Review limit reached
Next review available in: 7 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (8)
📝 WalkthroughWalkthrough세션 타임라인의 도구 호출 집계를 반복 필터링 방식에서 정렬된 배열의 투 포인터 단일 패스로 변경하고, 계산된 요약을 차트 데이터에 재사용하도록 수정했습니다. 관련 최적화 메모리도 추가되었습니다. Changes세션 타임라인 집계 최적화
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/web/src/components/dashboard/session-timeline-chart.tsx`:
- Around line 38-90: Update getToolSummaries so its two-pointer traversal does
not assume usageTimeline is timestamp-sorted: create a timestamp-ascending view
that retains each entry’s original index, compute summaries in that sorted
order, and write each result back to its original index. Preserve the existing
output array ordering and toolCalls processing behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 7b314248-72e2-4e3b-ba56-f2dc1f053131
📒 Files selected for processing (2)
.jules/bolt.mdpackages/web/src/components/dashboard/session-timeline-chart.tsx
| // ⚡ Bolt: O(N*M) 필터를 제거하고 투 포인터(Two-pointer)를 사용하여 | ||
| // 타임라인과 도구 호출 데이터를 O(N+M)으로 병합하는 최적화된 함수 | ||
| function getToolSummaries( | ||
| usageTimeline: SessionTimelineUsage[], | ||
| toolCalls: ToolCallPoint[] | ||
| ): string { | ||
| if (toolCalls.length === 0) return '' | ||
|
|
||
| const currentTimestamp = new Date(usageTimeline[index]!.timestamp).getTime() | ||
| const prevTimestamp = | ||
| index > 0 ? new Date(usageTimeline[index - 1]!.timestamp).getTime() : 0 | ||
|
|
||
| // 현재 usageTimeline timestamp 이전이면서, 이전 usageTimeline timestamp 이후의 tool events 찾기 | ||
| // 첫 번째 bar(index=0)는 prevTimestamp가 0이므로 해당 bar 이전의 모든 이벤트를 포함 | ||
| const relevantTools = toolCalls.filter((e) => { | ||
| const toolTimestamp = e.parsedTimestamp | ||
| return toolTimestamp <= currentTimestamp && toolTimestamp > prevTimestamp | ||
| }) | ||
|
|
||
| if (relevantTools.length === 0) return '' | ||
|
|
||
| // 이름별로 카운트 | ||
| const counts = new Map<string, number>() | ||
| for (const tool of relevantTools) { | ||
| const name = tool.toolName || 'unknown' | ||
| counts.set(name, (counts.get(name) || 0) + 1) | ||
| ): string[] { | ||
| if (usageTimeline.length === 0) return [] | ||
| if (toolCalls.length === 0) return new Array(usageTimeline.length).fill('') | ||
|
|
||
| const summaries: string[] = new Array(usageTimeline.length).fill('') | ||
| let toolIdx = 0 | ||
| const totalTools = toolCalls.length | ||
|
|
||
| for (let i = 0; i < usageTimeline.length; i++) { | ||
| const currentTimestamp = new Date(usageTimeline[i]!.timestamp).getTime() | ||
| const prevTimestamp = i > 0 ? new Date(usageTimeline[i - 1]!.timestamp).getTime() : 0 | ||
|
|
||
| const counts = new Map<string, number>() | ||
| let toolsInBucket = 0 | ||
|
|
||
| while (toolIdx < totalTools) { | ||
| const tool = toolCalls[toolIdx]! | ||
| if (tool.parsedTimestamp <= prevTimestamp) { | ||
| toolIdx++ | ||
| continue | ||
| } | ||
| if (tool.parsedTimestamp > currentTimestamp) { | ||
| break | ||
| } | ||
| const name = tool.toolName || 'unknown' | ||
| counts.set(name, (counts.get(name) || 0) + 1) | ||
| toolsInBucket++ | ||
| toolIdx++ | ||
| } | ||
|
|
||
| if (toolsInBucket === 0) continue | ||
|
|
||
| const sorted = Array.from(counts.entries()).sort((a, b) => b[1] - a[1]) | ||
| const displayCount = Math.min(3, sorted.length) | ||
| const displayItems = sorted.slice(0, displayCount).map(([name, count]) => { | ||
| return count > 1 ? `${name} x${count}` : name | ||
| }) | ||
|
|
||
| const remaining = sorted.length - displayCount | ||
| if (remaining > 0) { | ||
| summaries[i] = `${displayItems.join(', ')} +${remaining} more` | ||
| } else { | ||
| summaries[i] = displayItems.join(', ') | ||
| } | ||
| } | ||
|
|
||
| // 배열로 변환하여 카운트 내림차순 정렬 | ||
| const sorted = Array.from(counts.entries()).sort((a, b) => b[1] - a[1]) | ||
|
|
||
| // 최대 3개까지만 표시 | ||
| const displayCount = Math.min(3, sorted.length) | ||
| const displayItems = sorted.slice(0, displayCount).map(([name, count]) => { | ||
| return count > 1 ? `${name} x${count}` : name | ||
| }) | ||
|
|
||
| const remaining = sorted.length - displayCount | ||
| if (remaining > 0) { | ||
| return `${displayItems.join(', ')} +${remaining} more` | ||
| } | ||
|
|
||
| return displayItems.join(', ') | ||
| return summaries | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
usageTimeline 정렬 보장이 없으면 투 포인터가 오동작할 수 있습니다. 원본 인덱스를 보존한 정렬 뷰로 순회하거나, 입력이 항상 timestamp 오름차순임을 보장하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/web/src/components/dashboard/session-timeline-chart.tsx` around
lines 38 - 90, Update getToolSummaries so its two-pointer traversal does not
assume usageTimeline is timestamp-sorted: create a timestamp-ascending view that
retains each entry’s original index, compute summaries in that sorted order, and
write each result back to its original index. Preserve the existing output array
ordering and toolCalls processing behavior.
Superseded
Closed without merge because current
developmentalalready contains the durable implementation of this exact objective. At base snapshot4f8796ec8c3a8d130136029650705714724cb0ac,SessionTimelineChartuses a documentedbuildChartData()helper that sorts local usage/tool copies and advances a single forward tool cursor, replacing the former nested per-row tool-call filtering.This PR is anchored to stale base
9ef092b9979d46b96063701e706521d21407d6a9, exact head741401008cb8b55b856dc7fd28d50eec0f8a5563, is non-mergeable, and carries eleven changed files for the same primary goal. Keeping it open preserves a competing stale implementation and unrelated-diff risk. No checks, reviews, or approvals from this head transfer to current development.Any remaining optimization should be rebuilt as a focused change on the live base with fresh exact-head evidence.