/* ┌──────────────────────────────────────────────────────────────────────────────┐ │ @author: Davidson Gomes │ │ @file: /app/agents/config/TaskAgentConfig.tsx │ │ Developed by: Davidson Gomes │ │ Creation date: May 13, 2025 │ │ Contact: contato@evolution-api.com │ ├──────────────────────────────────────────────────────────────────────────────┤ │ @copyright © Evolution API 2025. All rights reserved. │ │ Licensed under the Apache License, Version 2.0 │ │ │ │ You may not use this file except in compliance with the License. │ │ You may obtain a copy of the License at │ │ │ │ http://www.apache.org/licenses/LICENSE-2.0 │ │ │ │ Unless required by applicable law or agreed to in writing, software │ │ distributed under the License is distributed on an "AS IS" BASIS, │ │ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ │ See the License for the specific language governing permissions and │ │ limitations under the License. │ ├──────────────────────────────────────────────────────────────────────────────┤ │ @important │ │ For any future changes to the code in this file, it is recommended to │ │ include, together with the modification, the information of the developer │ │ who changed it and the date of modification. │ └──────────────────────────────────────────────────────────────────────────────┘ */ "use client"; import { Agent, TaskConfig } from "@/types/agent"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { Maximize2, Save, X, ArrowDown, List, Search, Edit, PenTool, } from "lucide-react"; import { useState, useEffect } from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Checkbox } from "@/components/ui/checkbox"; interface TaskAgentConfigProps { values: Partial; onChange: (values: Partial) => void; agents: Agent[]; getAgentNameById: (id: string) => string; singleTask?: boolean; } const getAgentTypeLabel = (type: string): string => { const typeMap: Record = { llm: "LLM", a2a: "A2A", sequential: "Sequential", parallel: "Parallel", loop: "Loop", workflow: "Workflow", task: "Task", }; return typeMap[type] || type; }; const getAgentTypeColor = (type: string): string => { const colorMap: Record = { llm: "bg-blue-800 text-white", a2a: "bg-purple-800 text-white", sequential: "bg-orange-800 text-white", parallel: "bg-green-800 text-white", loop: "bg-pink-800 text-white", workflow: "bg-yellow-800 text-black", task: "bg-green-800 text-white", }; return colorMap[type] || "bg-neutral-800 text-white"; }; export function TaskAgentConfig({ values, onChange, agents, getAgentNameById, singleTask = false, }: TaskAgentConfigProps) { const [newTask, setNewTask] = useState({ agent_id: "", description: "", expected_output: "", enabled_tools: [], }); const [taskAgentSearchQuery, setTaskAgentSearchQuery] = useState(""); const [filteredTaskAgents, setFilteredTaskAgents] = useState([]); const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState(false); const [expandedDescription, setExpandedDescription] = useState(""); const [editingTaskIndex, setEditingTaskIndex] = useState(null); const [isEditing, setIsEditing] = useState(false); const [toolSearchQuery, setToolSearchQuery] = useState(""); const [filteredTools, setFilteredTools] = useState<{id: string, name: string}[]>([]); const [isToolsModalOpen, setIsToolsModalOpen] = useState(false); const [tempSelectedTools, setTempSelectedTools] = useState([]); useEffect(() => { if (isToolsModalOpen) { if (isEditing && editingTaskIndex !== null && values.config?.tasks) { setTempSelectedTools( values.config.tasks[editingTaskIndex]?.enabled_tools || [] ); } else { setTempSelectedTools([...newTask.enabled_tools || []]); } } }, [isToolsModalOpen]); const getAvailableTaskAgents = (currentTaskAgentId?: string) => agents.filter( (agent) => agent.id !== values.id && (!values.config?.tasks?.some((task) => task.agent_id === agent.id) || agent.id === currentTaskAgentId) ); useEffect(() => { const currentTaskAgentId = isEditing && editingTaskIndex !== null && values.config?.tasks ? values.config.tasks[editingTaskIndex].agent_id : undefined; const availableAgents = getAvailableTaskAgents(currentTaskAgentId); if (taskAgentSearchQuery.trim() === "") { setFilteredTaskAgents(availableAgents); } else { const query = taskAgentSearchQuery.toLowerCase(); setFilteredTaskAgents( availableAgents.filter( (agent) => agent.name.toLowerCase().includes(query) || (agent.description?.toLowerCase() || "").includes(query) ) ); } }, [ taskAgentSearchQuery, agents, values.config?.tasks, isEditing, editingTaskIndex, ]); useEffect(() => { // Reset editing state when values change externally if (!isEditing) { const currentTaskAgentId = editingTaskIndex !== null && values.config?.tasks ? values.config.tasks[editingTaskIndex]?.agent_id : undefined; setFilteredTaskAgents(getAvailableTaskAgents(currentTaskAgentId)); } }, [agents, values.config?.tasks]); const getAvailableTools = () => { if (!values.config?.tasks || values.config.tasks.length === 0) { return []; } const taskAgentIds = values.config.tasks.map(task => task.agent_id); const toolsList: {id: string, name: string}[] = []; const toolsMap: Record = {}; taskAgentIds.forEach(agentId => { const agent = agents.find(a => a.id === agentId); if (agent?.type === "llm" && agent.config?.tools) { agent.config.tools.forEach(tool => { if (!toolsMap[tool.id]) { toolsList.push({ id: tool.id, name: tool.id }); toolsMap[tool.id] = true; } }); } if (agent?.type === "llm" && agent.config?.mcp_servers) { agent.config.mcp_servers.forEach(mcp => { if (mcp.tools) { mcp.tools.forEach(toolId => { if (!toolsMap[toolId]) { toolsList.push({ id: toolId, name: toolId }); toolsMap[toolId] = true; } }); } }); } }); return toolsList; }; useEffect(() => { const availableTools = getAvailableTools(); if (toolSearchQuery.trim() === "") { setFilteredTools(availableTools); } else { const query = toolSearchQuery.toLowerCase(); setFilteredTools( availableTools.filter( (tool) => tool.name.toLowerCase().includes(query) || tool.id.toLowerCase().includes(query) ) ); } }, [toolSearchQuery, values.config?.tasks, agents]); const handleAddTask = () => { if (!newTask.agent_id || !newTask.description) { return; } if (isEditing && editingTaskIndex !== null) { const tasks = [...(values.config?.tasks || [])]; tasks[editingTaskIndex] = { ...newTask }; onChange({ ...values, config: { ...(values.config || {}), tasks, }, }); setIsEditing(false); setEditingTaskIndex(null); } else { const tasks = [...(values.config?.tasks || [])]; if (singleTask) { tasks.splice(0, tasks.length, newTask); } else { tasks.push(newTask); } onChange({ ...values, config: { ...(values.config || {}), tasks, }, }); } setNewTask({ agent_id: "", description: "", expected_output: "", enabled_tools: [], }); }; const handleEditTask = (index: number) => { const task = values.config?.tasks?.[index]; if (task) { setNewTask({ ...task }); setIsEditing(true); setEditingTaskIndex(index); } }; const handleCancelEdit = () => { setNewTask({ agent_id: "", description: "", expected_output: "", enabled_tools: [], }); setIsEditing(false); setEditingTaskIndex(null); }; const handleRemoveTask = (index: number) => { if (editingTaskIndex === index) { handleCancelEdit(); } const tasks = [...(values.config?.tasks || [])]; tasks.splice(index, 1); onChange({ ...values, config: { ...(values.config || {}), tasks, }, }); }; const handleDescriptionChange = ( e: React.ChangeEvent ) => { const newValue = e.target.value; setNewTask({ ...newTask, description: newValue, }); }; const handleExpandDescription = () => { setExpandedDescription(newTask.description); setIsDescriptionModalOpen(true); }; const handleSaveExpandedDescription = () => { setNewTask({ ...newTask, description: expandedDescription, }); setIsDescriptionModalOpen(false); }; const handleToggleTool = (toolId: string) => { const index = tempSelectedTools.indexOf(toolId); if (index > -1) { setTempSelectedTools(tempSelectedTools.filter(id => id !== toolId)); } else { setTempSelectedTools([...tempSelectedTools, toolId]); } }; const isToolEnabled = (toolId: string) => { return tempSelectedTools.includes(toolId); }; const handleSaveTools = () => { if (isEditing && editingTaskIndex !== null && values.config?.tasks) { const tasks = [...(values.config?.tasks || [])]; const updatedTask = { ...tasks[editingTaskIndex], enabled_tools: [...tempSelectedTools] }; tasks[editingTaskIndex] = updatedTask; const newConfig = { ...(values.config || {}), tasks: tasks }; onChange({ ...values, config: newConfig }); } else if (newTask.agent_id) { const updatedNewTask = { ...newTask, enabled_tools: [...tempSelectedTools] }; setNewTask(updatedNewTask); } setIsToolsModalOpen(false); }; const renderAgentTypeBadge = (agentId: string) => { const agent = agents.find((a) => a.id === agentId); if (!agent) { return null; } return ( {getAgentTypeLabel(agent.type)} ); }; return (

{singleTask ? "Task" : "Tasks"}

{singleTask ? "Configure the task that will be executed by the agent." : "Configure the sequential tasks that will be executed by the team of agents."}

{values.config?.tasks && values.config.tasks.length > 0 ? (
{values.config.tasks.map((task, index) => (
{index + 1}

{getAgentNameById(task.agent_id)} {renderAgentTypeBadge(task.agent_id)}

{task.description}

{task.expected_output && (
Expected output: {task.expected_output}
)} {task.enabled_tools && task.enabled_tools.length > 0 && (
Enabled tools:
{task.enabled_tools.map((toolId) => ( {toolId} ))}
)}
{!singleTask && index < (values.config?.tasks?.length || 0) - 1 && (
)}
))}
) : (

No tasks configured

{singleTask ? "Add a task to define the agent's behavior" : "Add tasks to define the workflow of the team"}

)} {(!singleTask || !values.config?.tasks || values.config.tasks.length === 0 || isEditing) && (

{isEditing ? "Edit task" : `Add ${singleTask ? "one" : "new"} task`} {isEditing && ( )}

setTaskAgentSearchQuery(e.target.value) } />
{filteredTaskAgents.length > 0 ? ( filteredTaskAgents.map((agent) => (
{agent.name} {getAgentTypeLabel(agent.type)}
)) ) : (
No agents found
)}