pyproject.toml
Rama main · réplica de lectura del repositorio original.
[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