pyproject.toml
Rama main · réplica de lectura del repositorio original.
[project]
name = "niles-core"
version = "0.2.16"
description = "Local AI Butler"
license = "AGPL-3.0-only"
requires-python = ">=3.14"
dependencies = [
# Web Framework
"fastapi>=0.129.0",
"uvicorn[standard]>=0.41.0",
# Async HTTP Client + Retry
"httpx>=0.28.1",
"tenacity>=9.0.0",
# Database
"asyncpg>=0.31.0",
# Database migrations (Alembic runs sync via psycopg2)
"alembic>=1.13.0",
"sqlalchemy>=2.0.0",
"psycopg2-binary>=2.9.0",
# LLM Client (OpenAI-kompatibel für LM Studio)
"openai>=2.21.0",
"tiktoken>=0.8.0", # approximate token counting for the context budget
# MCP SDK
"mcp>=2.3.0,<3",
# Config
"pydantic-settings>=2.13.0",
"pyyaml>=6.0.3",
# Scheduling
"apscheduler>=3.11.2",
# Web UI (templates + static files)
"jinja2>=3.1.0",
"aiofiles>=24.0.0",
# Signed session cookies (Google OAuth)
"itsdangerous>=2.0",
# Password hashing (Argon2id)
"argon2-cffi>=25.1.0",
# RRULE expansion for recurring calendar events
"python-dateutil>=2.8.0",
# Structured JSON logging (12-factor XI)
"structlog>=24.1.0",
# Prometheus metrics
"prometheus-client>=0.21.0",
# WebSocket client (Signal integration)
"websockets>=14.0",
# MCP Web Search is built-in (src/niles/mcp/search)
# HTML text extraction for web fetch MCP server
"trafilatura>=2.0.0",
# Pin transitive lxml[html-clean] to patched release (GHSA-4jhm-jv67-739f)
"lxml-html-clean>=0.4.5",
# Robust JSON repair for malformed LLM tool-call output
"json-repair>=0.58.0",
# Notion workspace integration (RAG pipeline)
"notion-client>=2.0.0",
# Column-level credential encryption (Fernet AES-128-CBC + HMAC)
"cryptography>=44.0",
# Error tracking (opt-in via SENTRY_DSN)
"sentry-sdk[fastapi]>=2.0.0",
# Safe XML parsing (XXE protection for CalDAV responses)
"defusedxml>=0.7.0",
]
[project.optional-dependencies]
tracing = [
"langfuse>=5.0.4,<6",
]
dev = [
"pytest>=9.0.0",
"pytest-asyncio>=1.3.0",
"pytest-cov>=6.0.0",
"pytest-json-report>=1.5.0", # machine-readable eval pass counts for the LLM-eval gate
"httpx>=0.28.1", # For TestClient
"mypy>=1.14.0",
"pip-audit>=2.7.0",
"ruff>=0.9.0",
"anthropic>=0.40", # Claude-as-Judge E2E tests
"types-python-dateutil>=2.9.0.20260518",
"types-pyyaml>=6.0.12.20260518",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/niles"]
[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"
markers = [
"integration: requires Docker Compose infrastructure (deselect with -m 'not integration')",
"e2e: end-to-end agent pipeline tests (deselect with -m 'not e2e')",
"llm_judge: requires Ollama + Claude API key, slow (deselect with -m 'not llm_judge')",
"llm_eval: behavioral quality evals against live Ollama (deselect with -m 'not llm_eval')",
]
[tool.ruff]
target-version = "py314"
line-length = 120
[tool.ruff.lint]
select = ["E", "F", "W", "S"]
ignore = ["E501"] # Line length handled by ruff format
[tool.ruff.lint.per-file-ignores]
# Tests legitimately use assert, hardcoded credentials, verify=False, and try-except-pass
"tests/**/*.py" = ["S101", "S105", "S106", "S110", "S501"]
# Web route handlers use assert for type narrowing after auth checks
"src/niles/sources/web/_admin.py" = ["S101"]
"src/niles/sources/web/_chat.py" = ["S101"]
"src/niles/sources/web/_contacts.py" = ["S101"]
"src/niles/sources/web/_core.py" = ["S101"]
"src/niles/sources/web/_settings.py" = ["S101"]
"src/niles/sources/web/_vikunja.py" = ["S101"]
"src/niles/sources/web/_whatsapp.py" = ["S101"]
# CalDAV: uses defusedxml (S314 no longer needed)
# Dynamic SQL uses parameterized queries (where_clause built from internal logic, not user input)
"src/niles/contact_store.py" = ["S608"]
[tool.mypy]
python_version = "3.14"
warn_return_any = false
warn_unused_configs = true
ignore_missing_imports = true
check_untyped_defs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
# All per-module error-code suppressions removed in W18 — fixed at the source.
# Type stubs (types-python-dateutil, types-pyyaml) resolve the import-untyped cases.
[tool.coverage.run]
source = ["src/niles"]
omit = ["tests/*", "alembic/*"]
branch = true
[tool.coverage.report]
show_missing = true
fail_under = 77 # branch-inclusive; raised from 70 after the route/agent-loop test additions (W16/W17)
exclude_lines = [
"pragma: no cover",
"if __name__",
"if TYPE_CHECKING",
]