Files
AgentCoord/frontend/src/stores/modules/agents.ts

474 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
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.
import { ref, watch, computed } from 'vue'
import { defineStore } from 'pinia'
import { v4 as uuidv4 } from 'uuid'
import { getAgentMapIcon } from '@/layout/components/config'
import { store } from '../index'
import { useStorage } from '@vueuse/core'
import type { IExecuteRawResponse } from '@/api'
import { useConfigStore } from '@/stores/modules/config.ts'
export interface Agent {
Name: string
Profile: string
Icon: string
Classification: string
apiUrl?: string
apiKey?: string
apiModel?: string
}
type HslColorVector = [number, number, number]
export interface IRichText {
template: string
data: Record<
string,
{
text: string
style?: Record<string, string>
color?: HslColorVector
}
>
}
export interface TaskProcess {
ActionType: string
AgentName: string
Description: string
ID: string
ImportantInput: string[]
}
export interface IRawStepTask {
Id?: string
StepName?: string
TaskContent?: string
InputObject_List?: string[]
OutputObject?: string
AgentSelection?: string[]
Collaboration_Brief_frontEnd: IRichText
TaskProcess: TaskProcess[]
}
export interface IApiAgentAction {
id: string
type: string
agent: string
description: string
inputs: string[]
}
export interface IApiStepTask {
name: string
content: string
inputs: string[]
output: string
agents: string[]
brief: IRichText
process: IApiAgentAction[]
}
// 智能体评分数据类型
export interface IScoreItem {
score: number
reason: string
}
export interface IAgentSelectModifyAddRequest {
aspectList: string[] // 维度列表
agentScores: Record<string, Record<string, IScoreItem>> // agent -> 维度 -> 评分(与后端返回格式一致)
}
export interface IAgentSelectModifyInitRequest {
goal: string
stepTask: IApiStepTask
}
export interface IRawPlanResponse {
'Initial Input Object'?: string[] | string
'General Goal'?: string
'Collaboration Process'?: IRawStepTask[]
}
const storageKey = '$agents' as const
// 清除所有以 storageKey 开头的 localStorage
function clearStorageByVersion() {
Object.keys(localStorage)
.filter((key) => key.startsWith(storageKey))
.forEach((key) => localStorage.removeItem(key))
}
export const useAgentsStore = defineStore('agents', () => {
const configStore = useConfigStore()
const agents = useStorage<Agent[]>(`${storageKey}-repository`, [])
function setAgents(agent: Agent[]) {
agents.value = agent
}
// 智能体评分数据存储
const IAgentSelectModifyAddRequest = useStorage<IAgentSelectModifyAddRequest | null>(
`${storageKey}-score-data`,
null,
)
// 设置智能体评分数据
function setAgentScoreData(data: IAgentSelectModifyAddRequest) {
IAgentSelectModifyAddRequest.value = data
}
// 添加新维度的评分数据(追加模式)
function addAgentScoreAspect(aspectName: string, scores: Record<string, IScoreItem>) {
if (!IAgentSelectModifyAddRequest.value) {
IAgentSelectModifyAddRequest.value = {
aspectList: [],
agentScores: {},
}
}
// 检查维度是否已存在
if (!IAgentSelectModifyAddRequest.value.aspectList.includes(aspectName)) {
IAgentSelectModifyAddRequest.value.aspectList.push(aspectName)
}
// 添加该维度的评分数据
// scores: { agentName: { score, reason } }
// 转换为 agentScores[agentName][aspectName] = { score, reason }
for (const [agentName, scoreItem] of Object.entries(scores)) {
if (!IAgentSelectModifyAddRequest.value.agentScores[agentName]) {
IAgentSelectModifyAddRequest.value.agentScores[agentName] = {}
}
IAgentSelectModifyAddRequest.value.agentScores[agentName][aspectName] = scoreItem
}
}
// 清除智能体评分数据
function clearAgentScoreData() {
IAgentSelectModifyAddRequest.value = null
}
// 智能体分配确认的组合列表存储 - 按任务ID分别存储不持久化到localStorage
const confirmedAgentGroupsMap = ref<Map<string, string[][]>>(new Map())
// 获取指定任务的确认的agent组合列表
function getConfirmedAgentGroups(taskId: string): string[][] {
return confirmedAgentGroupsMap.value.get(taskId) || []
}
// 添加确认的agent组合到指定任务
function addConfirmedAgentGroup(taskId: string, group: string[]) {
const groups = confirmedAgentGroupsMap.value.get(taskId) || []
groups.push(group)
confirmedAgentGroupsMap.value.set(taskId, groups)
}
// 清除指定任务的确认的agent组合列表
function clearConfirmedAgentGroups(taskId: string) {
confirmedAgentGroupsMap.value.delete(taskId)
}
// 清除所有任务的确认的agent组合列表
function clearAllConfirmedAgentGroups() {
confirmedAgentGroupsMap.value.clear()
}
const planModificationWindow = ref(false)
const planTaskWindow = ref(false)
const agentAllocationDialog = ref(false)
function openPlanModification() {
planModificationWindow.value = true
}
function closePlanModification() {
planModificationWindow.value = false
}
function openPlanTask() {
planTaskWindow.value = true
}
function closePlanTask() {
planTaskWindow.value = false
}
function openAgentAllocationDialog() {
agentAllocationDialog.value = true
}
function closeAgentAllocationDialog() {
agentAllocationDialog.value = false
}
// 任务搜索的内容
const searchValue = useStorage<string>(`${storageKey}-search-value`, '')
function setSearchValue(value: string) {
searchValue.value = value
}
const storageVersionIdentifier = useStorage<string>(
`${storageKey}-storage-version-identifier`,
'',
)
// 监听 configStore.config.agentRepository.storageVersionIdentifier 改变
watch(
() => configStore.config.agentRepository.storageVersionIdentifier,
(value) => {
// value与storageVersionIdentifier不一致清除所有storageKey开头的localStorage
if (value !== storageVersionIdentifier.value) {
clearStorageByVersion()
storageVersionIdentifier.value = value
}
},
{
immediate: true,
},
)
// 当前的展示的任务流程
const currentTask = ref<IRawStepTask>()
function setCurrentTask(task: IRawStepTask) {
const existingTask = currentTask.value
// 🆕 智能判断如果是同一个任务保留用户修改过的数据AgentSelection、TaskProcess、Collaboration_Brief_frontEnd
if (existingTask && existingTask.Id === task.Id) {
currentTask.value = {
...task,
AgentSelection: existingTask.AgentSelection || task.AgentSelection,
TaskProcess: existingTask.TaskProcess || task.TaskProcess,
Collaboration_Brief_frontEnd:
existingTask.Collaboration_Brief_frontEnd || task.Collaboration_Brief_frontEnd,
}
// 🆕 同步更新主流程数据(让执行结果卡片和任务大纲都能联动)
syncCurrentTaskToMainProcess(currentTask.value)
console.log('🔄 setCurrentTask: 保留同一任务的分支数据', {
taskId: task.Id,
taskName: task.StepName,
preservedAgentSelection: currentTask.value.AgentSelection,
preservedTaskProcessLength: currentTask.value.TaskProcess?.length || 0,
})
} else {
// 不同任务:使用新任务数据
currentTask.value = task
console.log('🔄 setCurrentTask: 切换到不同任务', {
taskId: task.Id,
taskName: task.StepName,
agentSelection: task.AgentSelection,
taskProcessLength: task.TaskProcess?.length || 0,
})
}
}
// 🆕 同步 currentTask 到主流程数据
function syncCurrentTaskToMainProcess(updatedTask: IRawStepTask) {
const collaborationProcess = agentRawPlan.value.data?.['Collaboration Process']
if (!collaborationProcess) {
console.warn('⚠️ syncCurrentTaskToMainProcess: collaborationProcess 不存在')
return
}
const taskIndex = collaborationProcess.findIndex((t) => t.Id === updatedTask.Id)
if (taskIndex === -1) {
console.warn('⚠️ syncCurrentTaskToMainProcess: 未找到对应任务', updatedTask.Id)
return
}
// 使用 splice 确保 Vue 响应式更新
collaborationProcess.splice(taskIndex, 1, {
...collaborationProcess[taskIndex]!,
AgentSelection: updatedTask.AgentSelection || [],
TaskProcess: updatedTask.TaskProcess || [],
Collaboration_Brief_frontEnd: updatedTask.Collaboration_Brief_frontEnd,
})
console.log('✅ syncCurrentTaskToMainProcess: 已同步到主流程', {
taskName: collaborationProcess[taskIndex]!.StepName,
agentSelection: collaborationProcess[taskIndex]!.AgentSelection,
taskProcessLength: collaborationProcess[taskIndex]!.TaskProcess?.length || 0,
})
}
// 🆕 设置当前任务的 TaskProcess 数据(用于切换分支时更新显示)
function setCurrentTaskProcess(taskProcess: TaskProcess[]) {
if (currentTask.value) {
// 创建新的对象引用,确保响应式更新
currentTask.value = {
...currentTask.value,
TaskProcess: JSON.parse(JSON.stringify(taskProcess)),
}
// 🆕 同步到主流程数据(让执行结果卡片和任务大纲都能联动)
syncCurrentTaskToMainProcess(currentTask.value)
console.log('✅ 已更新 currentTask.TaskProcess 并同步到主流程:', {
taskId: currentTask.value.Id,
agent数量: taskProcess.length,
})
}
}
// 🆕 更新当前任务的 AgentSelection 和 TaskProcess用于在 AgentAllocation 中切换 agent 组合)
// 此函数专门用于强制更新,不会被 setCurrentTask 的"智能保留"逻辑阻止
function updateCurrentAgentSelection(
agentSelection: string[],
taskProcess: TaskProcess[],
collaborationBrief: any,
) {
if (currentTask.value) {
// 直接更新 currentTask不保留旧数据
currentTask.value = {
...currentTask.value,
AgentSelection: agentSelection,
TaskProcess: taskProcess,
Collaboration_Brief_frontEnd: collaborationBrief,
}
// 同步到主流程数据
syncCurrentTaskToMainProcess(currentTask.value)
console.log('✅ 已强制更新 currentTask 的 AgentSelection 并同步到主流程:', {
taskId: currentTask.value.Id,
taskName: currentTask.value.StepName,
newAgentSelection: agentSelection,
taskProcessLength: taskProcess.length,
})
}
}
const agentRawPlan = ref<{ data?: IRawPlanResponse; loading?: boolean }>({ loading: false })
function setAgentRawPlan(plan: { data?: IRawPlanResponse; loading?: boolean }) {
if (plan.data) {
plan.data['Collaboration Process'] = plan.data['Collaboration Process']?.map((item) => ({
...item,
// ✅ 只在任务没有 Id 时才生成新的 ID保持已存在任务的 Id 不变
Id: item.Id || uuidv4(),
}))
}
agentRawPlan.value = {
...agentRawPlan.value,
...plan,
}
}
// 执行完任务的结果
const executePlan = ref<IExecuteRawResponse[]>([])
function setExecutePlan(plan: IExecuteRawResponse[]) {
executePlan.value = plan
}
function resetAgent() {
agentRawPlan.value = {
loading: false,
}
currentTask.value = undefined
executePlan.value = []
}
// 额外的产物列表
const additionalOutputs = ref<string[]>([])
// 用户在 Assignment 中选择的 agent 组合按任务ID分别存储
const selectedAgentGroupMap = ref<Map<string, string[]>>(new Map())
// 设置指定任务的选择的 agent 组合
function setSelectedAgentGroup(taskId: string, agents: string[]) {
selectedAgentGroupMap.value.set(taskId, agents)
console.log('💾 保存任务选择的 agent 组合:', { taskId, agents })
}
// 获取指定任务的选择的 agent 组合
function getSelectedAgentGroup(taskId: string): string[] | undefined {
return selectedAgentGroupMap.value.get(taskId)
}
// 清除指定任务的选择的 agent 组合
function clearSelectedAgentGroup(taskId: string) {
selectedAgentGroupMap.value.delete(taskId)
}
// 清除所有任务的选择的 agent 组合
function clearAllSelectedAgentGroups() {
selectedAgentGroupMap.value.clear()
}
// 添加新产物
function addNewOutput(outputObject: string) {
if (!outputObject.trim()) return false
const trimmedOutput = outputObject.trim()
if (!additionalOutputs.value.includes(trimmedOutput)) {
additionalOutputs.value.unshift(trimmedOutput)
return true
}
return false
}
// 删除额外产物
function removeAdditionalOutput(outputObject: string) {
const index = additionalOutputs.value.indexOf(outputObject)
if (index > -1) {
additionalOutputs.value.splice(index, 1)
return true
}
return false
}
// 清除额外产物
function clearAdditionalOutputs() {
additionalOutputs.value = []
}
return {
agents,
setAgents,
searchValue,
setSearchValue,
currentTask,
setCurrentTask,
setCurrentTaskProcess, // 🆕 设置当前任务的 TaskProcess
updateCurrentAgentSelection, // 🆕 强制更新 AgentSelection用于 AgentAllocation 切换组合)
syncCurrentTaskToMainProcess, // 🆕 同步 currentTask 到主流程
agentRawPlan,
setAgentRawPlan,
executePlan,
setExecutePlan,
resetAgent,
additionalOutputs,
addNewOutput,
removeAdditionalOutput,
clearAdditionalOutputs,
// 用户选择的 agent 组合
selectedAgentGroupMap,
setSelectedAgentGroup,
getSelectedAgentGroup,
clearSelectedAgentGroup,
clearAllSelectedAgentGroups,
planModificationWindow,
openPlanModification,
closePlanModification,
planTaskWindow,
openPlanTask,
closePlanTask,
agentAllocationDialog,
openAgentAllocationDialog,
closeAgentAllocationDialog,
// 智能体评分数据
IAgentSelectModifyAddRequest,
setAgentScoreData,
addAgentScoreAspect,
clearAgentScoreData,
// 确认的agent组合列表按任务ID分别存储
confirmedAgentGroupsMap,
getConfirmedAgentGroups,
addConfirmedAgentGroup,
clearConfirmedAgentGroups,
clearAllConfirmedAgentGroups,
}
})
/**
* 用于在组件外部如在Pinia Store 中)使用 Pinia 提供的 store 实例。
* 官方文档解释了如何在组件外部使用 Pinia Store
* https://pinia.vuejs.org/core-concepts/outside-component-usage.html#using-a-store-outside-of-a-component
*/
export function useAgentsStoreHook() {
return useAgentsStore(store)
}