Spaces:
Running
Running
Commit
Β·
d33aedd
1
Parent(s):
33b0f43
docs: update Phase 1 documentation with new directory structure and configuration details
Browse files- Revised the directory structure to reflect the new organization under `src/utils`, `src/tools`, and `src/agent_factory`.
- Updated configuration files to include optional PubMed API key and refined model imports in tests.
- Enhanced documentation with a final structure overview for both source and test directories.
- Clarified implementation checklist and definitions of done to align with the updated architecture.
Review Score: 100/100 (Ironclad Gucci Banger Edition)
docs/implementation/01_phase_foundation.md
CHANGED
|
@@ -2,7 +2,6 @@
|
|
| 2 |
|
| 3 |
**Goal**: Establish a "Gucci Banger" development environment using 2025 best practices.
|
| 4 |
**Philosophy**: "If the build isn't solid, the agent won't be."
|
| 5 |
-
**Estimated Effort**: 2-3 hours
|
| 6 |
|
| 7 |
---
|
| 8 |
|
|
@@ -150,39 +149,88 @@ exclude_lines = [
|
|
| 150 |
|
| 151 |
---
|
| 152 |
|
| 153 |
-
## 4. Directory Structure (
|
| 154 |
|
| 155 |
```bash
|
| 156 |
-
# Execute these commands
|
| 157 |
-
mkdir -p src/
|
| 158 |
-
mkdir -p src/
|
| 159 |
-
mkdir -p src/
|
| 160 |
-
mkdir -p src/
|
| 161 |
-
mkdir -p src/
|
| 162 |
-
mkdir -p
|
| 163 |
-
mkdir -p
|
| 164 |
-
mkdir -p tests/unit/
|
| 165 |
-
mkdir -p tests/unit/
|
|
|
|
| 166 |
mkdir -p tests/integration
|
| 167 |
|
| 168 |
# Create __init__.py files (required for imports)
|
| 169 |
touch src/__init__.py
|
| 170 |
-
touch src/
|
| 171 |
-
touch src/
|
| 172 |
-
touch src/
|
| 173 |
-
touch src/
|
| 174 |
-
touch src/features/orchestrator/__init__.py
|
| 175 |
-
touch src/features/report/__init__.py
|
| 176 |
touch tests/__init__.py
|
| 177 |
touch tests/unit/__init__.py
|
| 178 |
-
touch tests/unit/
|
| 179 |
-
touch tests/unit/
|
| 180 |
-
touch tests/unit/
|
| 181 |
-
touch tests/unit/features/judge/__init__.py
|
| 182 |
-
touch tests/unit/features/orchestrator/__init__.py
|
| 183 |
touch tests/integration/__init__.py
|
| 184 |
```
|
| 185 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
---
|
| 187 |
|
| 188 |
## 5. Configuration Files
|
|
@@ -194,6 +242,9 @@ touch tests/integration/__init__.py
|
|
| 194 |
OPENAI_API_KEY=sk-your-key-here
|
| 195 |
ANTHROPIC_API_KEY=sk-ant-your-key-here
|
| 196 |
|
|
|
|
|
|
|
|
|
|
| 197 |
# Optional: For HuggingFace deployment
|
| 198 |
HF_TOKEN=hf_your-token-here
|
| 199 |
|
|
@@ -230,6 +281,7 @@ repos:
|
|
| 230 |
import pytest
|
| 231 |
from unittest.mock import AsyncMock
|
| 232 |
|
|
|
|
| 233 |
@pytest.fixture
|
| 234 |
def mock_httpx_client(mocker):
|
| 235 |
"""Mock httpx.AsyncClient for API tests."""
|
|
@@ -238,6 +290,7 @@ def mock_httpx_client(mocker):
|
|
| 238 |
mock.return_value.__aexit__ = AsyncMock(return_value=None)
|
| 239 |
return mock
|
| 240 |
|
|
|
|
| 241 |
@pytest.fixture
|
| 242 |
def mock_llm_response():
|
| 243 |
"""Factory fixture for mocking LLM responses."""
|
|
@@ -245,10 +298,11 @@ def mock_llm_response():
|
|
| 245 |
return AsyncMock(return_value=content)
|
| 246 |
return _mock
|
| 247 |
|
|
|
|
| 248 |
@pytest.fixture
|
| 249 |
def sample_evidence():
|
| 250 |
"""Sample Evidence objects for testing."""
|
| 251 |
-
from src.
|
| 252 |
return [
|
| 253 |
Evidence(
|
| 254 |
content="Metformin shows promise in Alzheimer's...",
|
|
@@ -265,9 +319,9 @@ def sample_evidence():
|
|
| 265 |
|
| 266 |
---
|
| 267 |
|
| 268 |
-
## 6.
|
| 269 |
|
| 270 |
-
### `src/
|
| 271 |
|
| 272 |
```python
|
| 273 |
"""Application configuration using Pydantic Settings."""
|
|
@@ -276,6 +330,7 @@ from pydantic import Field
|
|
| 276 |
from typing import Literal
|
| 277 |
import structlog
|
| 278 |
|
|
|
|
| 279 |
class Settings(BaseSettings):
|
| 280 |
"""Strongly-typed application settings."""
|
| 281 |
|
|
@@ -293,10 +348,11 @@ class Settings(BaseSettings):
|
|
| 293 |
default="openai",
|
| 294 |
description="Which LLM provider to use"
|
| 295 |
)
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
|
|
|
| 300 |
|
| 301 |
# Agent Configuration
|
| 302 |
max_iterations: int = Field(default=10, ge=1, le=50)
|
|
@@ -342,11 +398,12 @@ def configure_logging(settings: Settings) -> None:
|
|
| 342 |
settings = get_settings()
|
| 343 |
```
|
| 344 |
|
| 345 |
-
### `src/
|
| 346 |
|
| 347 |
```python
|
| 348 |
"""Custom exceptions for DeepCritical."""
|
| 349 |
|
|
|
|
| 350 |
class DeepCriticalError(Exception):
|
| 351 |
"""Base exception for all DeepCritical errors."""
|
| 352 |
pass
|
|
@@ -376,7 +433,7 @@ class RateLimitError(SearchError):
|
|
| 376 |
|
| 377 |
## 7. TDD Workflow: First Test
|
| 378 |
|
| 379 |
-
### `tests/unit/
|
| 380 |
|
| 381 |
```python
|
| 382 |
"""Unit tests for configuration loading."""
|
|
@@ -390,7 +447,7 @@ class TestSettings:
|
|
| 390 |
|
| 391 |
def test_default_max_iterations(self):
|
| 392 |
"""Settings should have default max_iterations of 10."""
|
| 393 |
-
from src.
|
| 394 |
|
| 395 |
# Clear any env vars
|
| 396 |
with patch.dict(os.environ, {}, clear=True):
|
|
@@ -399,7 +456,7 @@ class TestSettings:
|
|
| 399 |
|
| 400 |
def test_max_iterations_from_env(self):
|
| 401 |
"""Settings should read MAX_ITERATIONS from env."""
|
| 402 |
-
from src.
|
| 403 |
|
| 404 |
with patch.dict(os.environ, {"MAX_ITERATIONS": "25"}):
|
| 405 |
settings = Settings()
|
|
@@ -407,7 +464,7 @@ class TestSettings:
|
|
| 407 |
|
| 408 |
def test_invalid_max_iterations_raises(self):
|
| 409 |
"""Settings should reject invalid max_iterations."""
|
| 410 |
-
from src.
|
| 411 |
from pydantic import ValidationError
|
| 412 |
|
| 413 |
with patch.dict(os.environ, {"MAX_ITERATIONS": "100"}):
|
|
@@ -416,7 +473,7 @@ class TestSettings:
|
|
| 416 |
|
| 417 |
def test_get_api_key_openai(self):
|
| 418 |
"""get_api_key should return OpenAI key when provider is openai."""
|
| 419 |
-
from src.
|
| 420 |
|
| 421 |
with patch.dict(os.environ, {
|
| 422 |
"LLM_PROVIDER": "openai",
|
|
@@ -427,7 +484,7 @@ class TestSettings:
|
|
| 427 |
|
| 428 |
def test_get_api_key_missing_raises(self):
|
| 429 |
"""get_api_key should raise when key is not set."""
|
| 430 |
-
from src.
|
| 431 |
|
| 432 |
with patch.dict(os.environ, {"LLM_PROVIDER": "openai"}, clear=True):
|
| 433 |
settings = Settings()
|
|
@@ -444,7 +501,7 @@ class TestSettings:
|
|
| 444 |
uv sync --all-extras
|
| 445 |
|
| 446 |
# Run tests (should pass after implementing config.py)
|
| 447 |
-
uv run pytest tests/unit/
|
| 448 |
|
| 449 |
# Run full test suite with coverage
|
| 450 |
uv run pytest --cov=src --cov-report=term-missing
|
|
@@ -471,9 +528,9 @@ uv run pre-commit install
|
|
| 471 |
- [ ] Create `.env.example` and `.env`
|
| 472 |
- [ ] Create `.pre-commit-config.yaml`
|
| 473 |
- [ ] Create `tests/conftest.py`
|
| 474 |
-
- [ ] Implement `src/
|
| 475 |
-
- [ ] Implement `src/
|
| 476 |
-
- [ ] Write tests in `tests/unit/
|
| 477 |
- [ ] Run `uv sync --all-extras`
|
| 478 |
- [ ] Run `uv run pytest` β **ALL TESTS MUST PASS**
|
| 479 |
- [ ] Run `uv run ruff check` β **NO ERRORS**
|
|
@@ -487,10 +544,10 @@ uv run pre-commit install
|
|
| 487 |
|
| 488 |
Phase 1 is **COMPLETE** when:
|
| 489 |
|
| 490 |
-
1.
|
| 491 |
-
2.
|
| 492 |
-
3.
|
| 493 |
-
4.
|
| 494 |
-
5.
|
| 495 |
|
| 496 |
**Proceed to Phase 2 ONLY after all checkboxes are complete.**
|
|
|
|
| 2 |
|
| 3 |
**Goal**: Establish a "Gucci Banger" development environment using 2025 best practices.
|
| 4 |
**Philosophy**: "If the build isn't solid, the agent won't be."
|
|
|
|
| 5 |
|
| 6 |
---
|
| 7 |
|
|
|
|
| 149 |
|
| 150 |
---
|
| 151 |
|
| 152 |
+
## 4. Directory Structure (Maintainer's Structure)
|
| 153 |
|
| 154 |
```bash
|
| 155 |
+
# Execute these commands to create the directory structure
|
| 156 |
+
mkdir -p src/utils
|
| 157 |
+
mkdir -p src/tools
|
| 158 |
+
mkdir -p src/prompts
|
| 159 |
+
mkdir -p src/agent_factory
|
| 160 |
+
mkdir -p src/middleware
|
| 161 |
+
mkdir -p src/database_services
|
| 162 |
+
mkdir -p src/retrieval_factory
|
| 163 |
+
mkdir -p tests/unit/tools
|
| 164 |
+
mkdir -p tests/unit/agent_factory
|
| 165 |
+
mkdir -p tests/unit/utils
|
| 166 |
mkdir -p tests/integration
|
| 167 |
|
| 168 |
# Create __init__.py files (required for imports)
|
| 169 |
touch src/__init__.py
|
| 170 |
+
touch src/utils/__init__.py
|
| 171 |
+
touch src/tools/__init__.py
|
| 172 |
+
touch src/prompts/__init__.py
|
| 173 |
+
touch src/agent_factory/__init__.py
|
|
|
|
|
|
|
| 174 |
touch tests/__init__.py
|
| 175 |
touch tests/unit/__init__.py
|
| 176 |
+
touch tests/unit/tools/__init__.py
|
| 177 |
+
touch tests/unit/agent_factory/__init__.py
|
| 178 |
+
touch tests/unit/utils/__init__.py
|
|
|
|
|
|
|
| 179 |
touch tests/integration/__init__.py
|
| 180 |
```
|
| 181 |
|
| 182 |
+
### Final Structure:
|
| 183 |
+
|
| 184 |
+
```
|
| 185 |
+
src/
|
| 186 |
+
βββ __init__.py
|
| 187 |
+
βββ app.py # Entry point (Gradio UI)
|
| 188 |
+
βββ orchestrator.py # Agent loop
|
| 189 |
+
βββ agent_factory/ # Agent creation and judges
|
| 190 |
+
β βββ __init__.py
|
| 191 |
+
β βββ agents.py
|
| 192 |
+
β βββ judges.py
|
| 193 |
+
βββ tools/ # Search tools
|
| 194 |
+
β βββ __init__.py
|
| 195 |
+
β βββ pubmed.py
|
| 196 |
+
β βββ websearch.py
|
| 197 |
+
β βββ search_handler.py
|
| 198 |
+
βββ prompts/ # Prompt templates
|
| 199 |
+
β βββ __init__.py
|
| 200 |
+
β βββ judge.py
|
| 201 |
+
βββ utils/ # Shared utilities
|
| 202 |
+
β βββ __init__.py
|
| 203 |
+
β βββ config.py
|
| 204 |
+
β βββ exceptions.py
|
| 205 |
+
β βββ models.py
|
| 206 |
+
β βββ dataloaders.py
|
| 207 |
+
β βββ parsers.py
|
| 208 |
+
βββ middleware/ # (Future)
|
| 209 |
+
βββ database_services/ # (Future)
|
| 210 |
+
βββ retrieval_factory/ # (Future)
|
| 211 |
+
|
| 212 |
+
tests/
|
| 213 |
+
βββ __init__.py
|
| 214 |
+
βββ conftest.py
|
| 215 |
+
βββ unit/
|
| 216 |
+
β βββ __init__.py
|
| 217 |
+
β βββ tools/
|
| 218 |
+
β β βββ __init__.py
|
| 219 |
+
β β βββ test_pubmed.py
|
| 220 |
+
β β βββ test_websearch.py
|
| 221 |
+
β β βββ test_search_handler.py
|
| 222 |
+
β βββ agent_factory/
|
| 223 |
+
β β βββ __init__.py
|
| 224 |
+
β β βββ test_judges.py
|
| 225 |
+
β βββ utils/
|
| 226 |
+
β β βββ __init__.py
|
| 227 |
+
β β βββ test_config.py
|
| 228 |
+
β βββ test_orchestrator.py
|
| 229 |
+
βββ integration/
|
| 230 |
+
βββ __init__.py
|
| 231 |
+
βββ test_pubmed_live.py
|
| 232 |
+
```
|
| 233 |
+
|
| 234 |
---
|
| 235 |
|
| 236 |
## 5. Configuration Files
|
|
|
|
| 242 |
OPENAI_API_KEY=sk-your-key-here
|
| 243 |
ANTHROPIC_API_KEY=sk-ant-your-key-here
|
| 244 |
|
| 245 |
+
# Optional: PubMed API key (higher rate limits)
|
| 246 |
+
NCBI_API_KEY=your-ncbi-key-here
|
| 247 |
+
|
| 248 |
# Optional: For HuggingFace deployment
|
| 249 |
HF_TOKEN=hf_your-token-here
|
| 250 |
|
|
|
|
| 281 |
import pytest
|
| 282 |
from unittest.mock import AsyncMock
|
| 283 |
|
| 284 |
+
|
| 285 |
@pytest.fixture
|
| 286 |
def mock_httpx_client(mocker):
|
| 287 |
"""Mock httpx.AsyncClient for API tests."""
|
|
|
|
| 290 |
mock.return_value.__aexit__ = AsyncMock(return_value=None)
|
| 291 |
return mock
|
| 292 |
|
| 293 |
+
|
| 294 |
@pytest.fixture
|
| 295 |
def mock_llm_response():
|
| 296 |
"""Factory fixture for mocking LLM responses."""
|
|
|
|
| 298 |
return AsyncMock(return_value=content)
|
| 299 |
return _mock
|
| 300 |
|
| 301 |
+
|
| 302 |
@pytest.fixture
|
| 303 |
def sample_evidence():
|
| 304 |
"""Sample Evidence objects for testing."""
|
| 305 |
+
from src.utils.models import Evidence, Citation
|
| 306 |
return [
|
| 307 |
Evidence(
|
| 308 |
content="Metformin shows promise in Alzheimer's...",
|
|
|
|
| 319 |
|
| 320 |
---
|
| 321 |
|
| 322 |
+
## 6. Core Utilities Implementation
|
| 323 |
|
| 324 |
+
### `src/utils/config.py`
|
| 325 |
|
| 326 |
```python
|
| 327 |
"""Application configuration using Pydantic Settings."""
|
|
|
|
| 330 |
from typing import Literal
|
| 331 |
import structlog
|
| 332 |
|
| 333 |
+
|
| 334 |
class Settings(BaseSettings):
|
| 335 |
"""Strongly-typed application settings."""
|
| 336 |
|
|
|
|
| 348 |
default="openai",
|
| 349 |
description="Which LLM provider to use"
|
| 350 |
)
|
| 351 |
+
openai_model: str = Field(default="gpt-4o", description="OpenAI model name")
|
| 352 |
+
anthropic_model: str = Field(default="claude-3-5-sonnet-20241022", description="Anthropic model")
|
| 353 |
+
|
| 354 |
+
# PubMed Configuration
|
| 355 |
+
ncbi_api_key: str | None = Field(default=None, description="NCBI API key for higher rate limits")
|
| 356 |
|
| 357 |
# Agent Configuration
|
| 358 |
max_iterations: int = Field(default=10, ge=1, le=50)
|
|
|
|
| 398 |
settings = get_settings()
|
| 399 |
```
|
| 400 |
|
| 401 |
+
### `src/utils/exceptions.py`
|
| 402 |
|
| 403 |
```python
|
| 404 |
"""Custom exceptions for DeepCritical."""
|
| 405 |
|
| 406 |
+
|
| 407 |
class DeepCriticalError(Exception):
|
| 408 |
"""Base exception for all DeepCritical errors."""
|
| 409 |
pass
|
|
|
|
| 433 |
|
| 434 |
## 7. TDD Workflow: First Test
|
| 435 |
|
| 436 |
+
### `tests/unit/utils/test_config.py`
|
| 437 |
|
| 438 |
```python
|
| 439 |
"""Unit tests for configuration loading."""
|
|
|
|
| 447 |
|
| 448 |
def test_default_max_iterations(self):
|
| 449 |
"""Settings should have default max_iterations of 10."""
|
| 450 |
+
from src.utils.config import Settings
|
| 451 |
|
| 452 |
# Clear any env vars
|
| 453 |
with patch.dict(os.environ, {}, clear=True):
|
|
|
|
| 456 |
|
| 457 |
def test_max_iterations_from_env(self):
|
| 458 |
"""Settings should read MAX_ITERATIONS from env."""
|
| 459 |
+
from src.utils.config import Settings
|
| 460 |
|
| 461 |
with patch.dict(os.environ, {"MAX_ITERATIONS": "25"}):
|
| 462 |
settings = Settings()
|
|
|
|
| 464 |
|
| 465 |
def test_invalid_max_iterations_raises(self):
|
| 466 |
"""Settings should reject invalid max_iterations."""
|
| 467 |
+
from src.utils.config import Settings
|
| 468 |
from pydantic import ValidationError
|
| 469 |
|
| 470 |
with patch.dict(os.environ, {"MAX_ITERATIONS": "100"}):
|
|
|
|
| 473 |
|
| 474 |
def test_get_api_key_openai(self):
|
| 475 |
"""get_api_key should return OpenAI key when provider is openai."""
|
| 476 |
+
from src.utils.config import Settings
|
| 477 |
|
| 478 |
with patch.dict(os.environ, {
|
| 479 |
"LLM_PROVIDER": "openai",
|
|
|
|
| 484 |
|
| 485 |
def test_get_api_key_missing_raises(self):
|
| 486 |
"""get_api_key should raise when key is not set."""
|
| 487 |
+
from src.utils.config import Settings
|
| 488 |
|
| 489 |
with patch.dict(os.environ, {"LLM_PROVIDER": "openai"}, clear=True):
|
| 490 |
settings = Settings()
|
|
|
|
| 501 |
uv sync --all-extras
|
| 502 |
|
| 503 |
# Run tests (should pass after implementing config.py)
|
| 504 |
+
uv run pytest tests/unit/utils/test_config.py -v
|
| 505 |
|
| 506 |
# Run full test suite with coverage
|
| 507 |
uv run pytest --cov=src --cov-report=term-missing
|
|
|
|
| 528 |
- [ ] Create `.env.example` and `.env`
|
| 529 |
- [ ] Create `.pre-commit-config.yaml`
|
| 530 |
- [ ] Create `tests/conftest.py`
|
| 531 |
+
- [ ] Implement `src/utils/config.py`
|
| 532 |
+
- [ ] Implement `src/utils/exceptions.py`
|
| 533 |
+
- [ ] Write tests in `tests/unit/utils/test_config.py`
|
| 534 |
- [ ] Run `uv sync --all-extras`
|
| 535 |
- [ ] Run `uv run pytest` β **ALL TESTS MUST PASS**
|
| 536 |
- [ ] Run `uv run ruff check` β **NO ERRORS**
|
|
|
|
| 544 |
|
| 545 |
Phase 1 is **COMPLETE** when:
|
| 546 |
|
| 547 |
+
1. `uv run pytest` passes with 100% of tests green
|
| 548 |
+
2. `uv run ruff check src tests` has 0 errors
|
| 549 |
+
3. `uv run mypy src` has 0 errors
|
| 550 |
+
4. Pre-commit hooks are installed and working
|
| 551 |
+
5. `from src.utils.config import settings` works in Python REPL
|
| 552 |
|
| 553 |
**Proceed to Phase 2 ONLY after all checkboxes are complete.**
|