 f3cbb5c6f7
			
		
	
	f3cbb5c6f7
	
	
	
		
			
			- Parameterize CORS_ORIGINS in docker-compose.swarm.yml - Add .env.example with configuration options - Create comprehensive LOCAL_DEVELOPMENT.md guide - Update README.md with environment variable documentation - Provide alternatives for local development without production domain 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			277 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			277 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """
 | |
| CLI Agent Manager for Hive Backend
 | |
| Integrates CCLI agents with the Hive coordinator system.
 | |
| """
 | |
| 
 | |
| import asyncio
 | |
| import logging
 | |
| import sys
 | |
| import os
 | |
| from typing import Dict, Any, Optional
 | |
| from dataclasses import asdict
 | |
| 
 | |
| # Add CCLI source to path
 | |
| ccli_path = os.path.join(os.path.dirname(__file__), '../../../ccli_src')
 | |
| sys.path.insert(0, ccli_path)
 | |
| 
 | |
| from agents.gemini_cli_agent import GeminiCliAgent, GeminiCliConfig, TaskRequest as CliTaskRequest, TaskResult as CliTaskResult
 | |
| from agents.cli_agent_factory import CliAgentFactory
 | |
| 
 | |
| 
 | |
| class CliAgentManager:
 | |
|     """
 | |
|     Manages CLI agents within the Hive backend system
 | |
|     
 | |
|     Provides a bridge between the Hive coordinator and CCLI agents,
 | |
|     handling lifecycle management, task execution, and health monitoring.
 | |
|     """
 | |
|     
 | |
|     def __init__(self):
 | |
|         self.logger = logging.getLogger(__name__)
 | |
|         self.cli_factory = CliAgentFactory()
 | |
|         self.active_agents: Dict[str, GeminiCliAgent] = {}
 | |
|         self.is_initialized = False
 | |
|     
 | |
|     async def initialize(self):
 | |
|         """Initialize the CLI agent manager"""
 | |
|         try:
 | |
|             self.logger.info("Initializing CLI Agent Manager...")
 | |
|             
 | |
|             # Auto-register predefined CLI agents
 | |
|             await self._register_predefined_agents()
 | |
|             
 | |
|             self.is_initialized = True
 | |
|             self.logger.info("✅ CLI Agent Manager initialized")
 | |
|             
 | |
|         except Exception as e:
 | |
|             self.logger.error(f"❌ CLI Agent Manager initialization failed: {e}")
 | |
|             raise
 | |
|     
 | |
|     async def _register_predefined_agents(self):
 | |
|         """Register predefined CLI agents"""
 | |
|         predefined_agents = [
 | |
|             "walnut-gemini",
 | |
|             "ironwood-gemini"
 | |
|         ]
 | |
|         
 | |
|         for agent_id in predefined_agents:
 | |
|             try:
 | |
|                 agent = self.cli_factory.create_agent(agent_id)
 | |
|                 self.active_agents[agent_id] = agent
 | |
|                 
 | |
|                 # Test connectivity
 | |
|                 health = await agent.health_check()
 | |
|                 if health.get('cli_healthy', False):
 | |
|                     self.logger.info(f"✅ CLI agent {agent_id} registered and healthy")
 | |
|                 else:
 | |
|                     self.logger.warning(f"⚠️ CLI agent {agent_id} registered but not healthy")
 | |
|                     
 | |
|             except Exception as e:
 | |
|                 self.logger.error(f"❌ Failed to register CLI agent {agent_id}: {e}")
 | |
|     
 | |
|     def create_cli_agent(self, agent_id: str, config: Dict[str, Any]) -> GeminiCliAgent:
 | |
|         """Create a new CLI agent with custom configuration"""
 | |
|         try:
 | |
|             agent = self.cli_factory.create_agent(agent_id, config)
 | |
|             self.active_agents[agent_id] = agent
 | |
|             self.logger.info(f"Created CLI agent: {agent_id}")
 | |
|             return agent
 | |
|         except Exception as e:
 | |
|             self.logger.error(f"Failed to create CLI agent {agent_id}: {e}")
 | |
|             raise
 | |
|     
 | |
|     def get_cli_agent(self, agent_id: str) -> Optional[GeminiCliAgent]:
 | |
|         """Get a CLI agent by ID"""
 | |
|         return self.active_agents.get(agent_id)
 | |
