Files
AgentCoord/frontend/src/layout/components/Main/TaskTemplate/AgentRepo/index.vue

273 lines
7.7 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 { ref, computed, onMounted } from 'vue'
import { ElNotification } from 'element-plus'
import { pick } from 'lodash'
import api from '@/api/index.ts'
import SvgIcon from '@/components/SvgIcon/index.vue'
import { agentMapDuty } from '@/layout/components/config.ts'
import { type Agent, useAgentsStore } from '@/stores'
import { readConfig } from '@/utils/readJson.ts'
import AgentRepoList from './AgentRepoList.vue'
const agentsStore = useAgentsStore()
// 如果agentsStore.agents不存在就读取默认配置的json文件
onMounted(async () => {
if (!agentsStore.agents.length) {
const res = await readConfig<Agent[]>('agent.json')
agentsStore.setAgents(res)
}
await api.setAgents(
agentsStore.agents.map(item => pick(item, ['Name', 'Profile', 'apiUrl', 'apiKey', 'apiModel']))
)
})
// 上传agent文件
const fileInput = ref<HTMLInputElement>()
const triggerFileSelect = () => {
fileInput.value?.click()
}
const handleFileSelect = (event: Event) => {
const input = event.target as HTMLInputElement
if (input.files && input.files[0]) {
const file = input.files[0]
readFileContent(file)
}
}
// 在validateApiConfig函数中添加更详细的日志
const validateApiConfig = (agent: any) => {
const hasApiUrl = 'apiUrl' in agent
const hasApiKey = 'apiKey' in agent
const hasApiModel = 'apiModel' in agent
// 三个字段必须同时存在或同时不存在
if (hasApiUrl !== hasApiKey || hasApiKey !== hasApiModel) {
console.error('❌ API配置不完整:', {
agentName: agent.Name,
missingFields: {
apiUrl: !hasApiUrl,
apiKey: !hasApiKey,
apiModel: !hasApiModel
}
})
return false
}
if (hasApiUrl && hasApiKey && hasApiModel) {
console.log('✅ API配置完整将使用自定义API配置:', {
apiUrl: agent.apiUrl,
apiKey: agent.apiKey ? '***' + agent.apiKey.slice(-4) : '未设置',
apiModel: agent.apiModel
})
} else {
console.log(' 未设置API配置将使用默认URL配置')
}
return true
}
const readFileContent = (file: File) => {
console.log('📁 开始读取文件:', file.name, '大小:', file.size, '类型:', file.type)
const reader = new FileReader()
reader.onload = e => {
try {
console.log('📄 文件读取完成开始解析JSON')
const content = e.target?.result as string
const jsonData = JSON.parse(content)
console.log('🔍 解析的JSON数据:', jsonData)
console.log(
'📊 数据类型:',
Array.isArray(jsonData) ? '数组' : '对象',
'长度:',
Array.isArray(jsonData) ? jsonData.length : 'N/A'
)
if (!Array.isArray(jsonData)) {
console.error('❌ JSON格式错误: 必须为数组格式')
ElMessage.error('JSON格式错误: 必须为数组格式')
return
}
console.log('🔍 开始验证智能体数据...')
const validAgents = jsonData.filter((agent, index) => {
console.log(`🔍 验证第${index + 1}个智能体:`, agent.Name || '未命名')
// 验证必需字段
if (!agent.Name || typeof agent.Name !== 'string') {
console.error(`❌ 智能体${index + 1}缺少Name字段或格式错误`)
return false
}
if (!agent.Icon || typeof agent.Icon !== 'string') {
console.error(`❌ 智能体${index + 1}缺少Icon字段或格式错误`)
return false
}
if (!agent.Profile || typeof agent.Profile !== 'string') {
console.error(`❌ 智能体${index + 1}缺少Profile字段或格式错误`)
return false
}
// 验证API配置
if (!validateApiConfig(agent)) {
console.error(`❌ 智能体${index + 1}API配置验证失败`)
return false
}
return true
})
// 修改发送到后端的数据
const processedAgents = validAgents.map(agent => ({
Name: agent.Name,
Profile: agent.Profile,
Icon: agent.Icon,
Classification: agent.Classification || '',
apiUrl: agent.apiUrl,
apiKey: agent.apiKey,
apiModel: agent.apiModel
}))
agentsStore.setAgents(processedAgents)
// 调用API
api
.setAgents(processedAgents)
.then(() => {
console.log('✅ 后端API调用成功')
ElMessage.success('智能体上传成功')
})
.catch(error => {
console.error('❌ 后端API调用失败:', error)
ElMessage.error('智能体上传失败')
})
} catch (error) {
console.error('❌ JSON解析错误:', error)
ElMessage.error('JSON解析错误')
}
}
reader.onerror = error => {
console.error('❌ 文件读取错误:', error)
ElMessage.error('文件读取错误')
}
reader.readAsText(file)
}
// 根据currentTask排序agent列表
const agentList = computed(() => {
const selected: Agent[] = []
const unselected: {
title: string
data: Agent[]
}[] = []
const obj: Record<string, Agent[]> = {}
if (!agentsStore.agents.length) {
return {
selected,
unselected
}
}
for (const agent of agentsStore.agents) {
// if (agentsStore.currentTask?.AgentSelection?.includes(agent.Name)) {
// selected.push(agent)
// continue
// }
if (obj[agent.Classification]) {
obj[agent.Classification]!.push(agent)
} else {
const arr = [agent]
obj[agent.Classification] = arr
unselected.push({
title: agent.Classification,
data: arr
})
}
}
return {
selected,
unselected: unselected
}
})
</script>
<template>
<div class="agent-repo h-full flex flex-col" id="agent-repo">
<!-- 头部 -->
<div class="flex items-center justify-between">
<span class="text-[18px] font-bold text-[var(--color-text-title-header)]">智能体库</span>
<!-- 上传文件 -->
<input type="file" accept=".json" @change="handleFileSelect" class="hidden" ref="fileInput" />
<div class="plus-button" @click="triggerFileSelect">
<svg-icon icon-class="plus" color="var(--color-text)" size="18px" />
</div>
</div>
<!-- 智能体列表 -->
<div class="pt-[18px] flex-1 overflow-y-auto relative">
<!-- 已选中的智能体 -->
<AgentRepoList :agent-list="agentList.selected" />
<!-- 为选择的智能体 -->
<div v-for="agent in agentList.unselected" :key="agent.title">
<p class="text-[12px] font-bold py-[8px]">{{ agent.title }}</p>
<AgentRepoList :agent-list="agent.data" />
</div>
</div>
<!-- 底部提示栏 -->
<div
class="w-full grid grid-cols-3 gap-x-[10px] bg-[var(--color-bg-indicator)] rounded-[20px] p-[8px] mt-[10px]"
>
<div
v-for="item in Object.values(agentMapDuty)"
:key="item.key"
class="flex items-center justify-center gap-x-1"
>
<div
class="w-[8px] h-[8px] rounded-full"
:style="{
background: item.color,
border: `1px solid ${item.border}`
}"
></div>
<span class="text-[12px]">{{ item.name }}</span>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.agent-repo {
padding: 0 8px;
.plus-button {
background: var(--color-bg-tertiary);
width: 24px;
height: 24px;
padding: 0;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
background: var(--color-bg-quaternary);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
}
}
}
#agent-repo {
:deep(.agent-repo-item-popover) {
padding: 0;
border-radius: 20px;
background: var(--color-bg-secondary);
}
}
</style>