diff --git a/backend/consts/model.py b/backend/consts/model.py index 2b4903c33..6bf368d7b 100644 --- a/backend/consts/model.py +++ b/backend/consts/model.py @@ -587,6 +587,12 @@ class AgentSkillInstanceRequest(BaseModel): # used in agent/search agent/update for save agent info +class RelatedAgentInfo(BaseModel): + """Related agent info with pinned version.""" + agent_id: int + version_no: Optional[int] = None + + class AgentInfoRequest(BaseModel): agent_id: Optional[int] = None name: Optional[str] = None @@ -611,6 +617,7 @@ class AgentInfoRequest(BaseModel): enabled_skill_ids: Optional[List[int]] = None skill_instances: Optional[List[AgentSkillInstanceRequest]] = None related_agent_ids: Optional[List[int]] = None + related_agents: Optional[List[RelatedAgentInfo]] = None # Related agents with pinned versions related_external_agent_ids: Optional[List[int]] = None group_ids: Optional[List[int]] = None ingroup_permission: Optional[str] = None diff --git a/backend/database/agent_db.py b/backend/database/agent_db.py index df7410115..ecdcb6336 100644 --- a/backend/database/agent_db.py +++ b/backend/database/agent_db.py @@ -339,7 +339,32 @@ def query_all_agent_info_by_tenant_id(tenant_id: str, version_no: int = 0): return [as_dict(agent) for agent in agents] -def insert_related_agent(parent_agent_id: int, child_agent_id: int, tenant_id: str, user_id: str, version_no: int = 0) -> bool: +def batch_search_agent_display_names(agent_ids: List[int], tenant_id: str) -> dict: + """ + Batch query agent display names by agent IDs. + Returns a dict mapping agent_id -> display_name (falls back to name). + + Args: + agent_ids: List of agent IDs to query + tenant_id: Tenant ID + """ + if not agent_ids: + return {} + with get_db_session() as session: + agents = session.query( + AgentInfo.agent_id, + AgentInfo.display_name, + AgentInfo.name + ).filter( + AgentInfo.agent_id.in_(agent_ids), + AgentInfo.tenant_id == tenant_id, + AgentInfo.version_no == 0, + AgentInfo.delete_flag != 'Y' + ).all() + return {a.agent_id: (a.display_name or a.name) for a in agents} + + +def insert_related_agent(parent_agent_id: int, child_agent_id: int, tenant_id: str, user_id: str, version_no: int = 0, selected_agent_version_no: Optional[int] = None) -> bool: """ Insert a related agent. Default version_no=0 creates the draft version. @@ -349,7 +374,8 @@ def insert_related_agent(parent_agent_id: int, child_agent_id: int, tenant_id: s child_agent_id: Child agent ID tenant_id: Tenant ID user_id: User ID - version_no: Version number. Default 0 = draft/editing state + version_no: Parent agent version number. Default 0 = draft/editing state + selected_agent_version_no: Pinned version of child agent. None = runtime fallback to child current_version_no """ try: relation_info = { @@ -357,6 +383,7 @@ def insert_related_agent(parent_agent_id: int, child_agent_id: int, tenant_id: s "selected_agent_id": child_agent_id, "tenant_id": tenant_id, "version_no": version_no, + "selected_agent_version_no": selected_agent_version_no, "created_by": user_id, "updated_by": user_id } @@ -398,22 +425,77 @@ def delete_related_agent(parent_agent_id: int, child_agent_id: int, tenant_id: s return False -def update_related_agents(parent_agent_id: int, related_agent_ids: List[int], tenant_id: str, user_id: str, version_no: int = 0): +def _parse_related_agents(related_agents: Optional[List[dict]]) -> tuple: + """Extract agent_id set and version_map from related_agents list.""" + new_related_ids: set = set() + version_map: dict = {} + if not related_agents: + return new_related_ids, version_map + for rel in related_agents: + agent_id = rel.get("agent_id") + if agent_id is None: + continue + new_related_ids.add(agent_id) + version_no_val = rel.get("version_no") + if version_no_val is not None: + version_map[agent_id] = version_no_val + return new_related_ids, version_map + + +def _add_new_relations(session, parent_agent_id, tenant_id, user_id, version_no, ids_to_add, version_map): + """Insert new agent relations into the database.""" + for child_agent_id in ids_to_add: + relation_info = { + "parent_agent_id": parent_agent_id, + "selected_agent_id": child_agent_id, + "tenant_id": tenant_id, + "version_no": version_no, + "created_by": user_id, + "updated_by": user_id, + } + if child_agent_id in version_map: + relation_info["selected_agent_version_no"] = version_map[child_agent_id] + new_relation = AgentRelation(**filter_property(relation_info, AgentRelation)) + session.add(new_relation) + + +def _update_existing_relations(current_relations, ids_to_update, version_map, user_id): + """Update version_no for existing relations.""" + if not ids_to_update or not version_map: + return + for rel in current_relations: + if rel.selected_agent_id not in ids_to_update: + continue + new_version_no = version_map.get(rel.selected_agent_id) + if new_version_no is not None: + rel.selected_agent_version_no = new_version_no + rel.updated_by = user_id + + +def update_related_agents( + parent_agent_id: int, + tenant_id: str, + user_id: str, + related_agents: Optional[List[dict]] = None, + version_no: int = 0, +): """ Update related agents for a parent agent by replacing all existing relations. Default version_no=0 updates the draft version. This function handles both creation and deletion of relations in a single transaction. + related_agents is the single source of truth: each item has 'agent_id' and optional 'version_no'. Args: parent_agent_id: ID of the parent agent - related_agent_ids: List of child agent IDs to be related tenant_id: Tenant ID user_id: User ID for audit trail + related_agents: List of dicts with 'agent_id' and optional 'version_no' keys version_no: Version number to filter. Default 0 = draft/editing state """ + new_related_ids, version_map = _parse_related_agents(related_agents) + with get_db_session() as session: - # Get current relations current_relations = session.query(AgentRelation).filter( AgentRelation.parent_agent_id == parent_agent_id, AgentRelation.tenant_id == tenant_id, @@ -421,17 +503,12 @@ def update_related_agents(parent_agent_id: int, related_agent_ids: List[int], te AgentRelation.delete_flag != 'Y' ).all() - current_related_ids = { - rel.selected_agent_id for rel in current_relations} - new_related_ids = set( - related_agent_ids) if related_agent_ids else set() + current_related_ids = {rel.selected_agent_id for rel in current_relations} - # Find IDs to delete (in current but not in new) ids_to_delete = current_related_ids - new_related_ids - # Find IDs to add (in new but not in current) ids_to_add = new_related_ids - current_related_ids + ids_to_update = current_related_ids & new_related_ids - # Soft delete removed relations if ids_to_delete: session.query(AgentRelation).filter( AgentRelation.parent_agent_id == parent_agent_id, @@ -443,19 +520,11 @@ def update_related_agents(parent_agent_id: int, related_agent_ids: List[int], te synchronize_session=False ) - # Add new relations - for child_agent_id in ids_to_add: - relation_info = { - "parent_agent_id": parent_agent_id, - "selected_agent_id": child_agent_id, - "tenant_id": tenant_id, - "version_no": version_no, - "created_by": user_id, - "updated_by": user_id - } - new_relation = AgentRelation( - **filter_property(relation_info, AgentRelation)) - session.add(new_relation) + _add_new_relations( + session, parent_agent_id, tenant_id, user_id, version_no, ids_to_add, version_map + ) + + _update_existing_relations(current_relations, ids_to_update, version_map, user_id) def delete_agent_relationship(agent_id: int, tenant_id: str, user_id: str, version_no: int = 0): diff --git a/backend/database/agent_version_db.py b/backend/database/agent_version_db.py index c0785c527..391e5d5e8 100644 --- a/backend/database/agent_version_db.py +++ b/backend/database/agent_version_db.py @@ -35,6 +35,36 @@ def search_version_by_version_no( return as_dict(version) if version else None +def batch_search_version_names( + agent_ids: List[int], + tenant_id: str, + version_nos: List[int], +) -> List[dict]: + """ + Batch query version names for multiple (agent_id, version_no) pairs. + + Returns list of dicts: [{"agent_id": int, "version_no": int, "version_name": Optional[str]}] + """ + if not agent_ids or not version_nos: + return [] + + with get_db_session() as session: + versions = session.query(AgentVersion).filter( + AgentVersion.agent_id.in_(agent_ids), + AgentVersion.version_no.in_(version_nos), + AgentVersion.tenant_id == tenant_id, + ).all() + + result = [] + for v in versions: + result.append({ + "agent_id": v.agent_id, + "version_no": v.version_no, + "version_name": v.version_name, + }) + return result + + def search_version_by_id( version_id: int, tenant_id: str, @@ -87,6 +117,42 @@ def query_current_version_no( return agent.current_version_no if agent else None +def batch_query_current_version_nos( + agent_ids: List[int], + tenant_id: str, +) -> dict: + """ + Batch query current published version_no for multiple agents. + + Returns a dict mapping agent_id -> current_version_no (only includes agents + that have a non-null current_version_no). + + Args: + agent_ids: List of agent IDs to query + tenant_id: Tenant ID + """ + if not agent_ids: + return {} + with get_db_session() as session: + agents = session.query( + AgentInfo.agent_id, + AgentInfo.current_version_no, + ).filter( + AgentInfo.agent_id.in_(agent_ids), + or_( + AgentInfo.tenant_id == tenant_id, + AgentInfo.tenant_id == ASSET_OWNER_TENANT_ID, + ), + AgentInfo.version_no == 0, + AgentInfo.delete_flag == 'N', + ).all() + return { + a.agent_id: a.current_version_no + for a in agents + if a.current_version_no is not None + } + + def query_agent_snapshot( agent_id: int, tenant_id: str, diff --git a/backend/database/db_models.py b/backend/database/db_models.py index 190b19694..3a6a27fa9 100644 --- a/backend/database/db_models.py +++ b/backend/database/db_models.py @@ -1074,18 +1074,19 @@ class UserTenant(TableBase): class AgentRelation(TableBase): """ Agent parent-child relationship table + Primary key: (relation_id, version_no) """ __tablename__ = "ag_agent_relation_t" __table_args__ = {"schema": SCHEMA} relation_id = Column(Integer, Sequence("ag_agent_relation_t_relation_id_seq", schema=SCHEMA), primary_key=True, nullable=False, doc="Relationship ID, primary key") + version_no = Column(Integer, primary_key=True, default=0, nullable=False, + doc="Version number. 0 = draft/editing state, >=1 = published snapshot") selected_agent_id = Column( - Integer, primary_key=True, doc="Selected agent ID") + Integer, doc="Selected agent ID") parent_agent_id = Column(Integer, doc="Parent agent ID") tenant_id = Column(String(100), doc="Tenant ID") - version_no = Column(Integer, default=0, nullable=False, - doc="Version number. 0 = draft/editing state, >=1 = published snapshot") selected_agent_version_no = Column( Integer, nullable=True, doc="Pinned version of selected_agent_id. NULL = runtime fallback to child current_version_no", diff --git a/backend/services/agent_service.py b/backend/services/agent_service.py index abf6026f1..240ea4367 100644 --- a/backend/services/agent_service.py +++ b/backend/services/agent_service.py @@ -42,6 +42,7 @@ ) from services.asset_owner_visibility import resolve_agent_list_permission from database.agent_db import ( + batch_search_agent_display_names, create_agent, delete_agent_by_id, delete_agent_relationship, @@ -80,7 +81,7 @@ from database.attachment_db import upload_fileobj from services.skill_service import SkillService from services.file_management_service import is_allowed_skill_upload_path -from database.agent_version_db import query_version_list, query_current_version_no +from database.agent_version_db import query_version_list, query_current_version_no, batch_search_version_names, batch_query_current_version_nos from database.group_db import query_group_ids_by_user from database.user_tenant_db import get_user_tenant_by_user_id from database.a2a_agent_db import get_server_agent_ids, query_external_sub_agents @@ -1487,9 +1488,80 @@ async def get_agent_info_impl(agent_id: int, tenant_id: str, version_no: int = 0 sub_agent_id_list = query_sub_agents_id_list( main_agent_id=agent_id, tenant_id=tenant_id) agent_info["sub_agent_id_list"] = sub_agent_id_list + + # Enrich sub-agent relations with version names (batch query) + relations = query_sub_agent_relations(agent_id, tenant_id, version_no) + enriched_relations = [] + + # Collect all agent_ids and (agent_id, version_no) pairs for batch lookup + all_agent_ids = set() + lookup_agent_ids = set() + lookup_version_nos = set() + # Track agents whose pinned version_no is null -> need to resolve latest published version + missing_version_agent_ids = set() + for rel in relations: + aid = rel.get("selected_agent_id") + if aid: + all_agent_ids.add(aid) + vno = rel.get("selected_agent_version_no") + if aid and vno is not None and vno != 0: + lookup_agent_ids.add(aid) + lookup_version_nos.add(vno) + elif aid: + # Historical data: pinned version_no is null or 0 (draft), resolve from child's current published version + missing_version_agent_ids.add(aid) + + # Batch query current published version_no for agents with missing pinned version + resolved_version_no_map: dict = {} + if missing_version_agent_ids: + resolved_version_no_map = batch_query_current_version_nos( + agent_ids=list(missing_version_agent_ids), + tenant_id=tenant_id, + ) + # Merge resolved version_nos into the version name lookup set + for aid, resolved_vno in resolved_version_no_map.items(): + lookup_agent_ids.add(aid) + lookup_version_nos.add(resolved_vno) + + # Batch query all version names at once + version_name_map: dict = {} + if lookup_agent_ids and lookup_version_nos: + batch_results = batch_search_version_names( + agent_ids=list(lookup_agent_ids), + tenant_id=tenant_id, + version_nos=list(lookup_version_nos), + ) + for item in batch_results: + key = (item["agent_id"], item["version_no"]) + version_name_map[key] = item["version_name"] + + # Batch query all agent display names at once + agent_name_map = batch_search_agent_display_names( + agent_ids=list(all_agent_ids), + tenant_id=tenant_id, + ) + + for rel in relations: + selected_agent_id = rel.get("selected_agent_id") + selected_version_no = rel.get("selected_agent_version_no") + # Fallback to resolved latest published version_no when pinned version is null or 0 (draft) + if (selected_version_no is None or selected_version_no == 0) and selected_agent_id in resolved_version_no_map: + selected_version_no = resolved_version_no_map[selected_agent_id] + version_name = None + if selected_agent_id and selected_version_no is not None: + version_name = version_name_map.get((selected_agent_id, selected_version_no)) + enriched_relations.append({ + "agent_id": selected_agent_id, + "agent_name": agent_name_map.get(selected_agent_id) if selected_agent_id else None, + "version_no": selected_version_no, + "version_name": version_name, + }) + + agent_info["sub_agent_relations"] = enriched_relations except Exception as e: logger.error(f"Failed to get sub agent id list: {str(e)}") agent_info["sub_agent_id_list"] = [] + agent_info["sub_agent_relations"] = [] try: skill_service = SkillService() @@ -1886,12 +1958,23 @@ async def update_agent_info_impl(request: AgentInfoRequest, authorization: str = main_agent_id=left_ele, tenant_id=tenant_id) search_list.extend(sub_ids) - # Update related agents + # Update related agents - use related_agents if provided, otherwise build from IDs + if request.related_agents: + related_agents_dicts = [ + {"agent_id": ra.agent_id, "version_no": ra.version_no} + for ra in request.related_agents + ] + else: + related_agents_dicts = [ + {"agent_id": aid, "version_no": None} + for aid in related_agent_ids + ] + update_related_agents( parent_agent_id=agent_id, - related_agent_ids=related_agent_ids, tenant_id=tenant_id, - user_id=user_id + user_id=user_id, + related_agents=related_agents_dicts, ) except ValueError: # Re-raise ValueError (circular dependency) as-is @@ -2287,12 +2370,13 @@ async def import_agent_impl( mapping_agent_id[need_import_agent_id] = new_agent_id agent_id_set.add(need_import_agent_id) - # Establish relationships with sub-agents + # Establish relationships with sub-agents - new sub-agents always use version 1 for sub_agent_id in managed_agents: insert_related_agent(parent_agent_id=mapping_agent_id[need_import_agent_id], child_agent_id=mapping_agent_id[sub_agent_id], tenant_id=tenant_id, - user_id=user_id) + user_id=user_id, + selected_agent_version_no=1) else: # Current agent still has sub-agents that haven't been imported agent_stack.append(need_import_agent_id) diff --git a/backend/services/agent_version_service.py b/backend/services/agent_version_service.py index fdaf7e89a..685d8f89a 100644 --- a/backend/services/agent_version_service.py +++ b/backend/services/agent_version_service.py @@ -825,7 +825,7 @@ async def list_published_agents_impl( _apply_duplicate_name_availability_rules, ) from services.asset_owner_visibility import resolve_agent_list_permission - from database.agent_version_db import query_agent_snapshot + from database.agent_version_db import query_agent_snapshot, query_version_list # Get user role for permission check user_tenant_record = get_user_tenant_by_user_id(user_id) or {} @@ -868,6 +868,21 @@ async def list_published_agents_impl( if not current_version_no or current_version_no <= 0: continue + # Verify current_version_no exists, if not find the latest available version + available_versions = query_version_list(agent_id=agent_id, tenant_id=tenant_id) + + if not available_versions: + logger.warning(f"No available versions found for agent_id={agent_id}") + continue + + available_version_nos = {v["version_no"] for v in available_versions} + + if current_version_no not in available_version_nos: + logger.warning( + f"Current version {current_version_no} not found for agent_id={agent_id}, using latest available version" + ) + current_version_no = available_versions[0]["version_no"] + # Get the published version snapshot agent_snapshot, tools_snapshot, relations_snapshot = query_agent_snapshot( agent_id=agent_id, @@ -889,6 +904,11 @@ async def list_published_agents_impl( if key != 'current_version_no': agent_info[key] = value + # Add version_name from version metadata + current_version_info = next((v for v in available_versions if v["version_no"] == current_version_no), None) + if current_version_info: + agent_info['version_name'] = current_version_info.get("version_name") + # Add tools agent_info['tools'] = tools_snapshot @@ -966,6 +986,7 @@ async def list_published_agents_impl( "group_ids": agent.get("group_ids", []), "permission": permission, "current_version_no": agent.get("current_version_no"), + "version_name": agent.get("version_name"), "greeting_message": agent.get("greeting_message"), "example_questions": agent.get("example_questions"), }) diff --git a/frontend/app/[locale]/agents/components/agentConfig/CollaborativeAgent.tsx b/frontend/app/[locale]/agents/components/agentConfig/CollaborativeAgent.tsx index d3090b369..240523047 100644 --- a/frontend/app/[locale]/agents/components/agentConfig/CollaborativeAgent.tsx +++ b/frontend/app/[locale]/agents/components/agentConfig/CollaborativeAgent.tsx @@ -18,6 +18,7 @@ export default function CollaborativeAgent() { const isCreatingMode = useAgentConfigStore((state) => state.isCreatingMode); const editedAgent = useAgentConfigStore((state) => state.editedAgent); const updateSubAgentIds = useAgentConfigStore((state) => state.updateSubAgentIds); + const updateSubAgentRelations = useAgentConfigStore((state) => state.updateSubAgentRelations); const updateExternalSubAgentIds = useAgentConfigStore((state) => state.updateExternalSubAgentIds); const { availableAgents: internalAgents } = usePublishedAgentList(); @@ -40,11 +41,61 @@ export default function CollaborativeAgent() { // Related internal agent IDs const relatedAgentIds = Array.isArray(editedAgent?.sub_agent_id_list) ? editedAgent.sub_agent_id_list : []; - // Related internal agents (from published list) - const relatedInternalAgents = (Array.isArray(internalAgents) ? internalAgents : []).filter( - (agent: Agent) => relatedAgentIds.includes(Number(agent.id)) + // Map of agent_id -> saved version info (from snapshot) + const savedVersionMap = (() => { + const map: Record = {}; + (editedAgent?.sub_agent_relations || []).forEach((rel) => { + map[rel.agent_id] = { + agent_name: rel.agent_name, + version_no: rel.version_no, + version_name: rel.version_name, + }; + }); + return map; + })(); + + // Related internal agents - display all agents from sub_agent_id_list + // For agents found in internalAgents, use full info (name, version) + // For legacy agents not found, still display with fallback info + const publishedAgentMap = new Map( + (Array.isArray(internalAgents) ? internalAgents : []).map((a: Agent) => [Number(a.id), a]) ); + const relatedInternalAgents = relatedAgentIds.map((agentId: number) => { + const publishedAgent = publishedAgentMap.get(Number(agentId)); + const savedVersion = savedVersionMap[Number(agentId)]; + const hasSavedVersion = savedVersion?.version_no != null; + + if (publishedAgent) { + const version_name = (hasSavedVersion + ? (savedVersion?.version_name ?? publishedAgent.version_name) + : publishedAgent.version_name) ?? undefined; + const version_no = (hasSavedVersion + ? savedVersion?.version_no + : (publishedAgent as any).current_version_no) ?? undefined; + return { + ...publishedAgent, + version_name, + version_no, + }; + } + + // Legacy agent not found in published list - use name from sub_agent_relations + return { + id: String(agentId), + name: savedVersion?.agent_name || `Agent #${agentId}`, + display_name: savedVersion?.agent_name || `Agent #${agentId}`, + description: "", + model: "", + max_step: 0, + provide_run_summary: false, + tools: [], + skills: [], + version_name: savedVersion?.version_name ?? undefined, + version_no: savedVersion?.version_no ?? undefined, + } as Agent; + }); + // Available internal agents (exclude already related ones and current agent) const availableInternalAgents = (Array.isArray(internalAgents) ? internalAgents : []).filter( (agent: Agent) => !relatedAgentIds.includes(Number(agent.id)) && Number(agent.id) !== currentAgentId @@ -82,6 +133,18 @@ export default function CollaborativeAgent() { const handleSelectInternalAgent = (agentId: number) => { const newRelatedAgentIds = [...(Array.isArray(relatedAgentIds) ? relatedAgentIds : []), agentId]; updateSubAgentIds(newRelatedAgentIds); + + // Sync sub_agent_relations: add new agent with its current published version_no + const selectedAgent = (Array.isArray(internalAgents) ? internalAgents : []).find( + (a: Agent) => Number(a.id) === agentId + ); + const currentVersionNo = selectedAgent?.current_version_no ?? null; + const existingRelations = editedAgent?.sub_agent_relations || []; + const newRelations = [ + ...existingRelations, + { agent_id: agentId, version_no: currentVersionNo, version_name: selectedAgent?.version_name }, + ]; + updateSubAgentRelations(newRelations); }; // Add external agent @@ -108,6 +171,13 @@ export default function CollaborativeAgent() { (id: number) => id !== agentId ); updateSubAgentIds(newRelatedAgentIds); + + // Sync sub_agent_relations: remove the agent + const existingRelations = editedAgent?.sub_agent_relations || []; + const newRelations = existingRelations.filter( + (rel: { agent_id: number; version_no: number | null; version_name?: string }) => rel.agent_id !== agentId + ); + updateSubAgentRelations(newRelations); }; // Remove external agent @@ -129,35 +199,67 @@ export default function CollaborativeAgent() { }; // Unified dropdown menu items - const dropdownMenuItems = [ - // Internal agents group - { - key: "internal", - type: "group" as const, - label: t("collaborativeAgent.internalAgents"), - children: availableInternalAgents.map((agent: Agent) => ({ - key: `internal-${agent.id}`, - label: agent.display_name || agent.name, - onClick: () => handleSelectInternalAgent(Number(agent.id)), - })), - }, - // External A2A agents group - { - key: "external", - type: "group" as const, - label: t("collaborativeAgent.externalAgents"), - children: availableExternalForSelection.map((agent: A2AExternalAgent) => ({ - key: `external-${agent.id}`, + const internalMenuItems = availableInternalAgents.map((agent: Agent) => ({ + key: `internal-${agent.id}`, + label: ( +
+ {agent.display_name || agent.name} + {agent.version_name && ( + {agent.version_name} + )} +
+ ), + onClick: () => handleSelectInternalAgent(Number(agent.id)), + })); + + const externalMenuItems = availableExternalForSelection.map((agent: A2AExternalAgent) => ({ + key: `external-${agent.id}`, + label: ( +
+ + + {agent.name} + + {agent.version && ( + v{agent.version} + )} +
+ ), + onClick: () => handleSelectExternalAgent(agent.id), + })); + + const hasInternal = internalMenuItems.length > 0; + const hasExternal = externalMenuItems.length > 0; + + const dropdownMenuItems = (() => { + if (!hasInternal && !hasExternal) { + return [{ + key: "no-agents", + disabled: true, label: ( - - - {agent.name} - + {t("collaborativeAgent.noAgents")} ), - onClick: () => handleSelectExternalAgent(agent.id), - })), - }, - ]; + }]; + } + const items: any[] = []; + if (hasInternal) { + items.push({ + key: "internal", + type: "group" as const, + label: t("collaborativeAgent.internalAgents"), + children: internalMenuItems, + }); + } + if (hasExternal) { + items.push({ + key: "external", + type: "group" as const, + label: t("collaborativeAgent.externalAgents"), + children: externalMenuItems, + }); + } + return items; + })(); return ( <> @@ -169,6 +271,7 @@ export default function CollaborativeAgent() { menu={{ items: dropdownMenuItems }} disabled={isReadOnly} trigger={["click"]} + styles={{ root: { maxHeight: '400px', overflowY: 'auto' } }} >