mirror of
https://github.com/OpenSquawk/OpenSquawk
synced 2026-08-05 17:05:53 +08:00
refactor(live-atc): drop unreachable decision-trace normalization
normalizeDecisionTraceResult had no callers — it was the only root of ensureTraceCalls, normalizeTraceFallback/AutoSelection, the whole normalizeTimeline* chain, VALID_TRACE_STAGES and isPlainObject, so that entire block was unreachable. Same for activeFlowInfo. Leftovers from the LLM decision routing that moved to the Python backend (see CLAUDE.md): the trace the debug panel renders now comes straight from the engine's lastDecisionTrace, already shaped. cloneForTrace stays — runFullSimulation still uses it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1532,14 +1532,6 @@ import { useBugReport } from '~/composables/useBugReport'
|
||||
import { useSimBridgeSync } from '~/composables/useSimBridgeSync'
|
||||
import { useSpeechInterrupt } from '~/composables/useSpeechInterrupt'
|
||||
import { useRadioSpeech } from '~/composables/useRadioSpeech'
|
||||
import type {
|
||||
CandidateTraceElimination,
|
||||
CandidateTraceEntry,
|
||||
CandidateTraceStage,
|
||||
CandidateTraceStep,
|
||||
DecisionCandidateTimeline,
|
||||
LLMDecisionTrace,
|
||||
} from '../../shared/types/llm'
|
||||
|
||||
// Core State
|
||||
const engine = useCommunicationsEngine()
|
||||
@@ -1747,18 +1739,6 @@ const clearLog = () => {
|
||||
clearLastTransmission()
|
||||
}
|
||||
|
||||
const activeFlowInfo = computed(() => {
|
||||
const slug = activeFlow.value
|
||||
const flows = availableFlows.value
|
||||
const entry = (slug ? flows.find((flow) => flow.slug === slug) : undefined) || flows.find((flow) => flow.mode === 'main') || flows[0]
|
||||
const resolvedSlug = entry?.slug || slug || ''
|
||||
const name = entry?.name || resolvedSlug || 'Main Flow'
|
||||
const description = entry?.description || ''
|
||||
const mode = entry?.mode || (resolvedSlug && resolvedSlug === slug ? 'parallel' : 'parallel')
|
||||
const modeLabel = mode === 'main' ? 'Main' : mode === 'linear' ? 'Linear' : 'Parallel'
|
||||
return { slug: resolvedSlug, name, description, mode, modeLabel }
|
||||
})
|
||||
|
||||
const decisionTrace = computed(() => lastDecisionTrace.value)
|
||||
const timelineSteps = computed(() => decisionTrace.value?.candidateTimeline?.steps ?? [])
|
||||
const timelineUsedFallback = computed(() => Boolean(decisionTrace.value?.candidateTimeline?.fallbackUsed))
|
||||
@@ -1766,17 +1746,6 @@ const traceAutoSelection = computed(() => decisionTrace.value?.autoSelection ??
|
||||
const traceFallback = computed(() => decisionTrace.value?.fallback ?? null)
|
||||
const sessionLabel = computed(() => engineSessionId.value || flags.value.session_id || '-')
|
||||
|
||||
const VALID_TRACE_STAGES: ReadonlySet<CandidateTraceStage> = new Set(
|
||||
[
|
||||
'regex_candidates',
|
||||
'regex_filtered',
|
||||
'condition_filtered',
|
||||
'fallback_candidates',
|
||||
'fallback_filtered',
|
||||
'final'
|
||||
] as CandidateTraceStage[]
|
||||
)
|
||||
|
||||
const cloneForTrace = <T>(value: T): T => {
|
||||
if (value === undefined || value === null) {
|
||||
return value
|
||||
@@ -1789,220 +1758,6 @@ const cloneForTrace = <T>(value: T): T => {
|
||||
}
|
||||
}
|
||||
|
||||
const isPlainObject = (value: unknown): value is Record<string, any> => {
|
||||
return typeof value === 'object' && value !== null
|
||||
}
|
||||
|
||||
const ensureTraceCalls = (calls: unknown): LLMDecisionTrace['calls'] => {
|
||||
if (!Array.isArray(calls)) {
|
||||
return []
|
||||
}
|
||||
return calls
|
||||
.filter((entry): entry is Record<string, any> => isPlainObject(entry))
|
||||
.map((entry) => {
|
||||
const normalized: LLMDecisionTrace['calls'][number] = {
|
||||
stage: entry.stage === 'readback-check' ? 'readback-check' : 'decision',
|
||||
request: isPlainObject(entry.request) ? cloneForTrace(entry.request) : {},
|
||||
}
|
||||
if ('response' in entry) {
|
||||
normalized.response = cloneForTrace(entry.response)
|
||||
}
|
||||
if (typeof entry.rawResponseText === 'string') {
|
||||
normalized.rawResponseText = entry.rawResponseText
|
||||
}
|
||||
if (typeof entry.error === 'string') {
|
||||
normalized.error = entry.error
|
||||
}
|
||||
return normalized
|
||||
})
|
||||
}
|
||||
|
||||
const normalizeTraceFallback = (raw: unknown): LLMDecisionTrace['fallback'] | undefined => {
|
||||
if (!isPlainObject(raw)) {
|
||||
return undefined
|
||||
}
|
||||
const fallback: LLMDecisionTrace['fallback'] = {
|
||||
used: Boolean(raw.used),
|
||||
}
|
||||
if (typeof raw.reason === 'string' && raw.reason.trim().length) {
|
||||
fallback.reason = raw.reason
|
||||
}
|
||||
if (typeof raw.selected === 'string' && raw.selected.trim().length) {
|
||||
fallback.selected = raw.selected
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
const normalizeTraceAutoSelection = (
|
||||
raw: unknown
|
||||
): NonNullable<LLMDecisionTrace['autoSelection']> | undefined => {
|
||||
if (!isPlainObject(raw)) {
|
||||
return undefined
|
||||
}
|
||||
const id = typeof raw.id === 'string' && raw.id.trim().length ? raw.id : undefined
|
||||
if (!id) {
|
||||
return undefined
|
||||
}
|
||||
const flow = typeof raw.flow === 'string' && raw.flow.trim().length ? raw.flow : 'current'
|
||||
const autoSelection: NonNullable<LLMDecisionTrace['autoSelection']> = { id, flow }
|
||||
if (typeof raw.reason === 'string' && raw.reason.trim().length) {
|
||||
autoSelection.reason = raw.reason
|
||||
}
|
||||
return autoSelection
|
||||
}
|
||||
|
||||
const normalizeTimelineCandidate = (raw: unknown): CandidateTraceEntry | null => {
|
||||
if (!isPlainObject(raw)) {
|
||||
return null
|
||||
}
|
||||
const id = typeof raw.id === 'string' && raw.id.trim().length ? raw.id : undefined
|
||||
if (!id) {
|
||||
return null
|
||||
}
|
||||
const flow = typeof raw.flow === 'string' && raw.flow.trim().length
|
||||
? raw.flow
|
||||
: typeof raw.flow?.slug === 'string'
|
||||
? raw.flow.slug
|
||||
: 'current'
|
||||
const candidate: CandidateTraceEntry = {
|
||||
id,
|
||||
flow,
|
||||
}
|
||||
if (typeof raw.name === 'string') {
|
||||
candidate.name = raw.name
|
||||
}
|
||||
if (typeof raw.summary === 'string') {
|
||||
candidate.summary = raw.summary
|
||||
}
|
||||
if (typeof raw.role === 'string') {
|
||||
candidate.role = raw.role
|
||||
}
|
||||
if (Array.isArray(raw.triggers)) {
|
||||
candidate.triggers = cloneForTrace(raw.triggers)
|
||||
}
|
||||
if (Array.isArray(raw.conditions)) {
|
||||
candidate.conditions = cloneForTrace(raw.conditions)
|
||||
}
|
||||
return candidate
|
||||
}
|
||||
|
||||
const normalizeTimelineElimination = (raw: unknown): CandidateTraceElimination | null => {
|
||||
if (!isPlainObject(raw)) {
|
||||
return null
|
||||
}
|
||||
const candidate = normalizeTimelineCandidate(raw.candidate)
|
||||
if (!candidate) {
|
||||
return null
|
||||
}
|
||||
const kind: CandidateTraceElimination['kind'] = raw.kind === 'regex' ? 'regex' : 'condition'
|
||||
const reason = typeof raw.reason === 'string' && raw.reason.trim().length ? raw.reason : ''
|
||||
const elimination: CandidateTraceElimination = {
|
||||
candidate,
|
||||
kind,
|
||||
reason,
|
||||
}
|
||||
if (isPlainObject(raw.context)) {
|
||||
elimination.context = cloneForTrace(raw.context)
|
||||
}
|
||||
return elimination
|
||||
}
|
||||
|
||||
const normalizeTimelineStep = (raw: unknown): CandidateTraceStep | null => {
|
||||
if (!isPlainObject(raw)) {
|
||||
return null
|
||||
}
|
||||
const stageCandidate = typeof raw.stage === 'string' && VALID_TRACE_STAGES.has(raw.stage as CandidateTraceStage)
|
||||
? (raw.stage as CandidateTraceStage)
|
||||
: 'final'
|
||||
const label = typeof raw.label === 'string' && raw.label.trim().length ? raw.label : stageCandidate
|
||||
const candidates = Array.isArray(raw.candidates)
|
||||
? raw.candidates
|
||||
.map((candidate) => normalizeTimelineCandidate(candidate))
|
||||
.filter((candidate): candidate is CandidateTraceEntry => Boolean(candidate))
|
||||
: []
|
||||
const eliminated = Array.isArray(raw.eliminated)
|
||||
? raw.eliminated
|
||||
.map((entry) => normalizeTimelineElimination(entry))
|
||||
.filter((entry): entry is CandidateTraceElimination => Boolean(entry))
|
||||
: []
|
||||
const note = typeof raw.note === 'string' && raw.note.trim().length ? raw.note : undefined
|
||||
const step: CandidateTraceStep = {
|
||||
stage: stageCandidate,
|
||||
label,
|
||||
candidates,
|
||||
}
|
||||
if (eliminated.length) {
|
||||
step.eliminated = eliminated
|
||||
}
|
||||
if (note) {
|
||||
step.note = note
|
||||
}
|
||||
return step
|
||||
}
|
||||
|
||||
const normalizeCandidateTimeline = (raw: unknown): DecisionCandidateTimeline | undefined => {
|
||||
if (!isPlainObject(raw)) {
|
||||
return undefined
|
||||
}
|
||||
const steps = Array.isArray(raw.steps)
|
||||
? raw.steps
|
||||
.map((step) => normalizeTimelineStep(step))
|
||||
.filter((step): step is CandidateTraceStep => Boolean(step))
|
||||
: []
|
||||
const timeline: DecisionCandidateTimeline = { steps }
|
||||
if ('fallbackUsed' in raw) {
|
||||
timeline.fallbackUsed = Boolean((raw as any).fallbackUsed)
|
||||
} else if ('fallback_used' in raw) {
|
||||
timeline.fallbackUsed = Boolean((raw as any).fallback_used)
|
||||
}
|
||||
const autoSelected = normalizeTimelineCandidate((raw as any).autoSelected ?? (raw as any).auto_selected)
|
||||
if (autoSelected !== null && autoSelected !== undefined) {
|
||||
timeline.autoSelected = autoSelected
|
||||
}
|
||||
return timeline
|
||||
}
|
||||
|
||||
const normalizeDecisionTraceResult = (result: any): LLMDecisionTrace | null => {
|
||||
const traceSource = isPlainObject(result?.trace) ? result.trace : undefined
|
||||
const fallbackFromTrace = normalizeTraceFallback(traceSource?.fallback)
|
||||
const timelineFromTrace = normalizeCandidateTimeline(traceSource?.candidateTimeline ?? traceSource?.timeline)
|
||||
const autoSelectionFromTrace = normalizeTraceAutoSelection(traceSource?.autoSelection ?? traceSource?.auto_selection)
|
||||
|
||||
const baseTrace: LLMDecisionTrace | null = traceSource
|
||||
? {
|
||||
calls: ensureTraceCalls(traceSource.calls),
|
||||
...(fallbackFromTrace ? { fallback: fallbackFromTrace } : {}),
|
||||
...(timelineFromTrace ? { candidateTimeline: timelineFromTrace } : {}),
|
||||
...(autoSelectionFromTrace ? { autoSelection: autoSelectionFromTrace } : {}),
|
||||
}
|
||||
: null
|
||||
|
||||
const candidateTimeline = timelineFromTrace
|
||||
?? normalizeCandidateTimeline(result?.candidateTimeline ?? result?.candidate_timeline ?? result?.timeline)
|
||||
const autoSelection = autoSelectionFromTrace
|
||||
?? normalizeTraceAutoSelection(result?.autoSelection ?? result?.auto_selection)
|
||||
const fallback = fallbackFromTrace
|
||||
?? normalizeTraceFallback(result?.fallback ?? result?.fallbackInfo ?? result?.fallback_info)
|
||||
|
||||
if (!baseTrace && !candidateTimeline && !autoSelection && !fallback) {
|
||||
return null
|
||||
}
|
||||
|
||||
const trace: LLMDecisionTrace = baseTrace ?? { calls: [] }
|
||||
|
||||
if (candidateTimeline && !trace.candidateTimeline) {
|
||||
trace.candidateTimeline = candidateTimeline
|
||||
}
|
||||
if (autoSelection && !trace.autoSelection) {
|
||||
trace.autoSelection = autoSelection
|
||||
}
|
||||
if (fallback && !trace.fallback) {
|
||||
trace.fallback = fallback
|
||||
}
|
||||
|
||||
return trace
|
||||
}
|
||||
|
||||
function describeElimination(entry: any): string {
|
||||
if (!entry || typeof entry !== 'object') {
|
||||
return ''
|
||||
|
||||
Reference in New Issue
Block a user