mirror of
https://github.com/alibaba/open-code-review.git
synced 2026-08-21 06:34:29 +00:00
Some checks are pending
CI / test (push) Waiting to run
CI / cross-compile (amd64, darwin) (push) Waiting to run
CI / cross-compile (amd64, windows) (push) Waiting to run
CI / cross-compile (arm64, windows) (push) Waiting to run
CI / cross-compile (arm64, darwin) (push) Waiting to run
CI / cross-compile (arm64, linux) (push) Waiting to run
CodeQL Advanced / Analyze (go) (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
* feat(viewer): add review comment tag filters * fix(viewer): normalize filter chip state values Use the same empty-string fallback when updating filter-chip active state as when handling clicks, preventing filters without a value attribute from appearing inactive after selection. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(viewer): improve comment tag filter controls * fix(viewer): support combined comment filters * fix(viewer): simplify active filter chip ring --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
281 lines
16 KiB
HTML
281 lines
16 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Session Detail - Open Code Review Viewer</title>
|
|
|
|
<link rel="stylesheet" href="/static/style.css">
|
|
</head>
|
|
<body>
|
|
<nav class="breadcrumb"><a href="/" class="nav-brand"><span class="brand-icon"></span>Open Code Review Viewer</a><span class="sep">/</span><a href="/r/{{.EncodedRepo}}">{{.RepoName}}</a><span class="sep">/</span><span class="current">{{.Session.Summary.SessionID | truncate 12}}</span></nav>
|
|
<main>
|
|
<div class="session-header">
|
|
<h2>Session: {{.Session.Summary.SessionID}}</h2>
|
|
<div class="meta">
|
|
<span><strong>CWD:</strong> {{.Session.Summary.CWD}}</span>
|
|
<span><strong>Branch:</strong> {{.Session.Summary.GitBranch}}</span>
|
|
<span><strong>Mode:</strong> {{if .Session.Summary.ReviewMode}}{{.Session.Summary.ReviewMode}}{{else}}-{{end}}</span>
|
|
{{if eq .Session.Summary.ReviewMode "range"}}
|
|
<span><strong>From:</strong> <code>{{.Session.Summary.DiffFrom}}</code></span>
|
|
<span><strong>To:</strong> <code>{{.Session.Summary.DiffTo}}</code></span>
|
|
{{end}}
|
|
{{if eq .Session.Summary.ReviewMode "commit"}}
|
|
<span><strong>Commit:</strong> <code>{{.Session.Summary.DiffCommit}}</code></span>
|
|
{{end}}
|
|
<span><strong>Model:</strong> <code>{{.Session.Summary.Model}}</code></span>
|
|
<span><strong>Duration:</strong> {{formatDuration .Session.Summary.DurationSec}}</span>
|
|
<span><strong>Files:</strong> {{.Session.Summary.FileCount}}</span>
|
|
<span><strong>Status:</strong> {{if .Session.Summary.Aborted}}aborted{{else if .Session.Summary.Legacy}}legacy{{else}}{{.Session.Summary.TerminalState}}{{end}}</span>
|
|
</div>
|
|
</div>
|
|
|
|
{{if .Session.Summary.RunManifest}}
|
|
<div class="token-summary">
|
|
<h3>Coverage</h3>
|
|
<div class="token-stats">
|
|
<div class="token-item"><div class="token-value">{{.Session.Summary.SelectedCount}}</div><div class="token-label">Selected</div></div>
|
|
<div class="token-item"><div class="token-value">{{.Session.Summary.CompletedCount}}</div><div class="token-label">Completed</div></div>
|
|
<div class="token-item"><div class="token-value">{{.Session.Summary.ReusedCount}}</div><div class="token-label">Reused</div></div>
|
|
<div class="token-item"><div class="token-value{{if .Session.Summary.FailedCount}} badge-error{{end}}">{{.Session.Summary.FailedCount}}</div><div class="token-label">Failed</div></div>
|
|
<div class="token-item"><div class="token-value">{{.Session.Summary.WaivedCount}}</div><div class="token-label">Waived</div></div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
<div class="token-summary">
|
|
<h3>Token Usage</h3>
|
|
<div class="token-stats">
|
|
<div class="token-item">
|
|
<div class="token-value" title="{{.Session.TokenUsage.TotalPromptTokens}}">{{formatNumber .Session.TokenUsage.TotalPromptTokens}}</div>
|
|
<div class="token-label">Prompt Tokens</div>
|
|
</div>
|
|
<div class="token-item">
|
|
<div class="token-value" title="{{.Session.TokenUsage.TotalCompletionTokens}}">{{formatNumber .Session.TokenUsage.TotalCompletionTokens}}</div>
|
|
<div class="token-label">Completion Tokens</div>
|
|
</div>
|
|
<div class="token-item">
|
|
<div class="token-value" title="{{add .Session.TokenUsage.TotalPromptTokens .Session.TokenUsage.TotalCompletionTokens}}">{{formatNumber (add .Session.TokenUsage.TotalPromptTokens .Session.TokenUsage.TotalCompletionTokens)}}</div>
|
|
<div class="token-label">Total Tokens</div>
|
|
</div>
|
|
<div class="token-item">
|
|
<div class="token-value" title="{{.Session.TokenUsage.RequestCount}}">{{formatNumber .Session.TokenUsage.RequestCount}}</div>
|
|
<div class="token-label">LLM Requests</div>
|
|
</div>
|
|
{{if or .Session.TokenUsage.TotalCacheReadTokens .Session.TokenUsage.TotalCacheWriteTokens}}
|
|
<div class="token-item">
|
|
<div class="token-value" title="{{.Session.TokenUsage.TotalCacheReadTokens}}">{{formatNumber .Session.TokenUsage.TotalCacheReadTokens}}</div>
|
|
<div class="token-label">Cache Read</div>
|
|
</div>
|
|
<div class="token-item">
|
|
<div class="token-value" title="{{.Session.TokenUsage.TotalCacheWriteTokens}}">{{formatNumber .Session.TokenUsage.TotalCacheWriteTokens}}</div>
|
|
<div class="token-label">Cache Write</div>
|
|
</div>
|
|
{{end}}
|
|
{{if .Session.Summary.LLMFailures}}
|
|
<div class="token-item">
|
|
<div class="token-value badge-error">{{.Session.Summary.LLMFailures}}</div>
|
|
<div class="token-label">LLM Failures</div>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{with .Session.TokenUsage.FileTokenBreakdown}}
|
|
<details class="token-breakdown">
|
|
<summary class="token-breakdown-toggle">
|
|
<span class="chevron-sm"></span>
|
|
<span>File breakdown</span>
|
|
<span class="file-count-badge">{{len .}} files</span>
|
|
</summary>
|
|
<div class="token-breakdown-body">
|
|
<table class="token-table">
|
|
<thead><tr><th>File</th><th>Prompt</th><th>Completion</th>{{if or $.Session.TokenUsage.TotalCacheReadTokens $.Session.TokenUsage.TotalCacheWriteTokens}}<th>Cache Read</th><th>Cache Write</th>{{end}}<th>Total</th></tr></thead>
|
|
<tbody>
|
|
{{range .}}
|
|
<tr>
|
|
<td title="{{.FilePath}}">{{.FilePath | truncate 60}}</td>
|
|
<td title="{{.PromptTokens}}">{{formatNumber .PromptTokens}}</td>
|
|
<td title="{{.CompletionTokens}}">{{formatNumber .CompletionTokens}}</td>
|
|
{{if or $.Session.TokenUsage.TotalCacheReadTokens $.Session.TokenUsage.TotalCacheWriteTokens}}<td title="{{.CacheReadTokens}}">{{formatNumber .CacheReadTokens}}</td><td title="{{.CacheWriteTokens}}">{{formatNumber .CacheWriteTokens}}</td>{{end}}
|
|
<td title="{{add .PromptTokens .CompletionTokens}}"><strong>{{formatNumber (add .PromptTokens .CompletionTokens)}}</strong></td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</details>
|
|
{{end}}
|
|
</div>
|
|
|
|
{{if .Session.Comments}}
|
|
<div class="comments-section">
|
|
<h3>Review Comments ({{len .Session.Comments}} findings)</h3>
|
|
{{with severityCounts .Session.Comments}}
|
|
<div class="comment-filter-bar severity-filters" aria-label="Filter comments by severity">
|
|
<span class="comment-filter-label">Severity:</span>
|
|
<button type="button" class="comment-filter-chip filter-all is-active" data-filter-kind="severity" data-filter-value="all" aria-pressed="true">All: {{len $.Session.Comments}}</button>
|
|
{{if .Critical}}<button type="button" class="comment-filter-chip severity-critical" data-filter-kind="severity" data-filter-value="critical" aria-pressed="false">Critical: {{.Critical}}</button>{{end}}
|
|
{{if .High}}<button type="button" class="comment-filter-chip severity-high" data-filter-kind="severity" data-filter-value="high" aria-pressed="false">High: {{.High}}</button>{{end}}
|
|
{{if .Medium}}<button type="button" class="comment-filter-chip severity-medium" data-filter-kind="severity" data-filter-value="medium" aria-pressed="false">Medium: {{.Medium}}</button>{{end}}
|
|
{{if .Low}}<button type="button" class="comment-filter-chip severity-low" data-filter-kind="severity" data-filter-value="low" aria-pressed="false">Low: {{.Low}}</button>{{end}}
|
|
</div>
|
|
{{end}}
|
|
{{with categoryCounts .Session.Comments}}
|
|
<div class="comment-filter-bar category-filters" aria-label="Filter comments by category">
|
|
<span class="comment-filter-label">Category:</span>
|
|
<button type="button" class="comment-filter-chip filter-all is-active" data-filter-kind="category" data-filter-value="all" aria-pressed="true">All: {{len $.Session.Comments}}</button>
|
|
{{if .Bug}}<button type="button" class="comment-filter-chip cat-bug" data-filter-kind="category" data-filter-value="bug" aria-pressed="false">Bug: {{.Bug}}</button>{{end}}
|
|
{{if .Security}}<button type="button" class="comment-filter-chip cat-security" data-filter-kind="category" data-filter-value="security" aria-pressed="false">Security: {{.Security}}</button>{{end}}
|
|
{{if .Performance}}<button type="button" class="comment-filter-chip cat-performance" data-filter-kind="category" data-filter-value="performance" aria-pressed="false">Performance: {{.Performance}}</button>{{end}}
|
|
{{if .Maintainability}}<button type="button" class="comment-filter-chip cat-maintainability" data-filter-kind="category" data-filter-value="maintainability" aria-pressed="false">Maintainability: {{.Maintainability}}</button>{{end}}
|
|
{{if .Test}}<button type="button" class="comment-filter-chip cat-test" data-filter-kind="category" data-filter-value="test" aria-pressed="false">Test: {{.Test}}</button>{{end}}
|
|
{{if .Style}}<button type="button" class="comment-filter-chip cat-style" data-filter-kind="category" data-filter-value="style" aria-pressed="false">Style: {{.Style}}</button>{{end}}
|
|
{{if .Documentation}}<button type="button" class="comment-filter-chip cat-documentation" data-filter-kind="category" data-filter-value="documentation" aria-pressed="false">Documentation: {{.Documentation}}</button>{{end}}
|
|
{{if .Other}}<button type="button" class="comment-filter-chip cat-other" data-filter-kind="category" data-filter-value="other" aria-pressed="false">Other: {{.Other}}</button>{{end}}
|
|
</div>
|
|
{{end}}
|
|
<div class="comment-groups">
|
|
{{range groupCommentsByFile .Session.Comments}}
|
|
<details class="comment-file-group" open>
|
|
<summary class="comment-file-header">
|
|
<span class="chevron"></span>
|
|
<span class="file-path">{{.FilePath}}</span>
|
|
<span class="file-count-badge" data-comment-count>{{len .Comments}} comments</span>
|
|
</summary>
|
|
<div class="comment-file-body">
|
|
{{range .Comments}}
|
|
<div class="comment-card" data-comment-card data-category="{{commentCategory .Category}}" data-severity="{{commentSeverity .Severity}}">
|
|
<div class="comment-meta">
|
|
{{if .Category}}<span class="comment-badge {{categoryClass .Category}}">{{.Category}}</span>{{end}}
|
|
{{if .Severity}}<span class="comment-badge {{severityClass .Severity}}">{{.Severity}}</span>{{end}}
|
|
{{if .StartLine}}<span class="comment-lines">L{{.StartLine}}{{if and .EndLine (ne .EndLine .StartLine)}}-L{{.EndLine}}{{end}}</span>{{end}}
|
|
</div>
|
|
<div class="comment-content">{{.Content}}</div>
|
|
{{if or .ExistingCode .SuggestionCode}}
|
|
<div class="comment-code-diff">
|
|
{{if .ExistingCode}}
|
|
<div class="code-panel code-existing">
|
|
<div class="code-panel-label">Existing Code</div>
|
|
<pre><code>{{.ExistingCode}}</code></pre>
|
|
</div>
|
|
{{end}}
|
|
{{if .SuggestionCode}}
|
|
<div class="code-panel code-suggestion">
|
|
<div class="code-panel-label">Suggested Change</div>
|
|
<pre><code>{{.SuggestionCode}}</code></pre>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</details>
|
|
{{end}}
|
|
</div>
|
|
<p class="comment-filter-empty" data-comment-filter-empty hidden>No comments match this filter.</p>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .Session.Summary.FilesReviewed}}
|
|
<details class="file-accordion section-accordion">
|
|
<summary class="file-accordion-header">
|
|
<span class="chevron"></span>
|
|
<span class="section-title">Files Reviewed</span>
|
|
<span class="file-count-badge">{{len .Session.Summary.FilesReviewed}} files</span>
|
|
</summary>
|
|
<div class="file-accordion-body">
|
|
<ul class="file-list">
|
|
{{range .Session.Summary.FilesReviewed}}
|
|
<li>{{.}}</li>
|
|
{{end}}
|
|
</ul>
|
|
</div>
|
|
</details>
|
|
{{end}}
|
|
|
|
{{if .Session.Files}}
|
|
<details class="file-accordion section-accordion">
|
|
<summary class="file-accordion-header">
|
|
<span class="chevron"></span>
|
|
<span class="section-title">Conversations</span>
|
|
<span class="file-count-badge">{{len .Session.Files}} files</span>
|
|
</summary>
|
|
<div class="file-accordion-body">
|
|
<div class="conversations">
|
|
{{range .Session.Files}}
|
|
<details class="file-accordion">
|
|
<summary class="file-accordion-header">
|
|
<span class="chevron"></span>
|
|
<span class="file-path">{{.FilePath}}</span>
|
|
<span class="file-count-badge">{{cardCount .Tasks}} requests</span>
|
|
</summary>
|
|
<div class="file-accordion-body">
|
|
{{range $tg := orderedTasks .Tasks}}
|
|
<div class="task-group {{taskTypeClass $tg.Type}}">
|
|
<h4 class="task-type-label">
|
|
<span class="task-type-dot"></span>
|
|
{{$tg.Type}}
|
|
</h4>
|
|
{{range $tg.Cards}}
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<span class="request-label">Request #{{.RequestNo}}</span>
|
|
{{if .Model}}<span class="badge badge-model">{{.Model}}</span>{{end}}
|
|
{{if or .PromptTokens .CompletionTokens}}<span class="badge badge-tokens">P:<span title="{{.PromptTokens}}">{{formatNumber .PromptTokens}}</span> C:<span title="{{.CompletionTokens}}">{{formatNumber .CompletionTokens}}</span>{{if or .CacheReadTokens .CacheWriteTokens}} CR:<span title="{{.CacheReadTokens}}">{{formatNumber .CacheReadTokens}}</span> CW:<span title="{{.CacheWriteTokens}}">{{formatNumber .CacheWriteTokens}}</span>{{end}}</span>{{end}}
|
|
{{if .DurationMs}}<span class="badge badge-duration">{{.DurationMs}}ms</span>{{end}}
|
|
{{if .Error}}<span class="badge badge-error">{{.Error}}</span>{{end}}
|
|
</div>
|
|
{{with .ResponseContent}}
|
|
<div class="card-body response-content">
|
|
<div class="response-text">{{.}}</div>
|
|
</div>
|
|
{{end}}
|
|
{{if .ToolCalls}}
|
|
<div class="tool-calls-section">
|
|
<div class="tool-calls-label">Tool Calls ({{len .ToolCalls}})</div>
|
|
{{range .ToolCalls}}
|
|
<div class="tool-call-item {{if not .Ok}}tool-call-error{{end}}">
|
|
<div class="tool-call-header">
|
|
<span class="tool-icon">⚙</span>
|
|
<strong class="tool-name">{{.Name}}</strong>
|
|
{{if .DurationMs}}<span class="badge badge-duration">{{.DurationMs}}ms</span>{{end}}
|
|
{{if not .Ok}}<span class="badge badge-error">Failed</span>{{end}}
|
|
</div>
|
|
<details class="tool-detail">
|
|
<summary class="tool-detail-toggle">
|
|
<span class="chevron-sm"></span>
|
|
<span>Show Details</span>
|
|
</summary>
|
|
<div class="tool-detail-body">
|
|
<div class="tool-section">
|
|
<div class="tool-section-label">Arguments</div>
|
|
<pre>{{.Arguments}}</pre>
|
|
</div>
|
|
{{if .Result}}
|
|
<div class="tool-section">
|
|
<div class="tool-section-label">Result</div>
|
|
<pre>{{.Result}}</pre>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</details>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</details>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</details>
|
|
{{end}}
|
|
|
|
<script src="/static/session.js"></script>
|
|
</main>
|
|
</body>
|
|
</html>
|