Files
AgentCoord/frontend/src/stores/modules/agents.ts
2026-01-26 16:46:58 +08:00

612 lines
18 KiB
TypeScript
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.
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 ITaskScoreData {
aspectList: string[] // 维度列表
agentScores: Record<string, Record<string, IScoreItem>> // agent -> 维度 -> 评分
timestamp: number // 缓存时间戳
}
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
}
// 🆕 新的按任务ID存储的评分数据
const taskScoreDataMap = useStorage<Record<string, ITaskScoreData>>(
`${storageKey}-task-score-data`,
{},
)
// 🆕 预加载状态追踪(用于避免重复预加载)
const preloadingTaskIds = ref<Set<string>>(new Set())
// 🆕 获取指定任务的评分数据按任务ID获取
function getTaskScoreData(taskId: string): IAgentSelectModifyAddRequest | null {
if (!taskId) {
console.warn('⚠️ getTaskScoreData: taskId 为空')
return null
}
const taskScoreData = taskScoreDataMap.value[taskId]
if (taskScoreData) {
console.log(`✅ 使用任务 ${taskId} 的缓存评分数据,维度数: ${taskScoreData.aspectList.length}`)
return {
aspectList: taskScoreData.aspectList,
agentScores: taskScoreData.agentScores,
}
}
console.log(` 任务 ${taskId} 没有缓存的评分数据`)
return null
}
// 🆕 设置指定任务的评分数据
function setTaskScoreData(taskId: string, data: IAgentSelectModifyAddRequest) {
if (!taskId) {
console.warn('⚠️ setTaskScoreData: taskId 为空,无法存储')
return
}
taskScoreDataMap.value = {
...taskScoreDataMap.value,
[taskId]: {
...data,
timestamp: Date.now(),
},
}
console.log(`✅ 任务 ${taskId} 的评分数据已存储`, {
aspectCount: data.aspectList.length,
agentCount: Object.keys(data.agentScores).length,
})
}
// 🆕 检查指定任务是否有评分数据
function hasTaskScoreData(taskId: string): boolean {
if (!taskId) return false
return !!taskScoreDataMap.value[taskId]?.aspectList?.length
}
// 🆕 获取所有已预加载的任务ID列表
function getPreloadedTaskIds(): string[] {
return Object.keys(taskScoreDataMap.value)
}
// 🆕 清除指定任务的评分数据
function clearTaskScoreData(taskId: string) {
if (taskId && taskScoreDataMap.value[taskId]) {
const newMap = { ...taskScoreDataMap.value }
delete newMap[taskId]
taskScoreDataMap.value = newMap
console.log(`✅ 任务 ${taskId} 的评分数据已清除`)
}
}
// 🆕 清除所有任务的评分数据
function clearAllTaskScoreData() {
taskScoreDataMap.value = {}
preloadingTaskIds.value.clear()
console.log('✅ 所有任务的评分数据已清除')
}
// 🆕 标记任务正在预加载中
function markTaskPreloading(taskId: string) {
preloadingTaskIds.value.add(taskId)
}
// 🆕 标记任务预加载完成
function markTaskPreloadComplete(taskId: string) {
preloadingTaskIds.value.delete(taskId)
}
// 🆕 检查任务是否正在预加载
function isTaskPreloading(taskId: string): boolean {
return preloadingTaskIds.value.has(taskId)
}
// 兼容旧版本:全局评分数据存储(单任务模式,已废弃,保留用于兼容)
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)
}
// 添加该维度的评分数据
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>()
// 记录用户修改过的步骤索引(用于重新执行)
const modifiedSteps = ref<Set<number>>(new Set())
function addModifiedStep(stepIndex: number) {
modifiedSteps.value.add(stepIndex)
}
function clearModifiedSteps() {
modifiedSteps.value.clear()
}
function hasModifiedSteps() {
return modifiedSteps.value.size > 0
}
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 = []
hasStoppedFilling.value = false
}
// 额外的产物列表
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 = []
}
// 标记是否用户已停止智能体分配过程
const hasStoppedFilling = ref(false)
// 设置停止状态
function setHasStoppedFilling(value: boolean) {
hasStoppedFilling.value = 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,
// 🆕 按任务ID存储的评分数据
taskScoreDataMap,
getTaskScoreData,
setTaskScoreData,
hasTaskScoreData,
getPreloadedTaskIds,
clearTaskScoreData,
clearAllTaskScoreData,
preloadingTaskIds,
markTaskPreloading,
markTaskPreloadComplete,
isTaskPreloading,
// 确认的agent组合列表按任务ID分别存储
confirmedAgentGroupsMap,
getConfirmedAgentGroups,
addConfirmedAgentGroup,
clearConfirmedAgentGroups,
clearAllConfirmedAgentGroups,
// 停止填充状态
hasStoppedFilling,
setHasStoppedFilling,
// 重新执行相关
modifiedSteps,
addModifiedStep,
clearModifiedSteps,
hasModifiedSteps,
}
})
/**
* 用于在组件外部如在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)
}