feat(diagnosis): integrate AI chat history/send and improve error details

pull/48/head
hnu202326010131 4 months ago
parent 0842169b91
commit 9b446db833

@ -86,24 +86,27 @@
<div class="layout__card-header"><h3 class="layout__card-title">对话历史</h3></div>
<div class="layout__card-body">
<div class="chat-history">
<div class="chat-item">
<div class="chat-role">系统</div>
<div class="chat-text">欢迎使用多智能体诊断面板</div>
</div>
<div class="chat-item">
<div class="chat-role">诊断智能体</div>
<div class="chat-text">请在左侧选择节点并拖入关键日志作为上下文</div>
<div class="chat-item" v-for="(m, i) in messages" :key="i">
<div class="chat-role">{{ roleLabel(m.role) }}</div>
<div class="chat-text">
<div>{{ m.content }}</div>
<details v-if="m.reasoning" class="u-mt-1">
<summary>推理过程</summary>
<pre style="white-space: pre-wrap">{{ m.reasoning }}</pre>
</details>
</div>
</div>
</div>
<textarea class="chat-input" placeholder="支持Markdown输入..."></textarea>
<textarea class="chat-input" v-model.trim="inputMsg" placeholder="支持Markdown输入..."></textarea>
<div class="chat-actions">
<button type="button" class="btn btn--primary"></button>
<button type="button" class="btn btn--primary u-ml-1">生成状态报</button>
<button type="button" class="btn btn--primary" :disabled="sending || !inputMsg" @click="send()"></button>
<button type="button" class="btn btn--primary u-ml-1" :disabled="sending" @click="generateReport()"></button>
</div>
<div class="chat-progress">
<span>流式显示占位</span>
<div class="progress-bar"><div class="progress-fill" style="width:0%"></div></div>
<span>{{ sending ? '正在生成回复...' : '就绪' }}</span>
<div class="progress-bar"><div class="progress-fill" :style="{ width: sending ? '60%' : '0%' }"></div></div>
</div>
<div class="u-text-sm u-text-gray-700 u-mt-1">{{ err }}</div>
</div>
</article>
</aside>
@ -112,7 +115,9 @@
</template>
<script setup lang="ts">
import { reactive, ref, computed } from 'vue'
import { reactive, ref, computed, watch, onMounted } from 'vue'
import api from '../lib/api'
import { useAuthStore } from '../stores/auth'
const kw = ref('')
const tab = ref<'live'|'auto'>('live')
const agent = ref('诊断智能体')
@ -138,6 +143,65 @@ const previewLogs = computed(() => {
{ id:3, time:'2025-11-07T10:14:55', level:'info', source:selectedNode.value, message:'尝试重连中' }
]
})
const auth = useAuthStore()
const messages = ref<Array<{ role: 'user'|'assistant'|'system'; content: string; reasoning?: string }>>([
{ role: 'system', content: '欢迎使用多智能体诊断面板' },
{ role: 'assistant', content: '请在左侧选择节点并拖入关键日志作为上下文' }
])
const inputMsg = ref('')
const sending = ref(false)
const err = ref('')
function sessionIdOf(){ return selectedNode.value ? `diagnosis-${selectedNode.value}` : 'diagnosis-global' }
function roleLabel(r: string){ return r==='assistant' ? '诊断智能体' : r==='user' ? '我' : '系统' }
async function loadHistory(){
err.value = ''
try{
const r = await api.get('/v1/ai/history', { params: { sessionId: sessionIdOf() }, headers: auth.token ? { Authorization: `Bearer ${auth.token}` } : undefined })
const list = Array.isArray(r.data?.messages) ? r.data.messages : []
messages.value = list.map((m:any)=>({ role: m.role || 'assistant', content: String(m.content || ''), reasoning: m.reasoning }))
}catch(e:any){
err.value = formatError(e, '历史记录加载失败')
}
}
async function send(){
if (!inputMsg.value) return
sending.value = true
err.value = ''
const userMsg = { role: 'user' as const, content: inputMsg.value }
messages.value.push(userMsg)
try{
const r = await api.post('/v1/ai/chat', { sessionId: sessionIdOf(), message: inputMsg.value, context: { node: selectedNode.value || '', agent: agent.value, model: model.value } }, { headers: auth.token ? { Authorization: `Bearer ${auth.token}` } : undefined })
const reply = String(r.data?.reply || '')
const reasoning = r.data?.reasoning
messages.value.push({ role: 'assistant', content: reply, reasoning })
inputMsg.value = ''
}catch(e:any){
err.value = formatError(e, '消息发送失败')
}finally{
sending.value = false
}
}
async function generateReport(){
inputMsg.value = inputMsg.value || `请根据当前节点${selectedNode.value || '(未选定)'}最近关键日志生成一份状态报告(包含症状、影响范围、根因假设与建议)。`
await send()
}
onMounted(()=>{ loadHistory() })
watch(selectedNode, () => { loadHistory() })
function formatError(e:any, def:string){
const r = e?.response
const s = r?.status
const st = r?.statusText
const d = r?.data
const detail = typeof d?.detail === 'string' ? d.detail : ''
const errs = Array.isArray(d?.detail?.errors) ? d.detail.errors : []
const msgs: string[] = []
if (s) msgs.push(`HTTP ${s}${st ? ' '+st : ''}`)
if (detail) msgs.push(detail)
if (errs.length) msgs.push(errs.map((x:any)=>x?.message||'').filter(Boolean).join(''))
if (!msgs.length) msgs.push(r ? def : '网络异常或后端不可用')
if (s === 401) msgs.push('Token 已过期或未登录,请重新登录')
return msgs.join(' | ')
}
</script>
<style scoped>

Loading…
Cancel
Save