Files
dev-puppeteer/my-deepagent/pyproject.toml
chungyeong e21a5241bf feat(my-deepagent): v0.2 PR #1 — Postgres migration (ahead of M8-Py FastAPI)
Switches the production backing store from SQLite to PostgreSQL 16, per DR-2.
The migration trigger is two concurrent writers on the my-deepagent ORM
tables — which first appears with FastAPI (M8-Py). Doing the cut now keeps
the surface area small while M8-Py is still planning.

Production deps: `asyncpg`, `psycopg[binary]`, `langgraph-checkpoint-postgres`.
Test deps: `aiosqlite` (the bulk of unit + integration tests stay on sqlite
tmp_path for speed; the E2E suite and the new checkpointer tests exercise
the live Postgres path).

Highlights
- `persistence/db.py`: dialect-aware connect listener. SQLite still gets
  WAL + busy_timeout=5000 + foreign_keys=ON; Postgres gets `SET TIME ZONE 'UTC'`.
  Added `Database.dialect_name` + `drop_schema` (test-only).
- `persistence/checkpointer.py`: SqliteSaver → AsyncPostgresSaver. API is
  now async (`async with`) and takes a connection string. SQLAlchemy URL
  prefixes (`+asyncpg`, `+psycopg`) are auto-stripped to a plain libpq DSN
  (`_to_psycopg_dsn` helper, 4 unit tests).
- `persistence/upsert.py` (new): `insert_for(session)` — dialect-aware UPSERT
  helper. Picks `postgresql.insert` or `sqlite.insert` based on the bound
  engine. Replaces 5 hardcoded `sqlite_insert` call sites in `budget.py`,
  `recovery.py`, `cli/doctor.py`.
- `persistence/models.py`: `RunRow` partial unique index declares both
  `postgresql_where=` and `sqlite_where=` for cross-dialect correctness.
- `config.py`: default `database_url` now
  `postgresql+asyncpg://devflow:devflow@localhost:55432/mydeepagent`. v3
  `devflow` DB preserved untouched; v4 lives in a fresh `mydeepagent` DB.
- `cli/doctor.py` check 8: dialect-aware DB liveness probe. Postgres path
  runs `SELECT 1` (pg_isready equivalent); SQLite keeps `PRAGMA integrity_check`.
- `alembic/env.py`: env-aware URL resolution (`MYDEEPAGENT_DATABASE_URL` >
  `DATABASE_URL` > default). Async driver prefixes are mapped to the sync
  equivalents alembic needs.
- `alembic/versions/9f2a6c79667e_v0_2_baseline_schema_postgres.py` (new):
  fresh baseline autogenerated against live Postgres. Old SQLite migrations
  (`79945fdc2649`, `839f2233e346`) deleted — v0.2 starts a clean history.
- `tests/conftest.py` (new): `pg_db_url` async fixture creates a fresh DB
  per test against docker-compose `devflow-postgres` and drops it on
  teardown after terminating lingering backends.
- `tests/integration/test_checkpointer.py`: rewritten for AsyncPostgresSaver
  (4 pure DSN-converter unit tests + 3 async context-manager integration tests).
- `tests/integration/test_e2e_workflow.py`: switched to `pg_db_url`. Real
  OpenRouter E2E now exercises the production Postgres path end-to-end.

Recovery
- Previous SQLite database at the platformdirs data_dir is NOT auto-migrated;
  v0.1.0 was the only release that wrote to it. Set
  `MYDEEPAGENT_DATABASE_URL=sqlite+aiosqlite:///<path>` to read it.
- The v3 `devflow` Postgres DB is preserved untouched (separate database
  name); to inspect: `psql -h localhost -p 55432 -U devflow -d devflow`.

Gates
- ruff check + ruff format --check + mypy --strict: PASS (102 source files)
- pytest non-E2E: 576 PASS (5.46 s)
- pytest E2E real OpenRouter on Postgres: 1 PASS (122.93 s, ~$0.05/run)

--no-verify: lefthook still TS-only (deleted in 0e61b2d but still queryable
in git history).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 18:11:19 +09:00

67 lines
1.7 KiB
TOML

[project]
name = "my-deepagent"
version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.12"
dependencies = [
"asyncpg>=0.30",
"psycopg[binary]>=3.2",
"alembic>=1.14",
"greenlet>=3.0",
"sqlalchemy[asyncio]>=2.0",
"httpx>=0.28",
"jsonschema>=4.23",
"keyring>=25.7",
"langchain>=0.3.0,<2.0.0",
"langchain-core>=0.3.0,<2.0.0",
"langchain-openai>=0.3.0,<2.0.0",
"langgraph>=0.2.0",
"langgraph-checkpoint-postgres>=2.0.0",
"openai>=1.0.0",
"platformdirs>=4.9",
"prompt-toolkit>=3.0",
"pydantic>=2.9",
"pydantic-settings>=2.6",
"pyyaml>=6.0",
"rich>=13.9",
"structlog>=24.4",
"typer>=0.14",
"zstandard>=0.23",
"deepagents>=0.6.1,<0.7.0",
]
[project.scripts]
mydeepagent = "my_deepagent.cli.main:app"
[build-system]
requires = ["uv_build>=0.9.28,<0.10.0"]
build-backend = "uv_build"
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
addopts = "-v --strict-markers"
markers = [
"integration: marks tests as integration tests that make real external API calls (deselect with '-m not integration')",
]
[dependency-groups]
dev = [
# aiosqlite is a TEST-ONLY dependency: production runs on Postgres
# (asyncpg, see [project.dependencies]) but the bulk of the test suite uses
# sqlite+aiosqlite tmp_path URLs for speed + isolation simplicity. Live
# Postgres validation happens via the E2E suite (real OpenRouter +
# docker-compose Postgres).
"aiosqlite>=0.20",
"mypy>=1.13",
"pre-commit>=4.0",
"pytest>=8.3",
"pytest-asyncio>=0.24",
"pytest-httpx>=0.34",
"pytest-timeout>=2.4.0",
"respx>=0.21",
"ruff>=0.8",
"types-jsonschema>=4.26.0.20260508",
"types-pyyaml>=6.0.12.20260510",
]