|     
 | |
|     async def execute_cli_task(self, agent_id: str, hive_task: Any) -> Dict[str, Any]:
 | |
|         """
 | |
|         Execute a Hive task on a CLI agent
 | |
|         
 | |
|         Args:
 | |
|             agent_id: ID of the CLI agent
 | |
|             hive_task: Hive Task object
 | |
|             
 | |
|         Returns:
 | |
|             Dictionary with execution results compatible with Hive format
 | |
|         """
 | |
|         agent = self.get_cli_agent(agent_id)
 | |
|         if not agent:
 | |
|             raise ValueError(f"CLI agent {agent_id} not found")
 | |
|         
 | |
|         try:
 | |
|             # Convert Hive task to CLI task format
 | |
|             cli_task = self._convert_hive_task_to_cli(hive_task)
 | |
|             
 | |
|             # Execute on CLI agent
 | |
|             cli_result = await agent.execute_task(cli_task)
 | |
|             
 | |
|             # Convert CLI result back to Hive format
 | |
|             hive_result = self._convert_cli_result_to_hive(cli_result)
 | |
|             
 | |
|             self.logger.info(f"CLI task {cli_task.task_id} executed on {agent_id}: {cli_result.status.value}")
 | |
|             return hive_result
 | |
|             
 | |
|         except Exception as e:
 | |
|             self.logger.error(f"CLI task execution failed on {agent_id}: {e}")
 | |
|             return {
 | |
|                 "error": str(e),
 | |
|                 "status": "failed",
 | |
|                 "agent_id": agent_id
 | |
|             }
 | |
|     
 | |
|     def _convert_hive_task_to_cli(self, hive_task: Any) -> CliTaskRequest:
 | |
|         """Convert Hive Task to CLI TaskRequest"""
 | |
|         # Build prompt from Hive task context
 | |
|         context = hive_task.context
 | |
|         prompt_parts = []
 | |
|         
 | |
|         if 'objective' in context:
 | |
|             prompt_parts.append(f"Objective: {context['objective']}")
 | |
|         
 | |
|         if 'files' in context and context['files']:
 | |
|             prompt_parts.append(f"Related files: {', '.join(context['files'])}")
 | |
|         
 | |
|         if 'constraints' in context and context['constraints']:
 | |
|             prompt_parts.append(f"Constraints: {', '.join(context['constraints'])}")
 | |
|         
 | |
|         if 'requirements' in context and context['requirements']:
 | |
|             prompt_parts.append(f"Requirements: {', '.join(context['requirements'])}")
 | |
|         
 | |
|         # Join parts to create comprehensive prompt
 | |
|         prompt = "\n".join(prompt_parts) if prompt_parts else "General task execution"
 | |
|         
 | |
|         return CliTaskRequest(
 | |
|             prompt=prompt,
 | |
|             task_id=hive_task.id,
 | |
|             priority=hive_task.priority,
 | |
|             metadata={
 | |
|                 "hive_task_type": hive_task.type.value,
 | |
|                 "hive_context": context
 | |
|             }
 | |
|         )
 | |
|     
 | |
|     def _convert_cli_result_to_hive(self, cli_result: CliTaskResult) -> Dict[str, Any]:
 | |
|         """Convert CLI TaskResult to Hive result format"""
 | |
|         # Map CLI status to Hive format
 | |
|         status_mapping = {
 | |
|             "completed": "completed",
 | |
|             "failed": "failed", 
 | |
|             "timeout": "failed",
 | |
|             "pending": "pending",
 | |
|             "running": "in_progress"
 | |
|         }
 | |
|         
 | |
|         hive_status = status_mapping.get(cli_result.status.value, "failed")
 | |
|         
 | |
|         result = {
 | |
|             "response": cli_result.response,
 | |
|             "status": hive_status,
 | |
|             "execution_time": cli_result.execution_time,
 | |
|             "agent_id": cli_result.agent_id,
 | |
|             "model": cli_result.model
 | |
|         }
 | |
|         
 | |
|         if cli_result.error:
 | |
|             result["error"] = cli_result.error
 | |
|         
 | |
|         if cli_result.metadata:
 | |
|             result["metadata"] = cli_result.metadata
 | |
|         
 | |
|         return result
 | |
|     
 | |
|     async def health_check_all_agents(self) -> Dict[str, Dict[str, Any]]:
 | |
