ForcePilot/web/src/stores/agent.js
2026-06-01 22:29:18 +08:00

326 lines
10 KiB
JavaScript

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { agentApi, databaseApi, mcpApi, skillApi } from '@/apis'
import { isDefaultAllAgentResourceKind } from '@/utils/agentConfigUtils'
import { handleChatError } from '@/utils/errorHandler'
function normalizeAgent(agent) {
const agentId = agent?.agent_id || agent?.slug || agent?.id
return agentId
? { ...agent, id: agentId, agent_id: agentId, slug: agent?.slug || agentId }
: agent
}
export const BUILTIN_AGENT_ID = 'default-chatbot'
export function isBuiltinAgent(agent) {
return agent?.is_builtin || agent?.id === BUILTIN_AGENT_ID || agent?.slug === BUILTIN_AGENT_ID
}
function sortAgents(agents) {
return [...agents].sort((a, b) => {
if (isBuiltinAgent(a) !== isBuiltinAgent(b)) return isBuiltinAgent(a) ? -1 : 1
return String(a.name || a.id).localeCompare(String(b.name || b.id), 'zh-CN')
})
}
function getPreferredAgentId(agents, persistedId) {
const chatAgents = agents.filter((agent) => !agent.is_subagent)
if (persistedId && chatAgents.some((agent) => agent.id === persistedId)) return persistedId
return chatAgents.find(isBuiltinAgent)?.id || chatAgents[0]?.id || null
}
function extractContext(agent) {
const configJson = agent?.config_json || {}
return { ...(configJson.context || configJson || {}) }
}
export const useAgentStore = defineStore(
'agent',
() => {
const agents = ref([])
const selectedAgentId = ref(null)
const availableKnowledgeBases = ref([])
const availableMcps = ref([])
const availableSkills = ref([])
const agentConfig = ref({})
const originalAgentConfig = ref({})
const agentDetails = ref({})
const isLoadingAgents = ref(false)
const isLoadingConfig = ref(false)
const isLoadingAgentDetail = ref(false)
const error = ref(null)
const isInitialized = ref(false)
const isInitializing = ref(false)
const selectedAgent = computed(() => {
const agentId = selectedAgentId.value
return agentId
? agentDetails.value[agentId] || agents.value.find((a) => a.id === agentId) || null
: null
})
const agentsList = computed(() => agents.value)
const configurableItems = computed(() => {
const items = { ...(selectedAgent.value?.configurable_items || {}) }
Object.keys(items).forEach((key) => {
const item = items[key]
if (item?.x_oap_ui_config) {
items[key] = { ...item, ...item.x_oap_ui_config }
delete items[key].x_oap_ui_config
}
})
return items
})
const availableTools = computed(() => configurableItems.value.tools?.options || [])
const hasConfigChanges = computed(
() => JSON.stringify(agentConfig.value) !== JSON.stringify(originalAgentConfig.value)
)
async function fetchMentionResources() {
try {
const [dbsRes, mcpsRes, skillsRes] = await Promise.all([
databaseApi.getAccessibleDatabases().catch(() => ({ databases: [] })),
mcpApi.getMcpServers().catch(() => ({ data: [] })),
skillApi.listSkills().catch(() => ({ data: [] }))
])
availableKnowledgeBases.value = dbsRes.databases || []
availableMcps.value = mcpsRes.data || []
availableSkills.value = skillsRes.data || []
} catch (e) {
console.warn('Failed to fetch mention resources:', e)
}
}
async function initialize() {
if (isInitialized.value || isInitializing.value) return
isInitializing.value = true
try {
await Promise.all([fetchAgents(), fetchMentionResources()])
const targetAgentId = getPreferredAgentId(agents.value, selectedAgentId.value)
if (targetAgentId) {
await selectAgent(targetAgentId)
}
isInitialized.value = true
} catch (err) {
console.error('Failed to initialize agent store:', err)
handleChatError(err, 'initialize')
error.value = err.message
} finally {
isInitializing.value = false
}
}
async function fetchAgents({ includeSubagents = false } = {}) {
isLoadingAgents.value = true
error.value = null
try {
const response = await agentApi.getAgents({ includeSubagents })
agents.value = sortAgents((response.agents || []).map(normalizeAgent))
} catch (err) {
console.error('Failed to fetch agents:', err)
handleChatError(err, 'fetch')
error.value = err.message
throw err
} finally {
isLoadingAgents.value = false
}
}
function applyConfigDefaults(loadedConfig, configItems = configurableItems.value) {
const items = { ...configItems }
Object.keys(items).forEach((key) => {
const item = items[key]?.x_oap_ui_config
? { ...items[key], ...items[key].x_oap_ui_config }
: items[key]
const isDefaultAllList = isDefaultAllAgentResourceKind(item?.kind)
if (loadedConfig[key] === undefined || (loadedConfig[key] === null && !isDefaultAllList)) {
if (item.default !== undefined) loadedConfig[key] = item.default
}
if (
loadedConfig[key] !== undefined &&
loadedConfig[key] !== null &&
loadedConfig[key] !== '' &&
(item?.type === 'number' || item?.type === 'int' || item?.type === 'float')
) {
const numericValue = Number(loadedConfig[key])
if (!Number.isNaN(numericValue)) {
loadedConfig[key] = item.type === 'int' ? Math.trunc(numericValue) : numericValue
}
}
})
return loadedConfig
}
async function fetchAgentDetail(agentId, forceRefresh = false) {
if (!agentId) return null
if (!forceRefresh && agentDetails.value[agentId]) return agentDetails.value[agentId]
isLoadingAgentDetail.value = true
error.value = null
try {
const response = await agentApi.getAgentDetail(agentId)
const agent = normalizeAgent(response.agent || response)
agentDetails.value[agent.id] = agent
return agent
} catch (err) {
console.error(`Failed to fetch agent detail for ${agentId}:`, err)
handleChatError(err, 'fetch')
error.value = err.message
throw err
} finally {
isLoadingAgentDetail.value = false
}
}
async function selectAgent(agentId, { allowSubagent = false } = {}) {
if (!agentId) return
let knownAgent = agentDetails.value[agentId] || agents.value.find((a) => a.id === agentId)
if (!knownAgent) {
knownAgent = await fetchAgentDetail(agentId)
}
if (knownAgent?.is_subagent && !allowSubagent) return
isLoadingConfig.value = true
try {
const detail = agentDetails.value[agentId] || (await fetchAgentDetail(agentId))
const loadedConfig = applyConfigDefaults(
extractContext(detail),
detail?.configurable_items || {}
)
selectedAgentId.value = agentId
agentConfig.value = loadedConfig
originalAgentConfig.value = { ...loadedConfig }
} finally {
isLoadingConfig.value = false
}
}
async function saveAgentConfig() {
const targetAgentId = selectedAgentId.value
if (!targetAgentId) return
try {
const response = await agentApi.updateAgent(targetAgentId, {
config_json: { context: agentConfig.value }
})
const updated = normalizeAgent(response.agent)
agentDetails.value[targetAgentId] = updated
const index = agents.value.findIndex((item) => item.id === targetAgentId)
if (index >= 0) agents.value.splice(index, 1, updated)
originalAgentConfig.value = { ...agentConfig.value }
} catch (err) {
console.error('Failed to save agent config:', err)
handleChatError(err, 'save')
error.value = err.message
throw err
}
}
async function createAgent(payload) {
const response = await agentApi.createAgent(payload)
const created = normalizeAgent(response.agent)
if (created?.id) {
agentDetails.value[created.id] = created
agents.value = sortAgents([
created,
...agents.value.filter((item) => item.id !== created.id)
])
if (!created.is_subagent) await selectAgent(created.id)
}
return created
}
async function updateAgentProfile(agentId, payload) {
const response = await agentApi.updateAgent(agentId, payload)
const updated = normalizeAgent(response.agent)
agentDetails.value[updated.id] = updated
const index = agents.value.findIndex((item) => item.id === updated.id)
if (index >= 0) agents.value.splice(index, 1, updated)
return updated
}
async function deleteAgent(agentId) {
await agentApi.deleteAgent(agentId)
agents.value = agents.value.filter((item) => item.id !== agentId)
delete agentDetails.value[agentId]
if (selectedAgentId.value === agentId) {
selectedAgentId.value = null
agentConfig.value = {}
originalAgentConfig.value = {}
const nextAgentId = getPreferredAgentId(agents.value)
if (nextAgentId) await selectAgent(nextAgentId)
}
}
function resetAgentConfig() {
agentConfig.value = { ...originalAgentConfig.value }
}
function updateAgentConfig(updates) {
Object.assign(agentConfig.value, updates)
}
function reset() {
agents.value = []
selectedAgentId.value = null
availableKnowledgeBases.value = []
availableMcps.value = []
availableSkills.value = []
agentConfig.value = {}
originalAgentConfig.value = {}
agentDetails.value = {}
isLoadingAgents.value = false
isLoadingConfig.value = false
isLoadingAgentDetail.value = false
error.value = null
isInitialized.value = false
isInitializing.value = false
}
return {
agents,
selectedAgentId,
availableKnowledgeBases,
availableMcps,
availableSkills,
agentConfig,
originalAgentConfig,
agentDetails,
isLoadingAgents,
isLoadingConfig,
isLoadingAgentDetail,
error,
isInitialized,
selectedAgent,
agentsList,
configurableItems,
availableTools,
hasConfigChanges,
initialize,
fetchAgents,
fetchAgentDetail,
fetchMentionResources,
selectAgent,
saveAgentConfig,
createAgent,
updateAgentProfile,
deleteAgent,
resetAgentConfig,
updateAgentConfig,
reset
}
},
{
persist: {
key: 'agent-store',
storage: localStorage,
pick: ['selectedAgentId']
}
}
)