Files

411 lines
9.1 KiB
HTML
Raw Permalink Normal View History

2026-08-03 18:42:47 +08:00
<!-- Agent状态显示组件 - 可嵌入index.html -->
<template id="agent-status-component">
<div class="agent-status-panel">
<div class="agent-header">
<h3>🤖 Agent 智能助手</h3>
<span class="agent-mode">{{ currentMode }}</span>
</div>
<div class="agent-messages" ref="messageContainer">
<div v-for="(msg, index) in messages" :key="index"
:class="['message', msg.role]">
<div class="message-role">{{ getRoleLabel(msg.role) }}</div>
<div class="message-content" v-html="formatMessage(msg.content)"></div>
<div class="message-time">{{ formatTime(msg.timestamp) }}</div>
</div>
</div>
<div class="agent-input">
<textarea v-model="userInput"
placeholder="输入您的问题,例如:分析SPL基因家族"
@keydown.enter.prevent="sendMessage"
rows="3"></textarea>
<button @click="sendMessage" :disabled="isProcessing">
{{ isProcessing ? '处理中...' : '发送' }}
</button>
</div>
<div class="agent-tasks" v-if="tasks.length > 0">
<h4>📋 任务列表</h4>
<div v-for="task in tasks" :key="task.task_id"
:class="['task-item', task.status]"
@click="selectTask(task)">
<span class="task-status">{{ getStatusIcon(task.status) }}</span>
<span class="task-message">{{ task.message }}</span>
<span class="task-progress">{{ task.progress }}%</span>
</div>
</div>
</div>
</template>
<script>
// Agent状态组件
const AgentStatusComponent = {
template: '#agent-status-component',
data() {
return {
messages: [],
userInput: '',
isProcessing: false,
currentMode: '待机',
sessionId: '',
tasks: [],
currentTask: null,
pollingInterval: null
};
},
mounted() {
this.sessionId = 'session_' + Date.now();
this.loadTasks();
this.startPolling();
},
beforeUnmount() {
this.stopPolling();
},
methods: {
async sendMessage() {
if (!this.userInput.trim() || this.isProcessing) return;
const message = this.userInput.trim();
this.userInput = '';
this.isProcessing = true;
// 添加用户消息
this.addMessage('user', message);
try {
// 调用Agent执行API
const response = await fetch('/api/agent/execute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: message,
session_id: this.sessionId,
model: 'kimi'
})
});
const data = await response.json();
if (data.success) {
this.currentTask = data.task_id;
this.addMessage('system', `任务已提交: ${data.task_id}`);
this.loadTasks();
} else {
this.addMessage('error', data.message || '提交失败');
}
} catch (error) {
this.addMessage('error', `请求失败: ${error.message}`);
}
this.isProcessing = false;
},
async loadTasks() {
try {
const response = await fetch('/api/agent/tasks');
const data = await response.json();
if (data.success) {
this.tasks = data.tasks;
}
} catch (error) {
console.error('加载任务失败:', error);
}
},
async checkTaskStatus(taskId) {
try {
const response = await fetch(`/api/agent/status/${taskId}`);
const data = await response.json();
if (data.success && data.task) {
const task = data.task;
// 更新任务状态
const taskIndex = this.tasks.findIndex(t => t.task_id === taskId);
if (taskIndex >= 0) {
this.tasks[taskIndex] = { ...this.tasks[taskIndex], ...task };
}
// 如果任务完成,显示结果
if (task.status === 'completed' && task.result) {
this.addMessage('assistant', task.result.response || '任务完成');
// 显示执行结果摘要
if (task.result.execution_result) {
const execResult = task.result.execution_result;
if (execResult.status === 'success') {
const manifest = execResult.manifest || {};
const metrics = manifest.metrics || {};
this.addMessage('system',
`✅ 分析完成! 找到 ${metrics.final_members || 0} 个基因成员`);
}
}
} else if (task.status === 'failed') {
this.addMessage('error', task.result?.response || '任务失败');
}
}
} catch (error) {
console.error('检查任务状态失败:', error);
}
},
selectTask(task) {
this.currentTask = task.task_id;
this.checkTaskStatus(task.task_id);
},
startPolling() {
this.pollingInterval = setInterval(() => {
if (this.currentTask) {
this.checkTaskStatus(this.currentTask);
}
this.loadTasks();
}, 3000); // 每3秒轮询一次
},
stopPolling() {
if (this.pollingInterval) {
clearInterval(this.pollingInterval);
}
},
addMessage(role, content) {
this.messages.push({
role: role,
content: content,
timestamp: new Date().toISOString()
});
// 滚动到底部
setTimeout(() => {
const container = this.$refs.messageContainer;
if (container) {
container.scrollTop = container.scrollHeight;
}
}, 100);
},
getRoleLabel(role) {
const labels = {
'user': '👤 用户',
'assistant': '🤖 Agent',
'system': '💬 系统',
'error': '❌ 错误',
'execution': '⚙️ 执行',
'planning': '📋 规划',
'diagnosis': '🔍 诊断'
};
return labels[role] || role;
},
getStatusIcon(status) {
const icons = {
'pending': '⏳',
'running': '🔄',
'completed': '✅',
'failed': '❌',
'cancelled': '🚫'
};
return icons[status] || '❓';
},
formatMessage(content) {
// 简单的Markdown格式化
return content
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
.replace(/\n/g, '<br>');
},
formatTime(timestamp) {
if (!timestamp) return '';
const date = new Date(timestamp);
return date.toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
}
};
</script>
<style>
.agent-status-panel {
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
background: #f9f9f9;
height: 100%;
display: flex;
flex-direction: column;
}
.agent-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #eee;
}
.agent-header h3 {
margin: 0;
font-size: 16px;
}
.agent-mode {
font-size: 12px;
color: #666;
background: #e0e0e0;
padding: 2px 8px;
border-radius: 4px;
}
.agent-messages {
flex: 1;
overflow-y: auto;
max-height: 400px;
margin-bottom: 12px;
}
.message {
margin-bottom: 12px;
padding: 8px 12px;
border-radius: 8px;
background: white;
border-left: 3px solid #ddd;
}
.message.user {
border-left-color: #4CAF50;
background: #E8F5E9;
}
.message.assistant {
border-left-color: #2196F3;
background: #E3F2FD;
}
.message.error {
border-left-color: #f44336;
background: #FFEBEE;
}
.message.system {
border-left-color: #9E9E9E;
background: #F5F5F5;
font-size: 12px;
}
.message-role {
font-size: 12px;
color: #666;
margin-bottom: 4px;
}
.message-content {
font-size: 14px;
line-height: 1.5;
}
.message-time {
font-size: 11px;
color: #999;
text-align: right;
margin-top: 4px;
}
.agent-input {
display: flex;
gap: 8px;
margin-bottom: 12px;
}
.agent-input textarea {
flex: 1;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
resize: none;
font-family: inherit;
}
.agent-input button {
padding: 8px 16px;
background: #2196F3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.agent-input button:disabled {
background: #ccc;
cursor: not-allowed;
}
.agent-tasks {
border-top: 1px solid #eee;
padding-top: 12px;
}
.agent-tasks h4 {
margin: 0 0 8px 0;
font-size: 14px;
}
.task-item {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 8px;
border-radius: 4px;
cursor: pointer;
margin-bottom: 4px;
background: white;
}
.task-item:hover {
background: #f0f0f0;
}
.task-item.pending {
border-left: 3px solid #FFC107;
}
.task-item.running {
border-left: 3px solid #2196F3;
}
.task-item.completed {
border-left: 3px solid #4CAF50;
}
.task-item.failed {
border-left: 3px solid #f44336;
}
.task-status {
font-size: 14px;
}
.task-message {
flex: 1;
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.task-progress {
font-size: 11px;
color: #666;
}
</style>