Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 41 additions & 32 deletions packages/web/src/components/dashboard/session-timeline-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,37 +35,16 @@ interface ChartDataItem {
toolSummary: string
}

function getToolSummaryForIndex(
index: number,
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 ''
function formatToolSummary(tools: ToolCallPoint[]): string {
if (tools.length === 0) return ''

// μ΄λ¦„λ³„λ‘œ 카운트
const counts = new Map<string, number>()
for (const tool of relevantTools) {
for (const tool of tools) {
const name = tool.toolName || 'unknown'
counts.set(name, (counts.get(name) || 0) + 1)
}

// λ°°μ—΄λ‘œ λ³€ν™˜ν•˜μ—¬ 카운트 λ‚΄λ¦Όμ°¨μˆœ μ •λ ¬
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
Expand Down Expand Up @@ -144,14 +123,44 @@ export function SessionTimelineChart({
// useMemo둜 μ΅œμ ν™”ν•˜μ—¬ 데이터 변경이 없을 λ•Œ μΊμ‹œλœ κ²°κ³Όλ₯Ό μž¬μ‚¬μš©ν•¨.
// 이둜 인해 λ¦¬λ Œλ”λ§ 속도가 ν–₯상됨.
const chartData: ChartDataItem[] = useMemo(() => {
return usageTimeline.map((u, idx) => ({
relativeTime: formatRelativeTime(u.timestamp, sessionStartedAt),
input: u.inputTokens,
output: u.outputTokens,
cost: u.estimatedCostUsd,
model: u.model,
toolSummary: getToolSummaryForIndex(idx, usageTimeline, toolCalls),
}))
// ⚑ Bolt: O(N*M) nested loop replaced with O(N+M) pointer-based algorithm
// by pre-sorting usage and tools by timestamp. This significantly improves
// performance for large sessions with many tools and usage records.
const sortedUsage = [...usageTimeline].sort(
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
)

const sortedTools = [...toolCalls].sort(
(a, b) => a.parsedTimestamp - b.parsedTimestamp
)

const result: ChartDataItem[] = []
let toolIdx = 0

for (let i = 0; i < sortedUsage.length; i++) {
const u = sortedUsage[i]!
const currentTimestamp = new Date(u.timestamp).getTime()

const bucketTools: ToolCallPoint[] = []
while (
toolIdx < sortedTools.length &&
sortedTools[toolIdx]!.parsedTimestamp <= currentTimestamp
) {
bucketTools.push(sortedTools[toolIdx]!)
toolIdx++
}

result.push({
relativeTime: formatRelativeTime(u.timestamp, sessionStartedAt),
input: u.inputTokens,
output: u.outputTokens,
cost: u.estimatedCostUsd,
model: u.model,
toolSummary: formatToolSummary(bucketTools),
})
}

return result
}, [usageTimeline, sessionStartedAt, toolCalls])

if (usageTimeline.length === 0) {
Expand Down
Loading