feat: 接入 Agent 历史记录
- src/services/devopsApi.ts: 增加真实 Agent 调用历史列表接口 - src/components/GlobalAgentDrawer.vue: 抽屉打开时加载持久化历史,并展示用途、状态、问题和上下文摘要
This commit is contained in:
@@ -4,10 +4,10 @@
|
|||||||
*/
|
*/
|
||||||
import { Close, Promotion } from '@element-plus/icons-vue'
|
import { Close, Promotion } from '@element-plus/icons-vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
import { devopsApi } from '../services/devopsApi'
|
import { devopsApi } from '../services/devopsApi'
|
||||||
import { usePlatformStore } from '../stores/platform'
|
import { usePlatformStore } from '../stores/platform'
|
||||||
import type { AgentMode, AgentPurpose } from '../types/devops'
|
import type { AgentInvocation, AgentMode, AgentPurpose } from '../types/devops'
|
||||||
|
|
||||||
type AgentMessage = {
|
type AgentMessage = {
|
||||||
id: string
|
id: string
|
||||||
@@ -28,6 +28,7 @@ const selectedProject = ref('')
|
|||||||
const selectedRun = ref('')
|
const selectedRun = ref('')
|
||||||
const prompt = ref('帮我检查当前发布是否存在配置、构建或通知风险。')
|
const prompt = ref('帮我检查当前发布是否存在配置、构建或通知风险。')
|
||||||
const sending = ref(false)
|
const sending = ref(false)
|
||||||
|
const historyLoading = ref(false)
|
||||||
const messages = ref<AgentMessage[]>([
|
const messages = ref<AgentMessage[]>([
|
||||||
{
|
{
|
||||||
id: 'welcome',
|
id: 'welcome',
|
||||||
@@ -38,6 +39,14 @@ const messages = ref<AgentMessage[]>([
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const purposeTitles: Record<AgentPurpose, string> = {
|
||||||
|
'release-risk': '发布风险',
|
||||||
|
'failure-diagnosis': '失败诊断',
|
||||||
|
'runbook-qa': 'Runbook',
|
||||||
|
'release-note': '发布说明',
|
||||||
|
'incident-review': '事故复盘',
|
||||||
|
}
|
||||||
|
|
||||||
const activeCard = computed(() => {
|
const activeCard = computed(() => {
|
||||||
return platform.agentCards.find((card) => card.mode === selectedMode.value)
|
return platform.agentCards.find((card) => card.mode === selectedMode.value)
|
||||||
})
|
})
|
||||||
@@ -98,6 +107,26 @@ watch(selectedMode, (mode) => {
|
|||||||
prompt.value = '帮我检查当前发布是否存在配置、构建或通知风险。'
|
prompt.value = '帮我检查当前发布是否存在配置、构建或通知风险。'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
void loadInvocationHistory()
|
||||||
|
})
|
||||||
|
|
||||||
|
async function loadInvocationHistory() {
|
||||||
|
historyLoading.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const invocations = await devopsApi.listAgentInvocations()
|
||||||
|
messages.value = [
|
||||||
|
messages.value[0],
|
||||||
|
...invocations.slice(0, 20).map(toHistoryMessage),
|
||||||
|
]
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(error instanceof Error ? error.message : 'Agent 历史加载失败')
|
||||||
|
} finally {
|
||||||
|
historyLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function sendPrompt() {
|
async function sendPrompt() {
|
||||||
const text = prompt.value.trim()
|
const text = prompt.value.trim()
|
||||||
|
|
||||||
@@ -137,6 +166,37 @@ async function sendPrompt() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toHistoryMessage(invocation: AgentInvocation): AgentMessage {
|
||||||
|
return {
|
||||||
|
id: invocation.id,
|
||||||
|
role: 'assistant',
|
||||||
|
title: `${purposeTitles[invocation.type]} · ${statusLabel(invocation.status)}`,
|
||||||
|
content: [
|
||||||
|
`问题:${invocation.promptSummary}`,
|
||||||
|
'',
|
||||||
|
invocation.resultMarkdown || 'Agent 未返回内容',
|
||||||
|
].join('\n'),
|
||||||
|
contextSummary: invocation.contextSummary,
|
||||||
|
createdAt: formatTime(invocation.createdAt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusLabel(status: AgentInvocation['status']): string {
|
||||||
|
if (status === 'success') {
|
||||||
|
return '成功'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'failed') {
|
||||||
|
return '失败'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'running') {
|
||||||
|
return '运行中'
|
||||||
|
}
|
||||||
|
|
||||||
|
return '等待中'
|
||||||
|
}
|
||||||
|
|
||||||
function handleComposerKeydown(event: KeyboardEvent) {
|
function handleComposerKeydown(event: KeyboardEvent) {
|
||||||
if (event.key !== 'Enter' || (!event.metaKey && !event.ctrlKey)) {
|
if (event.key !== 'Enter' || (!event.metaKey && !event.ctrlKey)) {
|
||||||
return
|
return
|
||||||
@@ -208,6 +268,7 @@ function formatTime(value: string): string {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="message-list">
|
<div class="message-list">
|
||||||
|
<div v-if="historyLoading" class="history-loading">正在加载历史记录...</div>
|
||||||
<article v-for="message in messages" :key="message.id" class="agent-message" :class="message.role">
|
<article v-for="message in messages" :key="message.id" class="agent-message" :class="message.role">
|
||||||
<div class="message-meta">
|
<div class="message-meta">
|
||||||
<strong>{{ message.title }}</strong>
|
<strong>{{ message.title }}</strong>
|
||||||
@@ -300,6 +361,12 @@ function formatTime(value: string): string {
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.history-loading {
|
||||||
|
color: #64748b;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.agent-message {
|
.agent-message {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
|||||||
@@ -503,6 +503,12 @@ export const devopsApi = {
|
|||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async listAgentInvocations(): Promise<AgentInvocation[]> {
|
||||||
|
const { data } = await http.get<AgentInvocation[]>('/agent/invocations')
|
||||||
|
lastDataSource = 'backend'
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
|
||||||
async getSettings(): Promise<SystemSetting[]> {
|
async getSettings(): Promise<SystemSetting[]> {
|
||||||
const [health, integrationConfig] = await Promise.all([
|
const [health, integrationConfig] = await Promise.all([
|
||||||
fetchHealth(),
|
fetchHealth(),
|
||||||
|
|||||||
Reference in New Issue
Block a user