Spaces:
Running
Running
| # Phase 4 Implementation Spec: Orchestrator & UI | |
| **Goal**: Connect the Brain and the Body, then give it a Face. | |
| **Philosophy**: "Streaming is Trust." | |
| **Estimated Effort**: 4-5 hours | |
| **Prerequisite**: Phases 1-3 complete | |
| --- | |
| ## 1. The Slice Definition | |
| This slice connects: | |
| 1. **Orchestrator**: The loop calling `SearchHandler` → `JudgeHandler`. | |
| 2. **UI**: Gradio app. | |
| **Files**: | |
| - `src/utils/models.py`: Add Orchestrator models | |
| - `src/orchestrator.py`: Main logic | |
| - `src/app.py`: UI | |
| --- | |
| ## 2. Models (`src/utils/models.py`) | |
| Add to models file: | |
| ```python | |
| from enum import Enum | |
| class AgentState(str, Enum): | |
| SEARCHING = "searching" | |
| JUDGING = "judging" | |
| COMPLETE = "complete" | |
| ERROR = "error" | |
| class AgentEvent(BaseModel): | |
| state: AgentState | |
| message: str | |
| data: dict[str, Any] | None = None | |
| ``` | |
| --- | |
| ## 3. Orchestrator (`src/orchestrator.py`) | |
| ```python | |
| """Main agent orchestrator.""" | |
| import structlog | |
| from typing import AsyncGenerator | |
| from src.shared.config import settings | |
| from src.tools.search_handler import SearchHandler | |
| from src.agent_factory.judges import JudgeHandler | |
| from src.utils.models import AgentEvent, AgentState | |
| logger = structlog.get_logger() | |
| class Orchestrator: | |
| def __init__(self): | |
| self.search = SearchHandler(...) | |
| self.judge = JudgeHandler() | |
| async def run(self, question: str) -> AsyncGenerator[AgentEvent, None]: | |
| """Run the loop.""" | |
| yield AgentEvent(state=AgentState.SEARCHING, message="Starting...") | |
| # ... while loop implementation ... | |
| # ... yield events ... | |
| ``` | |
| --- | |
| ## 4. UI (`src/app.py`) | |
| ```python | |
| """Gradio UI.""" | |
| import gradio as gr | |
| from src.orchestrator import Orchestrator | |
| async def chat(message, history): | |
| agent = Orchestrator() | |
| async for event in agent.run(message): | |
| yield f"**[{event.state.value}]** {event.message}" | |
| # ... gradio blocks setup ... | |
| ``` | |
| --- | |
| ## 5. TDD Workflow | |
| ### Test File: `tests/unit/test_orchestrator.py` | |
| ```python | |
| """Unit tests for Orchestrator.""" | |
| import pytest | |
| from unittest.mock import AsyncMock | |
| class TestOrchestrator: | |
| @pytest.mark.asyncio | |
| async def test_run_loop(self, mocker): | |
| from src.orchestrator import Orchestrator | |
| # Mock handlers | |
| # ... setup mocks ... | |
| orch = Orchestrator() | |
| events = [e async for e in orch.run("test")] | |
| assert len(events) > 0 | |
| ``` | |
| --- | |
| ## 6. Implementation Checklist | |
| - [ ] Update `src/utils/models.py` | |
| - [ ] Implement `src/orchestrator.py` | |
| - [ ] Implement `src/app.py` | |
| - [ ] Write tests in `tests/unit/test_orchestrator.py` | |
| - [ ] Run `uv run python src/app.py` |