# file: agents/hunter.py """ Hunter Agent - Discovers companies dynamically Now uses web search to find company information instead of static files """ import json from typing import List, Optional from app.schema import Company, Prospect from app.config import COMPANIES_FILE, SKIP_WEB_SEARCH from services.company_discovery import get_company_discovery_service import logging logger = logging.getLogger(__name__) class Hunter: """ Discovers companies and creates prospects dynamically NEW: Can now discover companies from user input (company names) LEGACY: Still supports loading from seed file for backwards compatibility """ def __init__(self, mcp_registry): self.mcp = mcp_registry self.store = mcp_registry.get_store_client() self.discovery = get_company_discovery_service() async def run( self, company_names: Optional[List[str]] = None, company_ids: Optional[List[str]] = None, use_seed_file: bool = False ) -> List[Prospect]: """ Discover companies and create prospects Args: company_names: List of company names to discover (NEW - dynamic mode) company_ids: List of company IDs from seed file (LEGACY - static mode) use_seed_file: If True, load from seed file instead of discovery Returns: List of Prospect objects """ prospects = [] # Mode 1: Dynamic discovery from company names (NEW) if company_names and not use_seed_file: logger.info(f"Hunter: Dynamic discovery mode - discovering {len(company_names)} companies") for company_name in company_names: try: logger.info(f"Hunter: Discovering '{company_name}'...") # Discover company information from web (or use fallback if configured) company = await self.discovery.discover_company(company_name, skip_search=SKIP_WEB_SEARCH) if not company: logger.warning(f"Hunter: Could not discover company '{company_name}'") # Create a minimal fallback company company = self._create_fallback_company(company_name) # Create prospect prospect = Prospect( id=company.id, company=company, status="new" ) # Save to store await self.store.save_prospect(prospect) prospects.append(prospect) logger.info(f"Hunter: Successfully created prospect for '{company_name}'") except Exception as e: logger.error(f"Hunter: Error discovering '{company_name}': {str(e)}") # Create fallback and continue company = self._create_fallback_company(company_name) prospect = Prospect( id=company.id, company=company, status="new" ) await self.store.save_prospect(prospect) prospects.append(prospect) # Mode 2: Legacy mode - load from seed file (BACKWARDS COMPATIBLE) else: logger.info("Hunter: Legacy mode - loading from seed file") try: # Load from seed file with open(COMPANIES_FILE) as f: companies_data = json.load(f) for company_data in companies_data: # Filter by IDs if specified if company_ids and company_data["id"] not in company_ids: continue company = Company(**company_data) # Create prospect prospect = Prospect( id=company.id, company=company, status="new" ) # Save to store await self.store.save_prospect(prospect) prospects.append(prospect) logger.info(f"Hunter: Loaded {len(prospects)} companies from seed file") except FileNotFoundError: logger.error(f"Hunter: Seed file not found: {COMPANIES_FILE}") # If no seed file and no company names provided, return empty if not company_names: return [] except Exception as e: logger.error(f"Hunter: Error loading seed file: {str(e)}") return [] return prospects def _create_fallback_company(self, company_name: str) -> Company: """Create a minimal fallback company when discovery fails""" import re import uuid # Generate ID slug = re.sub(r'[^a-zA-Z0-9]', '', company_name.lower())[:20] company_id = f"{slug}_{str(uuid.uuid4())[:8]}" # Create minimal company company = Company( id=company_id, name=company_name, domain=f"{slug}.com", industry="Technology", size=100, pains=[ "Customer experience improvement needed", "Operational efficiency challenges" ], notes=[ "Company information discovery in progress", "Limited data available" ] ) logger.info(f"Hunter: Created fallback company for '{company_name}'") return company