Files
AlphaPulse/web/src/pages/trading/components/AnalysisDrawer.vue
T
eezd 78ca4422af Add demo trading analyst with plan validation and sizing
The analyst prompts the model with trading rules, published skills,
account sizing inputs and prefetched market data, exposes read-only
tools plus submit_trade_plan, and validates and sizes the submitted plan
in pure functions covered by pytest. Manual analyses run as dry runs
while trading is stopped; the trading page shows the latest plan and an
analysis detail drawer.
2026-10-15 10:40:29 +08:00

69 lines
2.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts" setup>
import type { AnalysisDetail } from "@/apis/trading"
import { getAnalysis } from "@/apis/trading"
import { renderMarkdown } from "@/utils/markdown"
import { formatTime } from "@/utils/text"
const props = defineProps<{ analysisId: number | null }>()
const visible = defineModel<boolean>({ required: true })
const detail = ref<AnalysisDetail>()
watch(() => [visible.value, props.analysisId] as const, async ([open, id]) => {
detail.value = undefined
if (open && id)
detail.value = await getAnalysis(id)
})
const toolResults = computed(() => {
const results = new Map<string, string>()
for (const m of detail.value?.transcript ?? []) {
if (m.role === "tool")
results.set(m.tool_call_id, m.result)
}
return results
})
function pretty(raw: string | undefined) {
if (raw === undefined)
return "无结果"
try {
return JSON.stringify(JSON.parse(raw), null, 2)
} catch {
return raw
}
}
</script>
<template>
<el-drawer v-model="visible" :title="detail ? `分析 #${detail.id} · ${detail.symbol}` : '分析'" size="55%">
<div v-if="detail" class="flex flex-col gap-3 text-sm">
<div class="text-xs text-[var(--el-text-color-secondary)]">
{{ formatTime(detail.started_at) }} – {{ formatTime(detail.finished_at) }} · {{ detail.reasons.join(";") }}
· {{ detail.model }}{{ detail.reasoning_effort ? ` / ${detail.reasoning_effort}` : "" }}
· {{ detail.usage?.input_tokens.toLocaleString() }} / {{ detail.usage?.output_tokens.toLocaleString() }} token
{{ detail.dry_run ? "· 试运行" : "" }}
</div>
<el-alert v-if="detail.error" type="error" :closable="false" :title="detail.error" />
<!-- markdown-it 已关闭 html,模型输出中的标签按文本显示 -->
<div v-if="detail.text" class="markdown-body" v-html="renderMarkdown(detail.text)" />
<el-collapse>
<template v-for="(m, i) in detail.transcript ?? []" :key="i">
<el-collapse-item v-if="m.role === 'user'" :name="`u${i}`" :title="i === 0 ? '发给模型的数据(账户、持仓、预取行情)' : '程序提示'">
<pre class="m-0 max-h-120 overflow-auto whitespace-pre-wrap break-all text-xs">{{ m.text }}</pre>
</el-collapse-item>
<template v-else-if="m.role === 'assistant'">
<el-collapse-item v-for="call in m.tool_calls" :key="call.id" :name="call.id">
<template #title>
<span class="inline-block max-w-[90%] truncate text-xs font-mono">{{ call.name }} {{ JSON.stringify(call.arguments) }}</span>
</template>
<pre class="m-0 max-h-120 overflow-auto whitespace-pre-wrap break-all text-xs">{{ pretty(toolResults.get(call.id)) }}</pre>
</el-collapse-item>
</template>
</template>
</el-collapse>
</div>
<div v-else v-loading="true" class="h-40" />
</el-drawer>
</template>