fix: improve cross-device evidence, request diagnostics and prompt efficiency
This commit is contained in:
1 parent
621f30e437
commit
a66803651a
60 files changed
+3097
-160
No files matched your search
+94
@@ -0,0 +1,94 @@
|
||||
import { ChangeDetectionStrategy, Component, computed, effect, inject, input, output, signal } from '@angular/core';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
export interface ModelRequest {
|
||||
request_id: string; attempt_id: string; model?: string; reasoning_effort?: string;
|
||||
role: string; is_fallback: boolean; phase: string; active: boolean;
|
||||
attempt: number; max_retries: number; timeout_seconds: number; elapsed_seconds: number;
|
||||
started_at: number; headers_at?: number; first_output_at?: number; last_output_at?: number;
|
||||
ended_at?: number; retry_at?: number; http_status?: number; server_request_id?: string;
|
||||
error?: string; error_type?: string; failure_phase?: string;
|
||||
output_chunks?: number; tool_argument_chars?: number; max_output_tokens?: number | null;
|
||||
tools?: { name: string | null; argument_chars: number }[];
|
||||
usage: { prompt_tokens: number; completion_tokens: number; total_tokens: number } | null;
|
||||
images: { width: number | null; height: number | null }[];
|
||||
}
|
||||
|
||||
export function requestPhase(request: ModelRequest | string): string {
|
||||
const phase = typeof request === 'string' ? request : request.phase;
|
||||
return ({ waiting_response: '等待模型响应', waiting_output: '等待模型输出', receiving_output: '正在接收模型输出', generating_tool_arguments: '正在生成工具参数', retry_wait: '等待重试', completed: '请求完成', failed: '请求失败', cancelled: '请求已取消' } as Record<string, string>)[phase] || '请求状态待核实';
|
||||
}
|
||||
|
||||
export function requestHint(request: ModelRequest, now: number): string {
|
||||
if (request.phase === 'retry_wait') {
|
||||
const remaining = Math.max(0, Math.ceil((request.retry_at || now) - now));
|
||||
return remaining ? `${remaining} 秒后进行第 ${request.attempt}/${request.max_retries} 次重试。` : '重试等待已结束,正在等待下一次请求状态。';
|
||||
}
|
||||
if (request.phase === 'waiting_output') return '已收到服务响应,尚未收到有效模型输出。';
|
||||
if (request.phase === 'generating_tool_arguments') {
|
||||
const names = (request.tools || []).map(tool => tool.name).filter(Boolean).join('、');
|
||||
return `${names ? names + ' · ' : ''}已接收 ${(request.tool_argument_chars || 0).toLocaleString()} 个参数字符,最近输出在 ${Math.max(0, Math.floor(now - (request.last_output_at || now)))} 秒前;本次工具尚未执行。`;
|
||||
}
|
||||
if (request.phase === 'receiving_output') return `最近一次输出在 ${Math.max(0, Math.floor(now - (request.last_output_at || now)))} 秒前;完整响应仍在接收中。`;
|
||||
return '正在等待服务响应;尚不能确认模型是否开始生成。';
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-llm-request-status', standalone: true, imports: [DatePipe],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: './llm-request-status.component.html', styleUrl: './llm-request-status.component.scss'
|
||||
})
|
||||
export class LlmRequestStatusComponent {
|
||||
private http = inject(HttpClient);
|
||||
sessionId = input<string | null>(null);
|
||||
running = input(false);
|
||||
activeChange = output<boolean>();
|
||||
requests = signal<ModelRequest[]>([]);
|
||||
now = signal(Date.now() / 1000);
|
||||
unavailable = signal(false);
|
||||
loading = signal(false);
|
||||
active = computed(() => this.running() ? this.requests().filter(r => r.active) : []);
|
||||
downloadUrl = computed(() => `/api/sessions/${encodeURIComponent(this.sessionId() || '')}/request-diagnostics?download=true`);
|
||||
phase = requestPhase;
|
||||
hint = requestHint;
|
||||
|
||||
constructor() {
|
||||
effect(() => this.activeChange.emit(this.active().length > 0));
|
||||
effect(onCleanup => {
|
||||
const id = this.sessionId();
|
||||
const running = this.running();
|
||||
this.requests.set([]);
|
||||
this.unavailable.set(false);
|
||||
this.loading.set(!!id);
|
||||
if (!id) return;
|
||||
let stopped = false;
|
||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||
let subscription: Subscription | undefined;
|
||||
const schedule = () => { if (running && !stopped) timer = setTimeout(load, 3000); };
|
||||
const load = () => {
|
||||
subscription = this.http.get<{ requests: ModelRequest[] }>(`/api/sessions/${encodeURIComponent(id)}/request-diagnostics`).subscribe({
|
||||
next: data => {
|
||||
this.requests.set(data.requests || []);
|
||||
this.unavailable.set(false);
|
||||
this.loading.set(false);
|
||||
schedule();
|
||||
},
|
||||
error: () => { this.unavailable.set(true); this.loading.set(false); schedule(); }
|
||||
});
|
||||
};
|
||||
load();
|
||||
const clock = running ? setInterval(() => this.now.set(Date.now() / 1000), 1000) : undefined;
|
||||
onCleanup(() => { stopped = true; clearTimeout(timer); clearInterval(clock); subscription?.unsubscribe(); });
|
||||
});
|
||||
}
|
||||
|
||||
elapsed(request: ModelRequest): number {
|
||||
return Math.max(0, Math.floor(request.ended_at ? request.elapsed_seconds : this.now() - request.started_at));
|
||||
}
|
||||
|
||||
role(request: ModelRequest): string {
|
||||
return ({ FlashRunner: '任务执行', operator: '任务执行', planner: '规划', checker: '结果检查', outputter: '报告整理', step_summarizer: '步骤整理' } as Record<string, string>)[request.role] || request.role;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user