Ctrl K
ComunidadGuías…

pyproject.toml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
[project]
name = "spendif-ai"
version = "2026.11.0"
description = "Unified personal finance ledger with hybrid LLM pipeline"
readme = "README.md"
license = { file = "LICENSE" }
authors = [{ name = "Luigi Corsaro" }]
# Upper bound widened to 3.15 on 2026-09-22: Arch Linux has shipped Python
# 3.14 since August, so a cap at <3.14 made the app uninstallable on every
# rolling distribution - the venv is created against the system interpreter.
# The CI matrix runs the suite on 3.14 so this stays a checked claim.
requires-python = ">=3.12,<3.15"
dependencies = [
    # Core data
    "pandas>=2.2",
    "openpyxl>=3.1",
    # pandas reads legacy .xls through xlrd; the upload accepts that format.
    "xlrd>=2.0.1",
    "chardet>=5.0",
    # LLM backends (direct SDKs — no LangChain)
    "openai>=1.30",
    "anthropic>=0.28",
    "requests>=2.31",
    # Version floor is the first one the prebuilt wheel indexes below carry
    # for every platform we ship. See [tool.uv.sources].
    "llama-cpp-python>=0.3.35",
    # The app's own ears (spec 32, 3): whisper.cpp, MIT. Prebuilt wheels for
    # every platform we ship; none for Intel macOS, which we do not ship.
    "pywhispercpp>=1.5.1; sys_platform != 'darwin' or platform_machine == 'arm64'",
    # Database
    "sqlalchemy>=2.0",
    "alembic>=1.13",
    # Validation
    "pydantic>=2.0",
    "pyyaml>=6.0",
    # Configuration
    "python-dotenv>=1.0",
    # UI
    "streamlit>=1.35",
    "plotly>=5.20",
    # Reports
    "jinja2>=3.1",
    # REST API
    "fastapi>=0.111",
    "uvicorn[standard]>=0.29",
    "python-multipart>=0.0.9",
    "httpx>=0.27",
    # Security pin: idna<3.15 carries CVE-2026-45409 (pip-audit gate).
    # Floor declared here even though idna is a transitive dep of httpx/requests
    # so a fresh `uv lock` resolves to >=3.15.
    "idna>=3.15",
    # Security pins (transitive deps of streamlit) — pip-audit gate.
    # pillow<12.3.0 carries PYSEC-2026-2253..2257 / 3451..3454; 12.3.0 carries
    # CVE-2026-112395, 112397, 112400, 112404, 112895..112897, 112899 (fixed in
    # 13.0.0, which needs streamlit>=1.66).
    # gitpython<3.1.58 carries GHSA-9rj7-rf2p-w77r, GHSA-4gmw-gg2m-w46p,
    # CVE-2026-76217, GHSA-wvpp-8hx9-p66j, GHSA-jm78-9fvv-mhgr (all fixed in 3.1.58);
    # 3.1.59 carries PYSEC-2026-3982, PYSEC-2026-3983, PYSEC-2026-3984 (fixed in 3.1.60);
    # 3.1.62 carries CVE-2026-114145 (fixed in 3.2.1).
    "pillow>=13.0.0",
    "gitpython>=3.2.1",
    # certifi<2026.10.19 carries PYSEC-2026-4320 (pip-audit gate); transitive
    # dep of requests/httpx, floor declared so a fresh `uv lock` resolves to it.
    "certifi>=2026.10.19",
    # Security pin: cryptography<50.0.0 carries PYSEC-2026-3552 (pip-audit gate).
    # Transitive dep of azure-identity/msal; floor declared here so a fresh
    # `uv lock` resolves to >=50.0.0.
    "cryptography>=50.0.0",
    # Security pin: opentelemetry-api<1.45.1 carries CVE-2026-114603 (pip-audit
    # gate). Transitive dep of azure-ai-ml through azure-monitor-opentelemetry.
    "opentelemetry-api>=1.45.1",
    # Security pin: filelock<4.2.1 carries CVE-2026-112907, 112913, 112940
    # (pip-audit gate). Transitive dep of huggingface-hub.
    "filelock>=4.2.1",
    # Testing
    "pytest>=8.0",
    "pytest-cov>=5.0",
    "huggingface-hub>=1.8.0",
    "azure-ai-ml>=1.32.0",
    "azure-identity>=1.25.3",
]

