416 lines
9.3 KiB
HTML
Executable File
416 lines
9.3 KiB
HTML
Executable File
<!-- Agent状态显示组件 - 可嵌入index.html -->
|
|
<template id="agent-status-component">
|
|
<div class="agent-status-panel">
|
|
<div class="agent-header">
|
|
<h3>{{ t('agent-assistant') }}</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="t('enter-question')"
|
|
@keydown.enter.prevent="sendMessage"
|
|
rows="3"></textarea>
|
|
<button @click="sendMessage" :disabled="isProcessing">
|
|
{{ isProcessing ? t('processing') : t('send') }}
|
|
</button>
|
|
</div>
|
|
|
|
<div class="agent-tasks" v-if="tasks.length > 0">
|
|
<h4>{{ t('task-list') }}</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 src="/static/i18n.js"></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.currentMode = t('standby');
|
|
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', t('msg-task-submitted') + data.task_id);
|
|
this.loadTasks();
|
|
} else {
|
|
this.addMessage('error', data.message || t('msg-submit-failed'));
|
|
}
|
|
} catch (error) {
|
|
this.addMessage('error', t('msg-request-failed') + 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(t('msg-load-task-failed'), 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 || t('msg-task-completed'));
|
|
|
|
// 显示执行结果摘要
|
|
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',
|
|
t('msg-analysis-complete') + (metrics.final_members || 0) + t('msg-gene-members'));
|
|
}
|
|
}
|
|
} else if (task.status === 'failed') {
|
|
this.addMessage('error', task.result?.response || t('msg-task-failed'));
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error(t('msg-check-task-status-failed'), 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 keys = {
|
|
'user': 'user-role',
|
|
'assistant': 'agent-role',
|
|
'system': 'system-role',
|
|
'error': 'error-role',
|
|
'execution': 'execution-role',
|
|
'planning': 'planning-role',
|
|
'diagnosis': 'diagnosis-role'
|
|
};
|
|
return keys[role] ? t(keys[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'
|
|
});
|
|
},
|
|
|
|
t(key) {
|
|
return window.t ? window.t(key) : key;
|
|
}
|
|
}
|
|
};
|
|
</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> |