|         """Perform health checks on all CLI agents"""
 | |
|         health_results = {}
 | |
|         
 | |
|         for agent_id, agent in self.active_agents.items():
 | |
|             try:
 | |
|                 health = await agent.health_check()
 | |
|                 health_results[agent_id] = health
 | |
|             except Exception as e:
 | |
|                 health_results[agent_id] = {
 | |
|                     "agent_id": agent_id,
 | |
|                     "error": str(e),
 | |
|                     "healthy": False
 | |
|                 }
 | |
|         
 | |
|         return health_results
 | |
|     
 | |
|     def get_agent_statistics(self) -> Dict[str, Dict[str, Any]]:
 | |
|         """Get statistics for all CLI agents"""
 | |
|         stats = {}
 | |
|         for agent_id, agent in self.active_agents.items():
 | |
|             stats[agent_id] = agent.get_statistics()
 | |
|         return stats
 | |
|     
 | |
|     def get_active_agent_ids(self) -> list:
 | |
|         """Get list of active CLI agent IDs"""
 | |
|         return list(self.active_agents.keys())
 | |
|     
 | |
|     def is_cli_agent(self, agent_id: str) -> bool:
 | |
|         """Check if an agent ID corresponds to a CLI agent"""
 | |
|         return agent_id in self.active_agents
 | |
|     
 | |
|     async def shutdown(self):
 | |
|         """Shutdown CLI agent manager and cleanup resources"""
 | |
|         self.logger.info("Shutting down CLI Agent Manager...")
 | |
|         
 | |
|         try:
 | |
|             # Cleanup all CLI agents
 | |
|             cleanup_tasks = []
 | |
|             for agent_id, agent in list(self.active_agents.items()):
 | |
|                 cleanup_tasks.append(agent.cleanup())
 | |
|             
 | |
|             if cleanup_tasks:
 | |
|                 await asyncio.gather(*cleanup_tasks, return_exceptions=True)
 | |
|             
 | |
|             # Cleanup factory
 | |
|             await self.cli_factory.cleanup_all()
 | |
|             
 | |
|             self.active_agents.clear()
 | |
|             self.is_initialized = False
 | |
|             
 | |
|             self.logger.info("✅ CLI Agent Manager shutdown complete")
 | |
|             
 | |
|         except Exception as e:
 | |
|             self.logger.error(f"❌ CLI Agent Manager shutdown error: {e}")
 | |
|     
 | |
|     def register_hive_agent_from_cli_config(self, agent_id: str, cli_config: Dict[str, Any]) -> Dict[str, Any]:
 | |
|         """
 | |
|         Create agent registration data for Hive coordinator from CLI config
 | |
|         
 | |
|         Returns agent data compatible with Hive Agent dataclass
 | |
|         """
 | |
|         # Map CLI specializations to Hive AgentTypes
 | |
|         specialization_mapping = {
 | |
|             "general_ai": "general_ai",
 | |
|             "reasoning": "reasoning", 
 | |
|             "code_analysis": "profiler",  # Map to existing Hive type
 | |
|             "documentation": "docs_writer",
 | |
|             "testing": "tester"
 | |
|         }
 | |
|         
 | |
|         cli_specialization = cli_config.get("specialization", "general_ai")
 | |
|         hive_specialty = specialization_mapping.get(cli_specialization, "general_ai")
 | |
|         
 | |
|         return {
 | |
|             "id": agent_id,
 | |
|             "endpoint": f"cli://{cli_config['host']}",
 | |
|             "model": cli_config.get("model", "gemini-2.5-pro"),
 | |
|             "specialty": hive_specialty,
 | |
|             "max_concurrent": cli_config.get("max_concurrent", 2),
 | |
|             "current_tasks": 0,
 | |
|             "agent_type": "cli",
 | |
|             "cli_config": cli_config
 | |
|         }
 | |
| 
 | |
| 
 | |
| # Global CLI agent manager instance
 | |
| _cli_agent_manager = None
 | |
| 
 | |
| def get_cli_agent_manager() -> CliAgentManager:
 | |
|     """Get the global CLI agent manager instance"""
 | |
|     global _cli_agent_manager
 | |
|     if _cli_agent_manager is None:
 | |
|         _cli_agent_manager = CliAgentManager()
 | |
|     return _cli_agent_manager |