[project] name = "traceroot-platform" version = "0.1.0" description = "TraceRoot - open source AI agent improvement platform. Find the patterns in your agent's production issues. Ship verified fixes with your coding agent." readme = "README.md" requires-python = ">=3.11" dependencies = [ # Core "pydantic>=2.12", "pydantic-settings>=2.14.2", "cuid2>=2.0.0", # Database (PostgreSQL access moved to Prisma in ui/) "clickhouse-connect>=0.7", "boto3>=1.34", # REST API "fastapi>=0.109", "uvicorn[standard]>=0.27", "python-multipart>=0.0.31", # Upper-bounded: rate_limit.py rides slowapi private internals # (_check_request_limit, request.state.view_rate_limit, limiter._storage_dead); # a minor bump could silently change shadow/header behavior under swallow_errors=True. "slowapi>=0.1.9,<0.2", # OpenTelemetry "protobuf>=6.33.5", "opentelemetry-proto>=1.20.0", "opentelemetry-api>=1.20.0", # Task Queue "celery[redis]>=5.3", "redis>=5.0", # Utils "python-dotenv>=1.2.2", "watchfiles>=0.21", "httpx>=0.28.1", # Offline verification of the CLI access JWTs against the app's JWKS # (EdDSA/Ed25519 needs the crypto extra -> cryptography). "pyjwt[crypto]>=2.9", # SQL validation. Pinned EXACTLY, not to a range: the validator's function # gate is built on how this version normalises function names (uniq -> # APPROX_DISTINCT, formatDateTime -> TimeToStr, and the rest documented in # ALLOWED_FUNCTIONS), so a lockfile refresh that moved the parse could change # what the allowlist admits without a line of our code changing. Bump it # deliberately, with the suite as the gate. "sqlglot==26.33.0", # Tokenization "tiktoken>=0.5.0", # PostgreSQL (for standard model pricing lookup) "psycopg2-binary>=2.9", "bullmq>=2.25,<3", ] [dependency-groups] dev = [ "pytest>=9.0.3", "pytest-asyncio>=0.21", "pytest-cov>=4.0", "coverage>=7.0", "pre-commit>=4.0", "respx>=0.20", "ruff>=0.15.0", "mypy>=1.0", # Parsing the mounted ClickHouse config in tests. defusedxml rather than the stdlib # because the native xml modules are XXE-prone and the scanners reject them. "defusedxml>=0.7", ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["backend/db", "backend/rest", "backend/worker", "tmux_tools"] [tool.pytest.ini_options] testpaths = ["tests"] asyncio_mode = "auto" addopts = "-v --tb=short -ra" pythonpath = ["backend"] [tool.coverage.run] source = ["backend"] branch = true omit = ["tests/*", "*/tests/*"] [tool.coverage.report] show_missing = true sort = "cover" [tool.ruff] line-length = 100 target-version = "py311" [tool.ruff.lint] select = [ "E", # pycodestyle errors "W", # pycodestyle warnings "F", # pyflakes "I", # isort "B", # flake8-bugbear "UP", # pyupgrade "N", # pep8-naming "SIM", # flake8-simplify "RUF", # ruff-specific rules ] ignore = [ "E501", # line-too-long (formatter handles this) "B008", # function-call-in-default-argument (FastAPI Query/Depends pattern) "B905", # zip-without-explicit-strict "RUF005", # collection-literal-concatenation (list + list is fine) "RUF022", # unsorted-all (__all__ grouped by category) "SIM108", # if-else-block-instead-of-if-exp (readability preference) "SIM105", # suppressible-exception "SIM116", # if-else-block-instead-of-dict-lookup ] [tool.ruff.lint.per-file-ignores] "backend/rest/main.py" = ["E402"] # load_dotenv() must run before imports "backend/worker/celery_app.py" = ["E402"] # load_dotenv() must run before imports "examples/**/*.py" = ["E402"] # examples need dotenv/traceroot init before other imports [tool.ruff.lint.isort] known-first-party = ["db", "rest", "shared", "worker", "traceroot"] [tool.ruff.format] quote-style = "double" indent-style = "space" line-ending = "lf" [tool.mypy] python_version = "3.11" strict = false warn_return_any = true warn_unused_configs = true ignore_missing_imports = true