[project] name = "roboco" version = "0.1.0" description = "AI Agents Company - A virtual organization of AI agents functioning as a software development workforce" authors = [ {name = "Renzo Franceschini", email = "rennf93@users.noreply.github.com"} ] readme = "README.md" license = { text = "MIT" } requires-python = ">=3.10,<3.15" dependencies = [ # Core "pydantic", "pydantic-settings", # API "fastapi", "uvicorn[standard]", "websockets", # Database "sqlalchemy[asyncio]", "asyncpg", # PostgreSQL async driver "alembic", # Migrations # Cache/Queue "redis", "hiredis", # Redis performance # RAG (piragi with PostgreSQL/pgvector backend) "piragi[postgres]", # AI/LLM "anthropic", "openai", # For embeddings "tiktoken", # Token counting "python-toon", # Token-efficient LLM serialization # MCP (Model Context Protocol) "mcp", # Utilities "httpx", "python-multipart", "python-jose[cryptography]", # JWT "passlib[bcrypt]", # Password hashing "tenacity", # Retry logic "structlog", # Structured logging # Streaming "sse-starlette", # Server-Sent Events for A2A streaming # Direct imports (promoted from transitive) "cryptography", # utils/crypto.py — Fernet-encrypted project git tokens # Build-time pin (not imported). Verified empirically: removing this # entry causes uv to ignore the [tool.uv.sources.torch] CPU-only # redirect and pull ~2.5GB of unused CUDA wheels. The entry has to # appear in direct deps for the source override to bind. deptry's # DEP002 ignore for `torch` below documents the same reality. "torch", ] [project.optional-dependencies] dev = [ # Testing "pytest", "pytest-asyncio", "pytest-cov", "pytest-xdist", "httpx", # For TestClient "factory-boy", "faker", # Code Quality "ruff", "mypy", "vulture", "bandit", "pip-audit", "radon", "xenon", "deptry", "import-linter", # Type Stubs "types-redis", "types-passlib", "types-python-jose", # Development "ipython", "rich", ] docs = [ "mkdocs", "mkdocs-material", "mkdocstrings[python]", ] [project.scripts] roboco = "roboco.cli:main" roboco-bootstrap = "roboco.bootstrap:cli" [project.urls] Homepage = "https://github.com/renzof/roboco" Documentation = "https://roboco.dev/docs" Repository = "https://github.com/renzof/roboco" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["roboco"] [tool.hatch.metadata] allow-direct-references = true # ============================================================================= # uv: pin torch to the CPU-only wheel index # ============================================================================= # `piragi` transitively depends on torch. Our stack uses Ollama over HTTP for # all embeddings/LLM, so torch is never actually loaded at runtime — but uv # would otherwise resolve torch from PyPI, which bundles the full NVIDIA CUDA # stack (~2.5GB across nvidia-cublas, cudnn, cufft, cusolver, nccl, etc.). # The CPU-only index provides a ~200MB torch wheel and drops every CUDA dep. [[tool.uv.index]] name = "pytorch-cpu" url = "https://download.pytorch.org/whl/cpu" explicit = true [tool.uv.sources] torch = [{ index = "pytorch-cpu" }] # ============================================================================= # RUFF Configuration # ============================================================================= [tool.ruff] target-version = "py313" src = ["src"] exclude = ["vulture_whitelist.py", ".venv", "alembic"] [tool.ruff.lint] select = [ "E", # pycodestyle errors "W", # pycodestyle warnings "F", # Pyflakes "I", # isort "B", # flake8-bugbear "C4", # flake8-comprehensions "UP", # pyupgrade "ARG", # flake8-unused-arguments "SIM", # flake8-simplify "TCH", # flake8-type-checking "PTH", # flake8-use-pathlib "PL", # Pylint "RUF", # Ruff-specific ] # Lazy imports to avoid circular dependencies [tool.ruff.lint.per-file-ignores] "roboco/mcp/**/*.py" = ["PLC0415"] "roboco/services/*.py" = ["PLC0415"] "roboco/api/routes/*.py" = ["PLC0415"] "roboco/runtime/*.py" = ["PLC0415"] # Test fixtures that reload modules to test env-var-at-import-time behavior "tests/unit/mcp_servers/*.py" = ["PLC0415"] # ============================================================================= # MyPy Configuration # ============================================================================= [tool.mypy] python_version = "3.13" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true disallow_incomplete_defs = true check_untyped_defs = true disallow_untyped_decorators = true no_implicit_optional = true strict_optional = true warn_redundant_casts = true warn_unused_ignores = true warn_no_return = true warn_unreachable = true exclude = ["vulture_whitelist.py", ".venv", "alembic"] plugins = ["pydantic.mypy"] [[tool.mypy.overrides]] module = [ "redis.*", "anthropic.*", "tiktoken.*", "piragi.*", "toon.*", "sse_starlette.*", "asyncpg.*", ] ignore_missing_imports = true [tool.pydantic-mypy] init_forbid_extra = true init_typed = true warn_required_dynamic_aliases = true # ============================================================================= # Pytest Configuration # ============================================================================= [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] python_files = ["test_*.py"] asyncio_default_fixture_loop_scope = "function" addopts = "--cov=roboco --cov-report=term-missing" [tool.coverage.run] # Coverage core. Python 3.12+ supports sys.monitoring; the legacy `pytrace` # core loses trace events across `await` boundaries on 3.13, under-counting # every async route by ~30%. `sysmon` is the only core that gives accurate # coverage for async route handlers. core = "sysmon" # Modules excluded from the coverage gate because they require live # infrastructure (Ollama, real audio/video stacks, real workspaces, # Docker daemon, Claude Code CLI) that the unit-coverage gate does not # provision. They're covered by dedicated integration runs (smoke tests # on the NAS) rather than the unit-coverage threshold. omit = [ # RAG / proactive context — needs Ollama + real embedding model. "roboco/services/proactive.py", "roboco/services/optimal.py", "roboco/services/optimal_brain/*", # Audio transcription — needs Whisper-class model. "roboco/services/transcription.py", # Agent classes — instantiated by the orchestrator when spawning # Docker containers running the Claude CLI. No usable unit-test # surface; integration coverage is the smoke run. "roboco/agents/*", "roboco/agent_sdk/*", # Container orchestration — drives Docker daemon, agent spawning, # health/dispatch loops. Covered by smoke runs. "roboco/runtime/orchestrator.py", # MCP server entry points — modeled around the Claude CLI's STDIO # MCP transport and only meaningful when running inside an agent # container with the orchestrator reachable. Their _post() bridges # ARE unit-tested separately (test_envelope_on_4xx, test_flow_server, # test_do_server) — just not via direct module import here. "roboco/mcp/*", # WebSocket route — needs a running ASGI app + WS client. "roboco/api/websocket.py", # Event stream bus — wraps Redis Streams; tested via integration only. "roboco/events/stream_bus.py", # GitService — runs `git` subprocesses against per-agent workspaces. # Unit-testable surface is < 5% of the module; covered via the # _StubGit-based real-DB integration test + live smoke runs. "roboco/services/git.py", "roboco/services/workspace.py", # Notification delivery — Redis Streams + push to MCP transport. "roboco/services/notification_delivery.py", # CLI entry point. "roboco/cli.py", # Auto-generated migrations. "alembic/*", ] markers = [ "asyncio: mark tests as async", "slow: marks tests as slow", "integration: marks tests as integration tests", "unit: marks tests as unit tests", ] # ============================================================================= # Vulture Configuration # ============================================================================= [tool.vulture] paths = ["roboco", "tests", "vulture_whitelist.py"] exclude = ["**/conftest.py", ".venv", "alembic"] min_confidence = 100 ignore_decorators = [ "@app.route", "@app.get", "@app.post", "@app.put", "@app.delete", "@app.patch", "@pytest.fixture", "@pytest.mark.*", "@validator", "@root_validator", "@field_validator", "@model_validator", ] ignore_names = [ "test_*", "Test*", "cleanup_*", "mock_*", "expected_*", "exc_*", "__*__", "Config", "Field", ] sort_by_size = true # ============================================================================= # Bandit Configuration # ============================================================================= [tool.bandit] skips = [] exclude_dirs = ["tests", ".venv", "vulture_whitelist.py", "alembic"] severity = "medium" # ============================================================================= # Radon Configuration # ============================================================================= [tool.radon] exclude = "tests/*,.venv/*,vulture_whitelist.py,alembic/*" cc_min = "C" mi_min = "A" no_assert = false show_closures = true total_average = true # ============================================================================= # Xenon Configuration # ============================================================================= [tool.xenon] max_absolute = "B" max_modules = "A" max_average = "A" exclude = ["tests/*", ".venv/*", "vulture_whitelist.py", "alembic/*"] ignore = [] # ============================================================================= # Deptry Configuration # ============================================================================= [tool.deptry] exclude = ["tests", ".venv", "vulture_whitelist.py", "alembic"] extend_exclude = ["conftest.py", "setup.py"] known_first_party = ["roboco"] [tool.deptry.per_rule_ignores] # DEP002: Dependencies not directly imported but used at runtime or via CLI DEP002 = [ # Runtime server/driver dependencies (used by frameworks, not imported) "uvicorn", "websockets", "asyncpg", "hiredis", "python-multipart", # Database migrations (CLI tool) "alembic", # Auth libraries (used via passlib[bcrypt], python-jose[cryptography]) "python-jose", "passlib", # LLM utilities (embeddings/token counting) "openai", "tiktoken", # Retry logic (used in production services) "tenacity", # Build-time pin, not imported — see the comment next to the torch # entry in [project.dependencies] for the full rationale. Removing # it from deps caused uv to pull the full CUDA stack. "torch", # Dev tools (CLI, not imported) "pytest", "pytest-asyncio", "pytest-cov", "pytest-xdist", "factory-boy", "faker", "ruff", "mypy", "vulture", "bandit", "pip-audit", "radon", "xenon", "deptry", # CLI tool — invoked as `lint-imports` from the Makefile / quality gate "import-linter", "ipython", "rich", # Type stubs (used by mypy) "types-redis", "types-passlib", "types-python-jose", # Documentation (CLI tools) "mkdocs", "mkdocs-material", "mkdocstrings", ] # DEP003: Starlette is a transitive dep of FastAPI, but BaseHTTPMiddleware is needed DEP003 = ["starlette"] [dependency-groups] dev = [ "pytest>=9.0.3", "pytest-asyncio>=1.3.0", "pytest-cov>=7.1.0", ] # ============================================================================= # Import Linter (architectural boundaries) # ============================================================================= [tool.importlinter] root_package = "roboco" [[tool.importlinter.contracts]] name = "Gateway layer must not import from API routes or MCP servers" type = "forbidden" source_modules = ["roboco.services.gateway"] forbidden_modules = ["roboco.api.routes", "roboco.mcp"] [[tool.importlinter.contracts]] name = "Services must not import from API routes" type = "forbidden" source_modules = ["roboco.services"] forbidden_modules = ["roboco.api.routes"] # ============================================================================= # Roboco Commit Validator # ============================================================================= [tool.roboco.commits] subject_min_chars = 20 banned_words = ["wip", "tmp", "asdf", "oops", "fix", "update", "change", "stuff", "things"] prefer_conventional = true