mirror of
https://github.com/alibaba/open-code-review.git
synced 2026-07-09 17:28:58 +00:00
172 lines
7.1 KiB
HTML
172 lines
7.1 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="/">Open Code Review Viewer</a> › <a href="/r/{{.EncodedRepo}}">{{.RepoName}}</a> › Session {{.Session.Summary.SessionID | truncate 12}}</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>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>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="token-summary">
|
|
<h3>Token Usage</h3>
|
|
<div class="token-stats">
|
|
<div class="token-item">
|
|
<div class="token-value">{{.Session.TokenUsage.TotalPromptTokens}}</div>
|
|
<div class="token-label">Prompt Tokens</div>
|
|
</div>
|
|
<div class="token-item">
|
|
<div class="token-value">{{.Session.TokenUsage.TotalCompletionTokens}}</div>
|
|
<div class="token-label">Completion Tokens</div>
|
|
</div>
|
|
<div class="token-item">
|
|
<div class="token-value">{{add .Session.TokenUsage.TotalPromptTokens .Session.TokenUsage.TotalCompletionTokens}}</div>
|
|
<div class="token-label">Total Tokens</div>
|
|
</div>
|
|
<div class="token-item">
|
|
<div class="token-value">{{.Session.TokenUsage.RequestCount}}</div>
|
|
<div class="token-label">LLM Requests</div>
|
|
</div>
|
|
</div>
|
|
{{with .Session.TokenUsage.FileTokenBreakdown}}
|
|
<table class="token-table">
|
|
<thead><tr><th>File</th><th>Prompt</th><th>Completion</th><th>Total</th></tr></thead>
|
|
<tbody>
|
|
{{range .}}
|
|
<tr>
|
|
<td title="{{.FilePath}}">{{.FilePath | truncate 60}}</td>
|
|
<td>{{.PromptTokens}}</td>
|
|
<td>{{.CompletionTokens}}</td>
|
|
<td><strong>{{add .PromptTokens .CompletionTokens}}</strong></td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
{{end}}
|
|
</div>
|
|
|
|
{{if .Session.Summary.FilesReviewed}}
|
|
<h3>Files Reviewed</h3>
|
|
<ul class="file-list">
|
|
{{range .Session.Summary.FilesReviewed}}
|
|
<li>{{.}}</li>
|
|
{{end}}
|
|
</ul>
|
|
{{end}}
|
|
|
|
<h3>Conversations ({{len .Session.Files}} files)</h3>
|
|
<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:{{.PromptTokens}} C:{{.CompletionTokens}}</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>
|
|
|
|
<script>
|
|
document.querySelectorAll('.response-text').forEach(function(el) {
|
|
var text = el.textContent;
|
|
var esc = function(s) {
|
|
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
.replace(/"/g, '"').replace(/'/g, ''');
|
|
};
|
|
var codeBlocks = [];
|
|
var html = esc(text);
|
|
html = html.replace(/```(\w*)\n([\s\S]*?)```/g, function(_, lang, code) {
|
|
codeBlocks.push(code.replace(/^\n|\n$/g, ''));
|
|
return '%%CODEBLOCK_' + (codeBlocks.length - 1) + '%%';
|
|
});
|
|
html = html
|
|
.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>')
|
|
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
|
.replace(/^### (.+)$/gm, '<div class="md-h3">$1</div>')
|
|
.replace(/^## (.+)$/gm, '<div class="md-h2">$1</div>')
|
|
.replace(/^# (.+)$/gm, '<div class="md-h1">$1</div>')
|
|
.replace(/^[-*] (.+)$/gm, '<div class="md-li">• $1</div>')
|
|
.replace(/\n{2,}/g, '<br><br>')
|
|
.replace(/\n/g, '<br>');
|
|
codeBlocks.forEach(function(code, i) {
|
|
html = html.replace('%%CODEBLOCK_' + i + '%%',
|
|
'<pre class="code-block"><code>' + code + '</code></pre>');
|
|
});
|
|
el.innerHTML = html;
|
|
});
|
|
</script>
|
|
</main>
|
|
</body>
|
|
</html>
|