[build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [tool.poetry] name = "timmy-time" version = "1.0.0" description = "Mission Control for sovereign AI agents" readme = "README.md" license = "MIT" authors = ["Alexander Whitestone"] homepage = "https://alexanderwhitestone.github.io/Timmy-time-dashboard/" repository = "https://github.com/AlexanderWhitestone/Timmy-time-dashboard" packages = [ { include = "config.py", from = "src" }, { include = "brain", from = "src" }, { include = "dashboard", from = "src" }, { include = "infrastructure", from = "src" }, { include = "integrations", from = "src" }, { include = "spark", from = "src" }, { include = "timmy", from = "src" }, { include = "timmy_serve", from = "src" }, ] [tool.poetry.dependencies] python = ">=3.11,<4" agno = { version = ">=1.4.0,<2.0", extras = ["sqlite"] } ollama = ">=0.3.0,<1.0" openai = ">=1.0.0" fastapi = ">=0.115.0,<1.0" uvicorn = { version = ">=0.32.0,<1.0", extras = ["standard"] } jinja2 = ">=3.1.0" httpx = ">=0.27.0" python-multipart = ">=0.0.12" typer = ">=0.12.0" rich = ">=13.0.0" pydantic-settings = ">=2.0.0,<3.0" # Optional extras redis = { version = ">=5.0.0", optional = true } celery = { version = ">=5.3.0", extras = ["redis"], optional = true } python-telegram-bot = { version = ">=21.0", optional = true } "discord.py" = { version = ">=2.3.0", optional = true } airllm = { version = ">=2.9.0", optional = true } pyttsx3 = { version = ">=2.90", optional = true } sentence-transformers = { version = ">=2.0.0", optional = true } numpy = { version = ">=1.24.0", optional = true } requests = { version = ">=2.31.0", optional = true } GitPython = { version = ">=3.1.40", optional = true } pytest = { version = ">=8.0.0", optional = true } pytest-asyncio = { version = ">=0.24.0", optional = true } pytest-cov = { version = ">=5.0.0", optional = true } pytest-timeout = { version = ">=2.3.0", optional = true } selenium = { version = ">=4.20.0", optional = true } pytest-randomly = { version = ">=3.16.0", optional = true } pytest-xdist = { version = ">=3.5.0", optional = true } [tool.poetry.extras] telegram = ["python-telegram-bot"] discord = ["discord.py"] bigbrain = ["airllm"] voice = ["pyttsx3"] celery = ["celery"] embeddings = ["sentence-transformers", "numpy"] git = ["GitPython"] dev = ["pytest", "pytest-asyncio", "pytest-cov", "pytest-timeout", "pytest-randomly", "pytest-xdist", "selenium"] [tool.poetry.group.dev.dependencies] pytest = ">=8.0.0" pytest-asyncio = ">=0.24.0" pytest-cov = ">=5.0.0" pytest-timeout = ">=2.3.0" selenium = ">=4.20.0" pytest-randomly = "^4.0.1" pytest-xdist = "^3.8.0" ruff = ">=0.8.0" [tool.poetry.scripts] timmy = "timmy.cli:main" timmy-serve = "timmy_serve.cli:main" [tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["src", "tests"] asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" timeout = 30 timeout_method = "signal" timeout_func_only = false addopts = "-v --tb=short --strict-markers --disable-warnings --durations=10" markers = [ "unit: Unit tests (fast, no I/O)", "integration: Integration tests (may use SQLite)", "functional: Functional tests (real HTTP requests, no mocking)", "e2e: End-to-end tests (full system, may be slow)", "dashboard: Dashboard route tests", "slow: Tests that take >1 second", "selenium: Requires Selenium and Chrome (browser automation)", "docker: Requires Docker and docker-compose", "ollama: Requires Ollama service running", "external_api: Requires external API access", "skip_ci: Skip in CI environment (local development only)", ] [tool.ruff] line-length = 100 target-version = "py311" src = ["src", "tests"] [tool.ruff.lint] select = ["E", "F", "I", "UP", "B", "S"] ignore = [ # Mapped from existing bandit skips: B101→S101, B104→S104, etc. "S101", "S104", "S307", "S310", "S324", "S601", "S608", # Project patterns: graceful degradation (try/except pass), FastAPI Depends() "S110", "S112", "B008", # Subprocess usage in scripts/infrastructure "S603", "S607", # Non-cryptographic random is fine for non-security contexts "S311", # Line length handled by formatter; long strings/URLs can't always be broken "E501", ] [tool.ruff.lint.isort] known-first-party = ["brain", "config", "dashboard", "infrastructure", "integrations", "spark", "swarm", "timmy", "timmy_serve"] [tool.ruff.lint.per-file-ignores] "tests/**" = ["S"] [tool.coverage.run] source = ["src"] omit = [ "*/tests/*", ] [tool.coverage.report] show_missing = true skip_empty = true precision = 1 exclude_lines = [ "pragma: no cover", "if __name__ == .__main__.", "if TYPE_CHECKING:", "raise NotImplementedError", "@abstractmethod", ] # Fail CI if coverage drops below this threshold fail_under = 73 [tool.coverage.html] directory = "htmlcov" [tool.coverage.xml] output = "coverage.xml"