fix(workbench): remove frontend trace timing compensation
This commit is contained in:
@@ -7,7 +7,7 @@ import { useBottomFollowScroll } from "@/composables/useBottomFollowScroll";
|
||||
import { formatDisplayClock } from "@/config/runtime";
|
||||
import { traceDisplayRows, type TraceEventRow } from "../../../../../tools/src/hwlab-cli/trace-renderer.ts";
|
||||
|
||||
const props = defineProps<{ trace?: RunnerTrace | null; defaultExpanded?: boolean; storageKey?: string; autoExpanded?: boolean | null; hideSealedFinalResponse?: boolean }>();
|
||||
const props = defineProps<{ trace?: RunnerTrace | null; defaultExpanded?: boolean; storageKey?: string; autoExpanded?: boolean | null }>();
|
||||
|
||||
const listRef = ref<HTMLOListElement | null>(null);
|
||||
const expanded = ref(false);
|
||||
@@ -16,7 +16,7 @@ const { following, keepBottomAfterUpdate, onScroll, scrollToBottom } = useBottom
|
||||
const events = computed(() => Array.isArray(props.trace?.events) ? props.trace.events : []);
|
||||
const eventCount = computed(() => props.trace?.eventCount ?? events.value.length);
|
||||
const rawReadableRows = computed(() => traceDisplayRows(traceRecord(props.trace), events.value as Record<string, unknown>[], { formatClock: formatDisplayClock }));
|
||||
const readableRows = computed(() => filterReadableRows(rawReadableRows.value, props.hideSealedFinalResponse === true));
|
||||
const readableRows = computed(() => rawReadableRows.value);
|
||||
const projectionDiagnosticLabel = computed(() => {
|
||||
const projection = props.trace?.projection ?? null;
|
||||
const health = projection?.projectionHealth ?? props.trace?.projectionHealth;
|
||||
@@ -83,18 +83,6 @@ function rowIsTool(row: TraceEventRow): boolean {
|
||||
return row.rowId.startsWith("tool:") || /commandExecution/u.test(row.header);
|
||||
}
|
||||
|
||||
function filterReadableRows(rows: TraceEventRow[], hideSealedFinalResponse: boolean): TraceEventRow[] {
|
||||
if (!hideSealedFinalResponse) return rows;
|
||||
return rows.filter((row) => !traceRowDuplicatesSealedFinal(row));
|
||||
}
|
||||
|
||||
function traceRowDuplicatesSealedFinal(row: TraceEventRow): boolean {
|
||||
if (rowIsTool(row)) return false;
|
||||
if (row.rowId.startsWith("trace-final-response:")) return true;
|
||||
const rowText = [row.rowId, row.header, row.body, row.preview].map((value) => String(value ?? "")).join("\n");
|
||||
return /(?:助手(?:最终)?消息|assistant\s+(?:final\s+)?message)/iu.test(rowText);
|
||||
}
|
||||
|
||||
function toolCommandPreview(row: TraceEventRow): string | null {
|
||||
if (!rowIsTool(row)) return null;
|
||||
const preview = singleLinePreview(row.preview);
|
||||
|
||||
@@ -247,7 +247,7 @@ function traceForDisplay(message: ChatMessage): ChatMessage["runnerTrace"] {
|
||||
function messageDurationMeta(message: ChatMessage): { text: string; label: string } | null {
|
||||
if (message.role !== "agent") return null;
|
||||
const timing = messageTimingForDisplay(message);
|
||||
const durationMs = isRunningMessage(message) ? durationSince(timing?.startedAt) : terminalMessageDurationMs(message, timing);
|
||||
const durationMs = isRunningMessage(message) ? durationSince(timing?.startedAt) : terminalMessageDurationMs(timing);
|
||||
if (durationMs === null) return null;
|
||||
const value = formatDuration(durationMs);
|
||||
return { text: `耗时 ${value}`, label: `总耗时:${value}` };
|
||||
@@ -270,33 +270,8 @@ function messageTimingForDisplay(message: ChatMessage): ChatMessage["timing"] {
|
||||
return message.timing ?? null;
|
||||
}
|
||||
|
||||
function terminalMessageDurationMs(message: ChatMessage, timing: ChatMessage["timing"]): number | null {
|
||||
const elapsed = elapsedTerminalDurationMs(message, timing);
|
||||
if (elapsed !== null && elapsed > 0) return ceilDisplayDurationSecond(elapsed);
|
||||
const recorded = sealedTerminalRecordedDurationMs(message, timing);
|
||||
if (recorded !== null) return ceilDisplayDurationSecond(recorded);
|
||||
return null;
|
||||
}
|
||||
|
||||
function ceilDisplayDurationSecond(ms: number): number {
|
||||
if (!Number.isFinite(ms) || ms <= 0) return ms;
|
||||
return Math.ceil(ms / 1000) * 1000;
|
||||
}
|
||||
|
||||
function sealedTerminalRecordedDurationMs(message: ChatMessage, timing: ChatMessage["timing"]): number | null {
|
||||
void message;
|
||||
const messageHasTerminalTimestamp = timestampMs(timing?.finishedAt) !== null;
|
||||
return maxPositiveDurationMs(
|
||||
messageHasTerminalTimestamp ? timing?.durationMs : null,
|
||||
);
|
||||
}
|
||||
|
||||
function elapsedTerminalDurationMs(message: ChatMessage, timing: ChatMessage["timing"]): number | null {
|
||||
void message;
|
||||
const startedAt = timestampMs(timing?.startedAt);
|
||||
const endedAt = timestampMs(timing?.finishedAt);
|
||||
if (startedAt === null || endedAt === null || endedAt < startedAt) return null;
|
||||
return Math.trunc(endedAt - startedAt);
|
||||
function terminalMessageDurationMs(timing: ChatMessage["timing"]): number | null {
|
||||
return finiteDurationMs(timing?.durationMs);
|
||||
}
|
||||
|
||||
function durationSince(timestamp: unknown): number | null {
|
||||
@@ -317,15 +292,6 @@ function finiteDurationMs(value: unknown): number | null {
|
||||
return Number.isFinite(number) && number >= 0 ? Math.trunc(number) : null;
|
||||
}
|
||||
|
||||
function maxPositiveDurationMs(...values: unknown[]): number | null {
|
||||
let maximum: number | null = null;
|
||||
for (const value of values) {
|
||||
const duration = finiteDurationMs(value);
|
||||
if (duration !== null && duration > 0) maximum = maximum === null ? duration : Math.max(maximum, duration);
|
||||
}
|
||||
return maximum;
|
||||
}
|
||||
|
||||
function formatDuration(ms: number): string {
|
||||
const seconds = ms > 0 && ms < 1000 ? 1 : Math.max(0, Math.floor(ms / 1000));
|
||||
if (seconds < 60) return `${seconds} 秒`;
|
||||
@@ -364,7 +330,7 @@ function formatDuration(ms: number): string {
|
||||
<button v-if="message.role === 'agent'" class="message-detail-button" type="button" aria-label="运行详情" title="运行详情" @click="openDetails(message)">!</button>
|
||||
</div>
|
||||
</header>
|
||||
<TraceTimeline v-if="message.role === 'agent' && message.runnerTrace" :trace="traceForDisplay(message)" :auto-expanded="traceAutoExpanded(message)" :storage-key="traceStorageKey(message)" :hide-sealed-final-response="hasSealedCompletedText(message)" />
|
||||
<TraceTimeline v-if="message.role === 'agent' && message.runnerTrace" :trace="traceForDisplay(message)" :auto-expanded="traceAutoExpanded(message)" :storage-key="traceStorageKey(message)" />
|
||||
<LoadingState v-if="isAwaitingAgentBody(message)" class="message-loading" label="思考中..." compact />
|
||||
<MessageMarkdown v-if="showMessageText(message)" class="message-text" :source="visibleMessageText(message)" />
|
||||
<ApiErrorDiagnostic v-if="messageHasDiagnostic(message)" class="message-diagnostic projection-diagnostic" :error="messageDiagnosticText(message)" :api-error="messageApiError(message)" :diagnostic="messageErrorDiagnostic(message)" compact />
|
||||
|
||||
Reference in New Issue
Block a user