Files
routstr-core/pyproject.toml
T
9qeklajc 250864b45d Support PostgreSQL for the application database
SQLite stores every INTEGER as 64-bit, so a set of portability defects stayed
invisible until the application database ran on PostgreSQL:

- Monetary columns hold millisatoshis but mapped to INT4, capping a key balance
  at 2_147_483_647 msats (~0.0215 BTC); asyncpg rejects anything larger. Unix
  timestamp columns had the same width, so long-dated expiries and the 2038
  boundary would overflow. Both are widened to BIGINT by a migration that is a
  no-op on SQLite, where the width does not exist.
- Secret.nsec_state was a bare Python Enum, which makes PostgreSQL demand a
  native `nsecstate` type that the migration never created, so every read or
  write of the secrets singleton failed and took node bootstrap with it. It is
  now a non-native enum, rendering VARCHAR on both backends.
- Model-path persistence imported the SQLite-only INSERT .. ON CONFLICT
  construct, which does not compile against PostgreSQL. The upsert is now built
  for the dialect bound to the session.
- The Alembic version-clear recovery path built a sync engine from the async
  URL, which left postgresql+asyncpg intact and raised MissingGreenlet. It now
  runs through the configured async driver.
- The WAL PRAGMA is gated on the bound dialect rather than a DATABASE_URL
  prefix. SQLite-only Cashu wallet maintenance stays scoped to
  .wallet/*.sqlite3 and the log-derived analytics index is untouched.
- PostgreSQL migration failures and unknown revisions now fail closed instead
  of stamping head. PostgreSQL rolls back DDL transactionally, so stamping
  after an error can mark migrations applied that never committed. Existing
  SQLite recovery behaviour is unchanged.

Adds asyncpg and two test suites: dialect-portability invariants that run in the
normal suite, and an opt-in integration suite covering the full Alembic chain
plus the billing, reservation, refund, invoice and payout paths against a real
server, enabled by ROUTSTR_TEST_POSTGRES_URL and skipped without it.
2026-09-30 13:20:02 +00:00

121 lines
3.5 KiB
TOML

[project]
name = "routstr"
version = "0.4.7"
description = "Payment proxy for your LLM endpoint using cashu and nostr."
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"fastapi[standard-no-fastapi-cloud-cli]>=0.141",
"aiosqlite>=0.20",
"asyncpg>=0.30", # PostgreSQL backend for DATABASE_URL=postgresql+asyncpg://
"sqlmodel>=0.0.42", # Python 3.14 deferred-annotation support
"httpx[socks]>=0.28.1",
"h11>=0.16",
"greenlet>=3.2.1",
"alembic>=1.13",
"python-json-logger>=2.0.0",
"cashu>=0.20",
"marshmallow>=3.13,<4.0",
"websockets>=12.0",
"nostr-sdk>=0.45.1,<0.46",
"mdurl==0.1.2",
"pillow>=10",
"openai>=1.98.0",
"litellm>=1.101.2,<1.102",
"orjson>=3.10",
]
[dependency-groups]
dev = [
"mypy>=1.15.0",
"ruff>=0.11.6",
"openai>=1.76.0",
"pytest>=8.0.0",
"pytest-asyncio>=0.24.0",
"pytest-cov>=6.1.1",
"httpx>=0.25.2",
"psutil>=5.9.0",
"aiohttp>=3.9.0",
"pytest-benchmark>=4.0.0",
"routstr",
]
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = "test_*.py"
python_classes = "Test*"
python_functions = "test_*"
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
addopts = [
"-v",
"--tb=short",
"--strict-markers",
"--disable-warnings",
"-p",
"no:warnings",
]
markers = [
"asyncio: marks tests as async (deselect with '-m \"not asyncio\"')",
"integration: marks tests as integration tests (deselect with '-m \"not integration\"')",
"unit: marks tests as unit tests",
"slow: marks tests as slow running (deselect with '-m \"not slow\"')",
"requires_real_mint: marks tests that require a running Cashu mint instance",
"requires_docker: marks tests that require Docker services running (deselect with '-m \"not requires_docker\"')",
"performance: marks tests that measure performance metrics",
]
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[tool.setuptools]
packages = ["routstr"]
[tool.ruff.lint]
select = ["E", "F", "I"]
ignore = ["E501"]
exclude = ["examples"]
[tool.mypy]
python_version = "3.11"
ignore_missing_imports = true
disallow_untyped_defs = true
check_untyped_defs = true
disallow_untyped_calls = true
disallow_incomplete_defs = true
disallow_untyped_decorators = true
[tool.uv.sources]
routstr = { workspace = true }
# Security floors. cashu 0.20.x caps httpx, h11, fastapi, cryptography,
# setuptools and wheel below their patched versions. routstr uses only cashu's
# wallet modules, not its server paths, so the caps are safe to lift here.
[tool.uv]
# coincurve 20's build config uses cmake.verbose, removed in
# scikit-build-core 0.10. This keeps its source build working on Python 3.14.
build-constraint-dependencies = ["scikit-build-core<0.10"]
override-dependencies = [
# httpx 0.28 (required by litellm 1.93) removed the `proxies` kwarg cashu
# passes on every mint call; routstr/cashu_compat.py restores it for cashu.
"httpx[socks]>=0.28.1,<1.0",
"importlib-metadata>=8.0.0,<9.0",
"h11>=0.16.0",
"fastapi[standard-no-fastapi-cloud-cli]>=0.141",
"cryptography>=49.0.0",
"setuptools>=83.0.0",
"wheel>=0.46.2",
]
# Transitive deps whose dependents allow the patched version but don't require
# it. Constraints raise the floor without bypassing any upstream pin.
constraint-dependencies = [
"starlette>=1.3.1",
"httpcore>=1.0.9", # 1.0.8 caps h11<0.15
# 1.76 is the first grpcio-tools release with CPython 3.14 wheels.
"grpcio>=1.76.0,<2.0.0",
"grpcio-tools>=1.76.0,<2.0.0",
]