[project.optional-dependencies]
desktop = [
    "pywebview>=5.0",
    "pyinstaller>=6.0",
    # GUI backend for pywebview:
    # - macOS uses Cocoa (built into pyobjc) — no extra dep
    # - Windows uses WebView2 (system component) — no extra dep
    # - Linux uses GTK. We do NOT pip-install PyGObject/pycairo here —
    #   instead the .deb / .rpm depends on system `python3-gi` and
    #   `python3-cairo`, and launch.sh creates the venv with
    #   `--system-site-packages` so those system modules are visible
    #   inside the venv. Reasons:
    #     • pycairo has no Linux wheels on PyPI — every pip install
    #       triggers a source build that needs libcairo2-dev.
    #     • PyGObject wheels exist but require matching system libs
    #       (gir1.2-webkit2-4.1, libgirepository) which we already
    #       depend on from the .deb side — there is no reason to
    #       install another copy via pip.
]

# ─────────────────────────────────────────────────────────────────────────────
#  llama-cpp-python: install a prebuilt wheel, never compile on the user machine
#
#  PyPI ships this package as a source distribution only, so `uv sync` compiled
#  it on every machine that installed Spendif.ai. That needs a full C/C++
#  toolchain and CMake, which a normal desktop does not have: on 2026-09-22 it
#  was the cause of three failed installations out of four (a Ubuntu without
#  g++, a Windows without the Visual Studio Build Tools) and of a 3-to-8-minute
#  first launch where it did work.
#
#  Linux and Windows therefore take the prebuilt CPU wheels that upstream
#  publishes. macOS deliberately does NOT:
#    • the macOS wheel is built for CPU, and taking it would silently drop
#      Metal inference on Apple Silicon, our fastest platform;
#    • the `metal` wheel published upstream for 0.3.35 is a malformed zip
#      (uv: "invalid Zip archive: Invalid local file header"; Python's more
#      tolerant zipfile opens it, which is how a broken file gets published);
#    • and no macOS user ever compiles it anyway - the DMG is built by CI with
#      PyInstaller, so the compilation happens on a runner, once, with the
#      toolchain Xcode already provides.
#
#  On Linux and Windows the CPU wheel means an AMD GPU stays unused: there is
#  no prebuilt Vulkan or ROCm wheel upstream. Building one is a separate piece
#  of work; until then the app says so instead of reporting "no GPU".
#
#  The index is declared `explicit`, so ONLY this package is ever fetched from
#  it and no other dependency can be shadowed by a same-named file published
#  there.
#
#  INTEGRITY: unlike PyPI, this index publishes no checksums, so uv.lock
#  records its wheel URLs WITHOUT a sha256 - the only entries in the whole
#  lockfile that have none. The version is pinned, the bytes are not: a GitHub
#  release asset can be replaced in place by whoever owns it. The checksums we
#  observed are therefore committed in packaging/llama_wheel_hashes.txt and
#  checked by tools/check_llama_wheels.py, so a swap upstream turns CI red
#  instead of reaching a user.
# ─────────────────────────────────────────────────────────────────────────────
[[tool.uv.index]]
name = "llama-cpp-cpu"
url = "https://abetlen.github.io/llama-cpp-python/whl/cpu/"
explicit = true

[tool.uv.sources]
llama-cpp-python = [
    { index = "llama-cpp-cpu", marker = "sys_platform != 'darwin'" },
]

# The platforms we actually ship: arm64 DMG, x86_64 and arm64 .deb, x86_64 MSIX
# and RPM. Declaring them keeps the lock honest - a resolution that cannot be
# satisfied on one of these fails here, at lock time, instead of on a user
# machine. Intel Macs are deliberately absent (see getting-started.html).
[tool.uv]
environments = [
    "sys_platform == 'darwin' and platform_machine == 'arm64'",
    "sys_platform == 'linux'",
    "sys_platform == 'win32'",
]

[tool.pytest.ini_options]
markers = [
    "llm: tests requiring a running LLM backend (Ollama/OpenAI)",
    "siri_files: the test reads the real Siri folders, by design (only the path builder)",
]
addopts = "-m 'not llm'"
testpaths = ["tests"]

[tool.coverage.run]
omit = [
    "app.py",           # Streamlit entrypoint — top-level st.* calls, not importable in pytest
    "desktop/*",        # GUI launcher — requires pywebview display, not testable in headless CI
    "quarantine/*",     # archived/dead code
    "benchmark/*",      # standalone benchmark scripts
    "tools/*",          # dev tooling
]

[tool.bandit]
exclude_dirs = ["tests", "tools", "benchmark", "scripts", "quarantine"]
skips = ["B101"]  # assert used in tests / dev tooling