diff --git a/PERFORMANCE_ANALYSIS.md b/PERFORMANCE_ANALYSIS.md index 20a4c612..33ffe2f6 100644 --- a/PERFORMANCE_ANALYSIS.md +++ b/PERFORMANCE_ANALYSIS.md @@ -102,7 +102,7 @@ def load_prompts(self) -> None: **Issue**: Tools are registered in loops without checking for duplicates efficiently. ```python -for skill in self.router.skills: +for skill in self.skills: tool_manager.register_tool(skill.create_ticket_tool()) # ← Potential duplicate registrations ``` diff --git a/docs/api/core.md b/docs/api/core.md index 0ddd1838..aec6d176 100644 --- a/docs/api/core.md +++ b/docs/api/core.md @@ -165,19 +165,9 @@ class MemoryItem(BaseModel): embedding: Optional[List[float]] = None ``` -## Router System +## Skill and Service Management -### Router - -Routes queries to appropriate skills and services based on content analysis. - -```python -class Router: - def __init__(self): - """Initialize router with empty skill and service registries.""" -``` - -#### Methods +Skills and services are now managed directly by the MainAgent without a separate Router component. ##### `register_skill(skill: Skill, keywords: List[str]) -> None` @@ -412,31 +402,24 @@ for result in results: memory.flush() ``` -### Router Usage +### MainAgent Skill Management ```python -from talos.core.router import Router +from talos.core.main_agent import MainAgent from talos.skills.proposals import ProposalsSkill from talos.skills.twitter_sentiment import TwitterSentimentSkill -# Create router -router = Router() +# Create main agent (skills are automatically registered) +agent = MainAgent(model=model, prompts_dir="prompts") -# Register skills -router.register_skill( - ProposalsSkill(), - keywords=["proposal", "governance", "vote"] -) -router.register_skill( - TwitterSentimentSkill(), - keywords=["sentiment", "twitter", "social"] -) +# Skills are managed directly by MainAgent +# Access skills through agent.skills list +for skill in agent.skills: + print(f"Available skill: {skill.name}") -# Route queries -handler = router.route("Analyze this governance proposal") -if handler: - result = handler.run(proposal_text="...") - print(result.answers[0]) +# Use agent to process queries +result = agent.run("Analyze this governance proposal") +print(result) ``` ## Error Handling diff --git a/docs/architecture/agents.md b/docs/architecture/agents.md index bb5ac619..c39330b8 100644 --- a/docs/architecture/agents.md +++ b/docs/architecture/agents.md @@ -11,7 +11,8 @@ The `MainAgent` serves as the top-level orchestrator that integrates all system ```python class MainAgent: def __init__(self): - self.router = Router() + self.skills = [] + self.services = [] self.hypervisor = Hypervisor() self.tool_manager = ToolManager() self.memory = Memory() @@ -26,7 +27,7 @@ class MainAgent: **Workflow:** 1. Receives user input -2. Routes query through Router to appropriate skill/service +2. Routes query directly to appropriate skill/service 3. Executes actions through SupervisedTool wrappers 4. Stores results in Memory for future reference 5. Returns structured response to user diff --git a/docs/architecture/components.md b/docs/architecture/components.md index 58964711..e53a760f 100644 --- a/docs/architecture/components.md +++ b/docs/architecture/components.md @@ -76,7 +76,7 @@ Talos uses a variety of tools to interact with external services like Twitter, G The `MainAgent` serves as the top-level orchestrator that integrates all system components: -- **Router Integration** - Routes queries to appropriate skills/services +- **Direct Skill/Service Management** - Manages skills and services directly - **Hypervisor Integration** - Ensures all actions are supervised - **Tool Management** - Manages available tools and their registration - **Memory System** - Persistent conversation history and semantic search diff --git a/docs/architecture/skills-services.md b/docs/architecture/skills-services.md index 4a8f50c1..497b3cd2 100644 --- a/docs/architecture/skills-services.md +++ b/docs/architecture/skills-services.md @@ -206,14 +206,14 @@ Handles GitHub operations and PR reviews: 4. **Test Coverage** - Ensure adequate test coverage 5. **Documentation** - Verify documentation updates -## Router System +## Skill and Service Management -### Query Routing +### Direct Management -The `Router` directs queries to appropriate skills and services: +The `MainAgent` directly manages skills and services without a separate Router: ```python -class Router: +class MainAgent: def __init__(self): self.skills = [] self.services = [] @@ -235,12 +235,8 @@ class Router: Skills and services are dynamically registered: ```python -# Skill registration -router.register_skill(ProposalsSkill(), keywords=["proposal", "governance"]) -router.register_skill(TwitterSentimentSkill(), keywords=["sentiment", "twitter"]) - -# Service registration -router.register_service(YieldManagerService(), keywords=["yield", "apr", "staking"]) +# Skills and services are automatically registered during MainAgent initialization +# based on available API keys and configuration ``` ## Integration Patterns diff --git a/docs/development/contributing.md b/docs/development/contributing.md index 3155a4f9..80b64224 100644 --- a/docs/development/contributing.md +++ b/docs/development/contributing.md @@ -287,9 +287,10 @@ When adding new skills to Talos: pass ``` -2. **Register with router**: +2. **Add to MainAgent setup**: ```python - router.register_skill(MyNewSkill(), keywords=["keyword1", "keyword2"]) + # Skills are automatically registered in MainAgent._setup_skills_and_services() + # Add your skill to the skills list in that method ``` 3. **Add comprehensive tests** diff --git a/docs/development/performance.md b/docs/development/performance.md index 36ae46ef..d5977b1e 100644 --- a/docs/development/performance.md +++ b/docs/development/performance.md @@ -209,7 +209,7 @@ class FilePromptManager: **Issue**: Tools are registered in loops without checking for duplicates efficiently. ```python -for skill in self.router.skills: +for skill in self.skills: tool_manager.register_tool(skill.create_ticket_tool()) # ← Potential duplicate registrations ``` diff --git a/docs/development/testing.md b/docs/development/testing.md index f4c3e7b0..fd175e97 100644 --- a/docs/development/testing.md +++ b/docs/development/testing.md @@ -20,7 +20,7 @@ tests/ │ ├── core/ │ │ ├── test_agent.py │ │ ├── test_memory.py -│ │ └── test_router.py +│ │ └── test_main_agent.py │ ├── skills/ │ │ ├── test_proposals.py │ │ ├── test_sentiment.py diff --git a/src/talos/cli/daemon.py b/src/talos/cli/daemon.py index b1f434c3..d35d6560 100644 --- a/src/talos/cli/daemon.py +++ b/src/talos/cli/daemon.py @@ -10,7 +10,6 @@ from langchain_openai import ChatOpenAI from talos.core.main_agent import MainAgent -from talos.core.router import Router from talos.settings import OpenAISettings logger = logging.getLogger(__name__) @@ -53,12 +52,10 @@ def _initialize_agent(self) -> None: logger.info("Initializing MainAgent...") model = ChatOpenAI(model=self.model_name, temperature=self.temperature) - router = Router([], []) self.main_agent = MainAgent( prompts_dir=self.prompts_dir, model=model, - router=router, schema=None, ) diff --git a/src/talos/cli/main.py b/src/talos/cli/main.py index 448f6fb3..4b168ba3 100644 --- a/src/talos/cli/main.py +++ b/src/talos/cli/main.py @@ -8,7 +8,6 @@ from langchain_openai import ChatOpenAI from talos.core.main_agent import MainAgent -from talos.core.router import Router from talos.services.key_management import KeyManagement from talos.settings import OpenAISettings from talos.cli.daemon import TalosDaemon @@ -74,11 +73,9 @@ def main_command( # Create the main agent model = ChatOpenAI(model=model_name, temperature=temperature) - router = Router([], []) main_agent = MainAgent( prompts_dir=prompts_dir, model=model, - router=router, schema=None, user_id=user_id, use_database_memory=use_database, diff --git a/src/talos/core/main_agent.py b/src/talos/core/main_agent.py index b175606c..d24347e9 100644 --- a/src/talos/core/main_agent.py +++ b/src/talos/core/main_agent.py @@ -9,7 +9,6 @@ from talos.core.agent import Agent from talos.core.job_scheduler import JobScheduler -from talos.core.router import Router from talos.core.scheduled_job import ScheduledJob from talos.data.dataset_manager import DatasetManager from talos.hypervisor.hypervisor import Hypervisor @@ -37,7 +36,8 @@ class MainAgent(Agent): Also manages scheduled jobs for autonomous execution. """ - router: Optional[Router] = None + skills: list[Skill] = [] + services: list[Service] = [] prompts_dir: str model: BaseChatModel is_main_agent: bool = True @@ -51,7 +51,7 @@ def model_post_init(self, __context: Any) -> None: self._setup_prompt_manager() self._ensure_user_id() self._setup_memory() - self._setup_router() + self._setup_skills_and_services() self._setup_hypervisor() self._setup_dataset_manager() self._setup_tool_manager() @@ -152,7 +152,7 @@ def _setup_memory(self) -> None: verbose=self.verbose, ) - def _setup_router(self) -> None: + def _setup_skills_and_services(self) -> None: if not self.prompt_manager: raise ValueError("Prompt manager not initialized.") services: list[Service] = [] @@ -198,8 +198,8 @@ def _setup_router(self) -> None: except ValueError: pass # Twitter token not available, skip Twitter-dependent skills - if not self.router: - self.router = Router(services=services, skills=skills) + self.skills = skills + self.services = services def _setup_hypervisor(self) -> None: if not self.prompt_manager: @@ -226,9 +226,8 @@ def _setup_dataset_manager(self) -> None: self.dataset_manager = DatasetManager(verbose=self.verbose) def _setup_tool_manager(self) -> None: - assert self.router is not None tool_manager = ToolManager() - for skill in self.router.skills: + for skill in self.skills: tool_manager.register_tool(skill.create_ticket_tool()) tool_manager.register_tool(self._get_ticket_status_tool()) tool_manager.register_tool(self._add_memory_tool()) @@ -339,8 +338,11 @@ def get_ticket_status(service_name: str, ticket_id: str) -> Ticket: Returns: The ticket object. """ - assert self.router is not None - skill = self.router.get_skill(service_name) + skill = None + for s in self.skills: + if s.name == service_name: + skill = s + break if not skill: raise ValueError(f"Skill '{service_name}' not found.") ticket = skill.get_ticket_status(ticket_id) @@ -351,16 +353,16 @@ def get_ticket_status(service_name: str, ticket_id: str) -> Ticket: return get_ticket_status def _build_context(self, query: str, **kwargs) -> dict: - assert self.router is not None - base_context = super()._build_context(query, **kwargs) - active_tickets = self.router.get_all_tickets() + active_tickets = [] + for skill in self.skills: + active_tickets.extend(skill.get_all_tickets()) ticket_info = [f"- {ticket.ticket_id}: last updated at {ticket.updated_at}" for ticket in active_tickets] main_agent_context = { "time": datetime.now().isoformat(), - "available_services": ", ".join([service.name for service in self.router.services]), + "available_services": ", ".join([service.name for service in self.services]), "active_tickets": " ".join(ticket_info), } diff --git a/src/talos/core/router.py b/src/talos/core/router.py deleted file mode 100644 index 6434085d..00000000 --- a/src/talos/core/router.py +++ /dev/null @@ -1,83 +0,0 @@ -from __future__ import annotations - -from talos.models.services import Ticket -from talos.services.abstract.service import Service -from talos.skills.base import Skill - - -class Router: - """ - A simple router that uses keywords to decide which service to use. - """ - - def __init__(self, services: list[Service], skills: list[Skill]): - self.services = services - self.skills = skills - self.service_map = {service.name: service for service in services} - self.skill_map = {skill.name: skill for skill in skills} - self.keywords = { - "github": ["github", "issue", "pull request", "pr", "code"], - "pr_review_skill": ["pr review", "pull request review", "code review", "review pr"], - "proposals": ["proposal", "vote", "snapshot"], - "twitter": ["twitter", "tweet", "x"], - "twitter_influencer": [ - "crypto influencer", - "influencer analysis", - "crypto twitter", - "influencer evaluation", - ], - "twitter_influence_skill": [ - "twitter influence", - "influence evaluation", - "account influence", - "twitter perception", - "influence analysis", - "social influence", - "twitter credibility", - ], - "codebase_evaluation_skill": [ - "codebase", - "code quality", - "code evaluation", - "repository analysis", - "code review", - "code assessment", - "improve code", - "code improvements", - "evaluate repository" - ], - "devin": ["devin", "task", "tasks", "session", "sessions", "ai agent", "devin ai"], - } - - def route(self, query: str) -> Service | Skill | None: - """ - Routes the query to the appropriate service or skill. - """ - for service_name, keywords in self.keywords.items(): - if any(keyword in query.lower() for keyword in keywords): - if service_name in self.service_map: - return self.service_map.get(service_name) - elif service_name in self.skill_map: - return self.skill_map.get(service_name) - return None - - def get_service(self, service_name: str) -> Service | None: - """ - Returns a service by name. - """ - return self.service_map.get(service_name) - - def get_skill(self, skill_name: str) -> Skill | None: - """ - Returns a skill by name. - """ - return self.skill_map.get(skill_name) - - def get_all_tickets(self) -> list[Ticket]: - """ - Returns all tickets from all skills. - """ - tickets: list[Ticket] = [] - for skill in self.skills: - tickets.extend(skill.get_all_tickets()) - return tickets diff --git a/tests/test_main_agent.py b/tests/test_main_agent.py index 12ed19d5..b8f176a1 100644 --- a/tests/test_main_agent.py +++ b/tests/test_main_agent.py @@ -4,7 +4,6 @@ from langchain_core.language_models import BaseChatModel from talos.core.main_agent import MainAgent -from talos.core.router import Router from talos.hypervisor.hypervisor import Hypervisor from talos.prompts.prompt import Prompt from talos.prompts.prompt_managers.file_prompt_manager import FilePromptManager @@ -51,22 +50,18 @@ def test_main_agent_initialization(mock_model: BaseChatModel) -> None: ) mock_file_prompt_manager.return_value = mock_prompt_manager mock_hypervisor.return_value = MagicMock(spec=Hypervisor) - from talos.skills.proposals import ProposalsSkill agent = MainAgent( model=mock_model, prompts_dir="", prompt_manager=mock_prompt_manager, schema=None, - router=Router( - services=[], - skills=[ProposalsSkill(llm=mock_model)], - ), ) assert agent is not None assert agent.model == mock_model assert agent.prompt_manager == mock_prompt_manager - assert agent.router is not None + assert agent.skills is not None + assert agent.services is not None assert agent.supervisor is not None assert agent.tool_manager is not None assert len(agent.tool_manager.tools) > 0 diff --git a/tests/test_prompt_concatenation.py b/tests/test_prompt_concatenation.py index 5ef4773f..546a9f7a 100644 --- a/tests/test_prompt_concatenation.py +++ b/tests/test_prompt_concatenation.py @@ -4,7 +4,6 @@ from langchain_core.language_models import BaseChatModel from talos.core.main_agent import MainAgent -from talos.core.router import Router from talos.hypervisor.hypervisor import Hypervisor from talos.prompts.prompt import Prompt from talos.prompts.prompt_managers.file_prompt_manager import FilePromptManager @@ -65,15 +64,9 @@ def test_prompt_concatenation(mock_model: BaseChatModel) -> None: mock_file_prompt_manager.return_value = mock_prompt_manager mock_hypervisor.return_value = MagicMock(spec=Hypervisor) - from talos.skills.proposals import ProposalsSkill - MainAgent( model=mock_model, prompts_dir="", prompt_manager=mock_prompt_manager, schema=None, - router=Router( - services=[], - skills=[ProposalsSkill(llm=mock_model)], - ), )