实现Debug EML实时Trace调试链路
This commit is contained in:
@@ -14,13 +14,13 @@
|
||||
<div>
|
||||
<dt>{{ t('debugEml.debugRunId') }}</dt>
|
||||
<dd class="th-code">
|
||||
{{ result?.debug_run_id ?? '-' }}
|
||||
{{ currentDebugRunId ?? '-' }}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{{ t('debugEml.backendStatus') }}</dt>
|
||||
<dd class="th-code">
|
||||
{{ result?.status ?? '-' }}
|
||||
{{ currentBackendStatus ?? '-' }}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
@@ -127,13 +127,13 @@
|
||||
<div>
|
||||
<dt>{{ t('debugEml.debugRunId') }}</dt>
|
||||
<dd class="th-code">
|
||||
{{ result?.debug_run_id ?? '-' }}
|
||||
{{ currentDebugRunId ?? '-' }}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{{ t('debugEml.backendStatus') }}</dt>
|
||||
<dd class="th-code">
|
||||
{{ result?.status ?? '-' }}
|
||||
{{ currentBackendStatus ?? '-' }}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
@@ -157,6 +157,95 @@
|
||||
</aside>
|
||||
|
||||
<main class="debug-eml-results">
|
||||
<section
|
||||
v-if="stageEvents.length || busy"
|
||||
class="debug-eml-card debug-eml-stream-section"
|
||||
>
|
||||
<div class="th-section-header">
|
||||
<h2 class="th-section-title">
|
||||
<i
|
||||
class="pi pi-list-check"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{ t('debugEml.systemStages') }}
|
||||
</h2>
|
||||
</div>
|
||||
<ol
|
||||
v-if="stageEvents.length"
|
||||
class="debug-eml-event-list debug-eml-event-list--stages"
|
||||
>
|
||||
<li
|
||||
v-for="(stage, index) in stageEvents"
|
||||
:key="`${stage.status ?? 'stage'}-${index}`"
|
||||
>
|
||||
<strong>{{ stage.status ?? '-' }}</strong>
|
||||
<span>{{ stage.safe_summary ?? '-' }}</span>
|
||||
<small class="th-code">{{ stage.debug_run_id ?? '-' }}</small>
|
||||
</li>
|
||||
</ol>
|
||||
<div
|
||||
v-else
|
||||
class="empty-state empty-state--compact"
|
||||
>
|
||||
{{ t('debugEml.waitingSuperAgent') }}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
v-if="visibleTraceEvents.length || busy"
|
||||
class="debug-eml-card debug-eml-stream-section"
|
||||
>
|
||||
<div class="th-section-header">
|
||||
<h2 class="th-section-title">
|
||||
<i
|
||||
class="pi pi-wave-pulse"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{ t('debugEml.superAgentTrace') }}
|
||||
</h2>
|
||||
</div>
|
||||
<ol
|
||||
v-if="visibleTraceEvents.length"
|
||||
class="debug-eml-event-list debug-eml-event-list--trace"
|
||||
>
|
||||
<li
|
||||
v-for="(trace, index) in visibleTraceEvents"
|
||||
:key="`${trace.event ?? 'trace'}-${trace.tool_call_id ?? index}`"
|
||||
>
|
||||
<strong>{{ trace.event ?? '-' }}</strong>
|
||||
<span v-if="trace.text">{{ trace.text }}</span>
|
||||
<dl class="debug-eml-trace-fields">
|
||||
<div v-if="trace.tool_name">
|
||||
<dt>{{ t('debugEml.traceToolName') }}</dt>
|
||||
<dd class="th-code">{{ trace.tool_name }}</dd>
|
||||
</div>
|
||||
<div v-if="trace.input_summary">
|
||||
<dt>{{ t('debugEml.traceInputSummary') }}</dt>
|
||||
<dd class="th-code">{{ trace.input_summary }}</dd>
|
||||
</div>
|
||||
<div v-if="trace.output_summary">
|
||||
<dt>{{ t('debugEml.traceOutputSummary') }}</dt>
|
||||
<dd class="th-code">{{ trace.output_summary }}</dd>
|
||||
</div>
|
||||
<div v-if="trace.status">
|
||||
<dt>{{ t('debugEml.traceStatus') }}</dt>
|
||||
<dd class="th-code">{{ trace.status }}</dd>
|
||||
</div>
|
||||
<div v-if="trace.ts">
|
||||
<dt>{{ t('debugEml.traceTime') }}</dt>
|
||||
<dd class="th-code">{{ trace.ts }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</li>
|
||||
</ol>
|
||||
<div
|
||||
v-else
|
||||
class="empty-state empty-state--compact"
|
||||
>
|
||||
{{ t('common.noData') }}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<template v-if="result">
|
||||
<section class="debug-eml-card">
|
||||
<div class="th-section-header">
|
||||
@@ -341,8 +430,13 @@ import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { reservationHotelId } from '@/config/reservationConfig'
|
||||
import { DebugEmlUploadError, uploadDebugEmlSuperAgentRun } from '@/services/debugEmlService'
|
||||
import type { DebugEmlErrorCode, DebugEmlSuperAgentRunResult } from '@/types/debugEml'
|
||||
import { DebugEmlUploadError, uploadDebugEmlSuperAgentRunStream } from '@/services/debugEmlService'
|
||||
import type {
|
||||
DebugEmlErrorCode,
|
||||
DebugEmlStageEvent,
|
||||
DebugEmlSuperAgentRunResult,
|
||||
DebugEmlSuperAgentTraceEvent,
|
||||
} from '@/types/debugEml'
|
||||
|
||||
type DebugEmlStatus = 'idle' | 'uploading' | 'succeeded' | 'failed'
|
||||
|
||||
@@ -354,6 +448,8 @@ const runLabel = ref('')
|
||||
const selectedFile = ref<File | null>(null)
|
||||
const status = ref<DebugEmlStatus>('idle')
|
||||
const result = ref<DebugEmlSuperAgentRunResult | null>(null)
|
||||
const stageEvents = ref<DebugEmlStageEvent[]>([])
|
||||
const traceEvents = ref<DebugEmlSuperAgentTraceEvent[]>([])
|
||||
const errorCode = ref<DebugEmlErrorCode | ''>('')
|
||||
const errorMessage = ref('')
|
||||
|
||||
@@ -362,6 +458,16 @@ const submitDisabled = computed(
|
||||
() => busy.value || !debugUploadKey.value.trim() || !selectedFile.value,
|
||||
)
|
||||
const statusLabel = computed(() => t(`debugEml.statuses.${status.value}`))
|
||||
const latestStageEvent = computed(() => stageEvents.value[stageEvents.value.length - 1] ?? null)
|
||||
const currentDebugRunId = computed(
|
||||
() => result.value?.debug_run_id || latestStageEvent.value?.debug_run_id || null,
|
||||
)
|
||||
const currentBackendStatus = computed(
|
||||
() => result.value?.status || latestStageEvent.value?.status || null,
|
||||
)
|
||||
const visibleTraceEvents = computed(() =>
|
||||
traceEvents.value.length ? traceEvents.value : result.value?.superagent_trace_events ?? [],
|
||||
)
|
||||
const errorDisplayMessage = computed(() => {
|
||||
if (!errorCode.value) {
|
||||
return ''
|
||||
@@ -394,14 +500,33 @@ async function submitUpload(): Promise<void> {
|
||||
errorCode.value = ''
|
||||
errorMessage.value = ''
|
||||
result.value = null
|
||||
stageEvents.value = []
|
||||
traceEvents.value = []
|
||||
|
||||
try {
|
||||
result.value = await uploadDebugEmlSuperAgentRun({
|
||||
hotelId: hotelId.value,
|
||||
debugUploadKey: debugUploadKey.value.trim(),
|
||||
runLabel: runLabel.value,
|
||||
file: selectedFile.value,
|
||||
})
|
||||
const finalResult = await uploadDebugEmlSuperAgentRunStream(
|
||||
{
|
||||
hotelId: hotelId.value,
|
||||
debugUploadKey: debugUploadKey.value.trim(),
|
||||
runLabel: runLabel.value,
|
||||
file: selectedFile.value,
|
||||
},
|
||||
{
|
||||
onStage: (stage) => {
|
||||
stageEvents.value = [...stageEvents.value, stage]
|
||||
},
|
||||
onTrace: (trace) => {
|
||||
traceEvents.value = [...traceEvents.value, trace]
|
||||
},
|
||||
onResult: (streamResult) => {
|
||||
result.value = streamResult
|
||||
},
|
||||
},
|
||||
)
|
||||
result.value = finalResult
|
||||
if (!traceEvents.value.length && finalResult.superagent_trace_events.length) {
|
||||
traceEvents.value = finalResult.superagent_trace_events
|
||||
}
|
||||
status.value = 'succeeded'
|
||||
} catch (error) {
|
||||
status.value = 'failed'
|
||||
@@ -680,6 +805,11 @@ function formatMediaSize(sizeBytes: number | null): string {
|
||||
color: var(--th-color-slate-900);
|
||||
}
|
||||
|
||||
.empty-state--compact {
|
||||
min-height: 84px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.empty-state--error {
|
||||
place-items: start;
|
||||
color: var(--th-color-danger);
|
||||
@@ -732,6 +862,78 @@ function formatMediaSize(sizeBytes: number | null): string {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.debug-eml-stream-section {
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.debug-eml-event-list {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.debug-eml-event-list li {
|
||||
min-width: 0;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.25);
|
||||
border-radius: 10px;
|
||||
background: var(--th-color-slate-50);
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.debug-eml-event-list strong {
|
||||
color: var(--th-color-slate-900);
|
||||
font-size: 13px;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.debug-eml-event-list span,
|
||||
.debug-eml-event-list small {
|
||||
color: var(--th-color-slate-600);
|
||||
font-size: 12px;
|
||||
line-height: 1.55;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.debug-eml-event-list--stages li {
|
||||
grid-template-columns: minmax(150px, 210px) minmax(0, 1fr) minmax(100px, 160px);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.debug-eml-event-list--trace li {
|
||||
background: linear-gradient(180deg, var(--th-color-white), var(--th-color-slate-50));
|
||||
}
|
||||
|
||||
.debug-eml-trace-fields {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.debug-eml-trace-fields div {
|
||||
min-width: 0;
|
||||
border-radius: 8px;
|
||||
background: rgb(255 255 255 / 76%);
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.debug-eml-trace-fields dt {
|
||||
color: var(--th-color-slate-500);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.debug-eml-trace-fields dd {
|
||||
margin: 4px 0 0;
|
||||
color: var(--th-color-slate-900);
|
||||
font-size: 12px;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.debug-eml-html-preview {
|
||||
max-height: 520px;
|
||||
overflow: auto;
|
||||
@@ -886,6 +1088,8 @@ pre {
|
||||
.debug-eml-kv,
|
||||
.debug-eml-kv--grid,
|
||||
.debug-eml-two-column,
|
||||
.debug-eml-event-list--stages li,
|
||||
.debug-eml-trace-fields,
|
||||
.debug-eml-media-list li {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user