Add dark theme, richer Markdown and readable plugin results. Improve Android input evidence, response integrity and diagnostics. Unify lifecycle, guidance receipts and evidence sources across clients. Keep direct action dispatch without an added AI quota approval layer. Align version metadata at 2.2.0 and bundle Helper 1.2.2. Validation: 3063 backend tests before the version bump; 44 after. Frontend: 317 tests and production build passed.
88 lines
8.1 KiB
HTML
88 lines
8.1 KiB
HTML
@if (sessionId()) {
|
||
<section class="model-requests" aria-label="模型请求状态">
|
||
@for (review of activeReviews(); track review.review_id) {
|
||
<div class="request-live review-live" [class.stale]="unavailable()" aria-live="polite">
|
||
<div class="request-heading">
|
||
<span class="status-dot" [class.waiting]="!unavailable()"></span>
|
||
<strong>{{ unavailable() ? '验收状态暂时无法更新' : reviewStage(review) }}</strong>
|
||
<span class="elapsed">验收已用 {{ reviewElapsed(review) }} 秒</span>
|
||
</div>
|
||
@if (!unavailable()) {
|
||
<p>完整结论确认前,测试仍为待判定。剩余验收预算 {{ reviewRemaining(review) }} 秒 / 总计 {{ review.total_seconds }} 秒。</p>
|
||
<p class="notice">已发起 {{ review.request_count }} 次模型请求,已重试 {{ review.retry_count }} 次;重试复用已有证据。</p>
|
||
}
|
||
</div>
|
||
}
|
||
@for (request of active(); track request.attempt_id) {
|
||
<div class="request-live" [class.retrying]="request.phase === 'retry_wait'" [class.stale]="unavailable()">
|
||
<div class="request-heading" aria-live="polite">
|
||
<span class="status-dot" [class.waiting]="!unavailable()"></span>
|
||
<strong>{{ unavailable() ? '请求状态暂时无法更新' : phase(request) }}</strong>
|
||
<span class="model">{{ request.model || '模型未记录' }} · {{ role(request) }}{{ request.is_fallback ? ' · 兜底模型' : '' }}</span>
|
||
<span class="elapsed">已用 {{ elapsed(request) }} 秒</span>
|
||
</div>
|
||
@if (!unavailable()) {
|
||
<p>{{ hint(request, now()) }} @if (request.phase !== 'retry_wait') { 本次请求最长等待 {{ request.timeout_seconds }} 秒。 }</p>
|
||
}
|
||
@if (request.error) { <p class="error">{{ request.error }}</p> }
|
||
</div>
|
||
}
|
||
@if (unavailable()) { <p class="notice" role="status">诊断信息暂时不可用;显示的是上次记录,不能据此判断任务是否卡住。</p> }
|
||
<details>
|
||
<summary>模型请求诊断 @if (requests().length) { <span>· 最近 {{ requests().length }} 次尝试</span> }</summary>
|
||
<div class="diagnostics-header"><span>时间按本机时区显示;下载文件同时包含 UTC 时间。</span><a [href]="downloadUrl()" download>下载完整诊断</a></div>
|
||
@if (loading()) { <p class="notice">正在加载请求记录…</p> }
|
||
@if (!loading() && !unavailable() && !requests().length) { <p class="notice">暂无请求诊断。旧任务没有记录的时间和用量无法补推。</p> }
|
||
<div class="request-history">
|
||
@for (review of reviews(); track review.review_id) {
|
||
<details class="request-detail review-detail">
|
||
<summary>{{ review.started_at * 1000 | date:'HH:mm:ss' }} · 最终验收 · {{ reviewStage(review) }}</summary>
|
||
<dl>
|
||
<dt>验收编号</dt><dd>{{ review.review_id }} · {{ review.attempt_id }}</dd>
|
||
<dt>验收预算</dt><dd>总计 {{ review.total_seconds }} 秒 · 已用 {{ reviewElapsed(review) }} 秒</dd>
|
||
<dt>模型请求</dt><dd>{{ review.request_count }} 次,实际重试 {{ review.retry_count }} 次;每个请求最多重试 {{ review.max_retries }} 次,总预算内执行</dd>
|
||
<dt>开始时间</dt><dd>{{ review.started_at * 1000 | date:'yyyy-MM-dd HH:mm:ss.SSS Z' }}</dd>
|
||
<dt>预算截止</dt><dd>{{ review.deadline_at * 1000 | date:'yyyy-MM-dd HH:mm:ss.SSS Z' }}</dd>
|
||
<dt>结束时间</dt><dd>{{ review.ended_at ? (review.ended_at * 1000 | date:'HH:mm:ss.SSS') : '未记录' }}</dd>
|
||
@if (review.last_failure_category) { <dt>最近错误类别</dt><dd>{{ review.last_failure_category }}</dd> }
|
||
@if (review.status === 'budget_exhausted') { <dt>结束原因</dt><dd class="error">取证、模型请求与重试已耗尽验收总预算;未确认通过。</dd> }
|
||
@if (review.status === 'request_retries_exhausted') { <dt>结束原因</dt><dd class="error">验收模型请求重试已用尽;未确认通过,没有重跑设备操作。</dd> }
|
||
@if (review.status === 'cancelled') { <dt>结束原因</dt><dd>{{ cancellationReason(review.cancel_reason) }}</dd> }
|
||
</dl>
|
||
</details>
|
||
}
|
||
@for (request of requests(); track request.attempt_id) {
|
||
<details class="request-detail">
|
||
<summary><span>{{ request.started_at * 1000 | date:'HH:mm:ss' }}</span> {{ request.model || '模型' }} · {{ role(request) }} · {{ phase(request) }} @if (!request.active && !request.ended_at) { <span>(未记录结束,当前未在执行)</span> } @if (request.is_fallback) { <span class="fallback">兜底</span> }</summary>
|
||
<dl>
|
||
<dt>本地请求编号</dt><dd>{{ request.request_id }}</dd>
|
||
<dt>本次尝试编号</dt><dd>{{ request.attempt_id }}</dd>
|
||
@if (request.previous_attempt_id) { <dt>上次尝试编号</dt><dd>{{ request.previous_attempt_id }}</dd> }
|
||
<dt>出站请求编号</dt><dd>{{ request.client_request_id || '未记录' }}</dd>
|
||
<dt>服务端请求编号</dt><dd>{{ request.server_request_id || '服务未提供' }}</dd>
|
||
<dt>响应编号</dt><dd>{{ request.response_id || '服务未提供' }}</dd>
|
||
@if (request.server_trace_id) { <dt>服务端链路编号</dt><dd>{{ request.server_trace_id }}</dd> }
|
||
@if (request.traceparent) { <dt>链路上下文</dt><dd>{{ request.traceparent }}</dd> }
|
||
<dt>模型配置</dt><dd>{{ request.model || '未记录' }} / {{ request.reasoning_effort || '默认' }} · 第 {{ request.attempt }} 次请求,最多重试 {{ request.max_retries }} 次</dd>
|
||
@if (request.final_review; as review) { <dt>所属最终验收</dt><dd>{{ review.review_id }} · {{ reviewStage(review) }} · 请求记录时剩余 {{ review.remaining_seconds }} 秒</dd> }
|
||
<dt>输出 Token 上限</dt><dd>{{ request.max_output_tokens || '未设置(由模型服务决定)' }}</dd>
|
||
<dt>有效输出片段</dt><dd>{{ request.output_chunks ?? '未记录' }}(客户端计数,与服务端 SSE 事件数口径不同)</dd>
|
||
<dt>工具参数</dt><dd>{{ request.tool_argument_chars ?? '未记录' }} 字符 @for (tool of request.tools || []; track $index) { <span class="image-size">{{ tool.name || '工具名未提供' }} · {{ tool.argument_chars }} 字符</span> }</dd>
|
||
<dt>请求开始</dt><dd>{{ request.started_at * 1000 | date:'yyyy-MM-dd HH:mm:ss.SSS Z' }}</dd>
|
||
<dt>收到响应头</dt><dd>{{ request.headers_at ? (request.headers_at * 1000 | date:'HH:mm:ss.SSS') : '未记录' }} · HTTP {{ request.http_status || '未知' }}</dd>
|
||
<dt>首个有效输出</dt><dd>{{ request.first_output_at ? (request.first_output_at * 1000 | date:'HH:mm:ss.SSS') : '未收到/未记录' }}</dd>
|
||
<dt>最后有效输出</dt><dd>{{ request.last_output_at ? (request.last_output_at * 1000 | date:'HH:mm:ss.SSS') : '未收到/未记录' }}</dd>
|
||
<dt>结束时间</dt><dd>{{ request.ended_at ? (request.ended_at * 1000 | date:'HH:mm:ss.SSS') : '尚未记录' }}</dd>
|
||
@if (termination(request); as reason) { <dt>结束原因</dt><dd>{{ reason }}</dd> }
|
||
@if (request.cancel_phase) { <dt>中断阶段</dt><dd>{{ phase(request.cancel_phase) }}</dd> }
|
||
<dt>输入图片</dt><dd>{{ request.images.length }} 张 @for (image of request.images; track $index) { <span class="image-size">{{ image.width && image.height ? image.width + ' × ' + image.height : '尺寸未知' }}</span> }</dd>
|
||
<dt>Token 用量</dt><dd>{{ request.usage ? '输入 ' + request.usage.prompt_tokens + ' / 输出 ' + request.usage.completion_tokens + ' / 合计 ' + request.usage.total_tokens : '未知(服务未报告)' }}</dd>
|
||
@if (request.error) { <dt>失败原因</dt><dd class="error">{{ request.error_type }} · {{ request.error }} · 阶段 {{ phase(request.failure_phase || '') }} @if (request.category) { · {{ request.category }} }</dd> }
|
||
</dl>
|
||
</details>
|
||
}
|
||
</div>
|
||
</details>
|
||
</section>
|
||
}
|