326 lines
10 KiB
JavaScript
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']
|
|
}
|
|
}
|
|
)
|