Files
common_agent/frontend/src/pages/studio/AgentWorkspacePage.vue

144 lines
4.7 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 setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { ChatDotRound, Coin, Timer } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import type { AgentMessageRecord, AgentWorkspace } from '@/api/agent';
import { appendAgentMessage, getAgentWorkspace } from '@/api/agent';
const loading = ref(false);
const agentId = ref('1001');
const workspace = ref<AgentWorkspace | null>(null);
const draftMessage = ref('');
const citations = computed(() => {
return (workspace.value?.messages ?? [])
.filter((message) => message.citationJson?.includes('chunkId'))
.map((message, index) => ({
key: `${message.id || index}`,
title: `引用 ${index + 1}`,
text: message.citationJson ?? '',
}));
});
const latestAssistantMessage = computed(() => {
const assistantMessages = (workspace.value?.messages ?? []).filter((item) => item.role === 'assistant');
return assistantMessages.length > 0 ? assistantMessages[assistantMessages.length - 1] : null;
});
function roleLabel(role: AgentMessageRecord['role']) {
if (role === 'user') {
return '用户';
}
if (role === 'assistant') {
return 'Agent';
}
return '系统';
}
async function loadWorkspace() {
loading.value = true;
try {
const response = await getAgentWorkspace(agentId.value);
workspace.value = response.data;
} finally {
loading.value = false;
}
}
async function sendMessage() {
if (!workspace.value?.sessionId || !draftMessage.value.trim()) {
return;
}
await appendAgentMessage(workspace.value.sessionId, {
role: 'user',
content: draftMessage.value.trim(),
});
ElMessage.success('调试消息已写入会话');
draftMessage.value = '';
await loadWorkspace();
}
function formatCost(tokens?: number) {
if (!tokens) {
return '¥0.000';
}
return `¥${(tokens / 100000).toFixed(3)}`;
}
onMounted(loadWorkspace);
</script>
<template>
<section class="studio-page agent-page">
<header class="page-title-row">
<div>
<p class="studio-kicker">AgentWorkspaceView</p>
<h1>Agent 对话调试</h1>
</div>
<el-button type="primary">{{ workspace?.status || 'Draft' }}</el-button>
</header>
<div class="agent-layout" v-loading="loading">
<section class="studio-panel chat-panel">
<div class="panel-heading">
<div>
<h2>{{ workspace?.agentName || 'Agent 工作台' }}</h2>
<span>GET /api/agent-sessions/workspace</span>
</div>
<el-tag>{{ workspace?.sessionStatus || workspace?.status || 'DRAFT' }}</el-tag>
</div>
<div class="message-list">
<article
v-for="message in workspace?.messages ?? []"
:key="message.id || message.content"
:class="message.role"
:data-test="`agent-message-${message.role}`"
>
<strong>{{ roleLabel(message.role) }}</strong>
<p>{{ message.content }}</p>
</article>
</div>
<div class="chat-composer">
<span>输入调试问题写入当前 agent_session / agent_message</span>
<input v-model="draftMessage" data-test="agent-message-input" type="text" />
<el-button data-test="agent-send-message" type="primary" @click="sendMessage">
<el-icon><ChatDotRound /></el-icon>
发送
</el-button>
</div>
</section>
<aside class="studio-panel citation-panel">
<div class="panel-heading compact">
<h2>引用切片</h2>
<span>{{ workspace?.citationCount || 0 }} 个来源</span>
</div>
<article v-for="citation in citations" :key="citation.key" class="citation-card" data-test="agent-citation-card">
<strong>{{ citation.title }}</strong>
<el-tag type="success">引用</el-tag>
<p>{{ citation.text }}</p>
</article>
</aside>
<aside class="studio-panel run-inspector">
<div class="panel-heading compact">
<h2>运行追踪</h2>
<span data-test="agent-latest-request-id">requestId: {{ workspace?.latestRequestId || '-' }}</span>
</div>
<div class="metric-mini">
<span><el-icon><Timer /></el-icon> {{ workspace?.sessionStatus || '-' }}</span>
<span><el-icon><Coin /></el-icon> {{ formatCost(workspace?.totalTokens) }}</span>
<span>{{ workspace?.totalTokens || 0 }} tokens</span>
</div>
<ol class="log-list">
<li v-if="latestAssistantMessage">
<time>{{ workspace?.sessionCode || '-' }}</time>
<span>{{ latestAssistantMessage.content }}</span>
</li>
</ol>
</aside>
</div>
</section>
</template>