Skip to content

feat(async): AsyncMemory facade + 3-tier cancellation contract (M3.2 PR-A)#3

Merged
Ketankhunti merged 2 commits into
mainfrom
005-async-fastapi
May 1, 2026
Merged

feat(async): AsyncMemory facade + 3-tier cancellation contract (M3.2 PR-A)#3
Ketankhunti merged 2 commits into
mainfrom
005-async-fastapi

Conversation

@Ketankhunti

Copy link
Copy Markdown
Owner

Summary

PR-A of M3.2 (005-async-fastapi). Ships an async/await-native mirror of
openmem.Memory with zero changes to the sync surface. PR-B (the FastAPI
server) will follow once this lands.

What's new

openmem.AsyncMemory — async facade

  • Zero-I/O constructor; pools and HTTP clients are built lazily on the
    first verb call (data-model AM-INV-3 / C-LIFE-1).
  • async with AsyncMemory(...) as mem: context manager + idempotent
    close().
  • Cross-loop guard: the running loop id is captured on the first verb
    call; awaiting a verb on a different loop raises RuntimeError
    before any backend call (C-LOOP-1).
  • add() validates user_id centrally so the threadwrap tier can't
    smuggle empty/whitespace ids past require_user_id (C-ERR-3).

Three async adapters

Tier Provider(s) Backend
Native postgres asyncpg + pgvector pool; SQL placeholders translated %s → $1 from a new shared openmem.adapters._postgres_sql module so sync + async use one source of truth. Lazy pool init guarded by asyncio.Lock to prevent fan-out races starving max_connections.
Native passthrough httpx.AsyncClient; identical timeout / retry / header conventions to sync passthrough.py.
Threadwrap mem0, supermemory, letta Per-instance ThreadPoolExecutor; verbs dispatch via executor.submit() + asyncio.wrap_future() so the orphan-completion DEBUG log fires after the worker thread actually finishes.

Cancellation contract (contracts/async-memory.md §3)

ID Tier Guarantee
C-CAN-1 Native Awaiter receives CancelledError ≤ 50 ms.
C-CAN-2 Native Pool/socket released to its pool ≤ 500 ms post-cancel.
C-CAN-3 postgres Server-side query absent from pg_stat_activity ≤ 1 s.
C-CAN-4 Threadwrap Awaiter cancels immediately; worker thread completes in the background; orphan completion logged at DEBUG.
C-CAN-5 All Subsequent verbs on the same AsyncMemory succeed (no pool corruption).

Packaging

  • New [async] install extra: asyncpg>=0.29, httpx>=0.27.
  • from openmem import AsyncMemory raises a clear ImportError
    containing pip install 'openmem[async]' when the extra is missing
    (FR-026 / C-EXT-1..3). The bare openmem import triggers no
    async dependency resolution.

Sync compatibility (non-negotiable)

  • openmem.Memory is byte-identical — every public signature, default,
    and behavior unchanged.
  • New regression test tests/async/test_memory_signatures.py commits a
    JSON snapshot at tests/async/_signatures_baseline.json; any drift
    fails the gate (SC-008 / FR-011).
  • Bare-install packaging guard runs in a throwaway venv to prove the
    extras contract end-to-end (tests/async/test_packaging_extras.py).
  • Full sync suite still green: 365 passed, 21 skipped (no removals,
    no new failures).

Test results

Suite Result
Sync regression 365 passed, 21 skipped
Async contract suite 52 passed, 5 skipped (3 live-only + 2 letta-skips for get)
Cancellation suite (OMP_LIVE=1) 6 / 6 passed
Full suite + coverage gate 419 passed, 26 skipped, 87.44% ≥ 85%

Version

0.1.00.4.0 (sdk-python/pyproject.toml + openmem.__version__).

Files of interest

  • New: sdk-python/openmem/async_memory.py
  • New adapters: sdk-python/openmem/adapters/async_{postgres,passthrough,threadwrap,base}.py
  • New shared: sdk-python/openmem/adapters/_postgres_sql.py, _validation.py
  • New tests: sdk-python/tests/async/ (10 files, 52 tests + signature snapshot)
  • New spec: specs/005-async-fastapi/
  • Updated docs: root README.md, sdk-python/README.md, CHANGELOG.md

Out of scope (PR-B)

Phase 6 FastAPI server (omp serve, T034–T051) and PR-B polish
(T057–T062). Tracked in specs/005-async-fastapi/tasks.md.

Checklist

  • Async test suite passes (52 / 5)
  • Sync regression preserved (365 / 21)
  • Live-mode cancellation tests pass with OMP_LIVE=1 (6 / 6)
  • Coverage ≥ 85% (87.44%)
  • Sync Memory signature snapshot committed
  • Bare-install ImportError contract enforced via venv subprocess
  • Version bumped, CHANGELOG updated, READMEs updated

ketan7506 added 2 commits May 2, 2026 01:34
…PR-A)

PR-A delivers the async/await-native mirror of openmem.Memory:

* AsyncMemory facade — zero-I/O constructor, lazy backend init,
  async context manager, cross-loop guard, idempotent close.
* AsyncPostgresAdapter on asyncpg + pgvector (native cancel; SQL
  placeholders translated %s -> $1 from shared _postgres_sql so
  sync/async share one source of truth).
* AsyncPassthroughAdapter on httpx.AsyncClient (native cancel).
* AsyncThreadwrapAdapter wrapping mem0 / supermemory / letta sync
  adapters via a per-instance ThreadPoolExecutor (best-effort
  cancel; orphan completion logged at DEBUG).

Cancellation contract C-CAN-1..5:
  - native awaiter receives CancelledError <= 50 ms
  - native pool/socket released <= 500 ms post-cancel
  - postgres server-side query absent from pg_stat_activity <= 1 s
  - threadwrap awaiter cancels immediately; worker keeps running
  - subsequent verbs on the same AsyncMemory succeed (no pool
    corruption)

Packaging:
  - new [async] install extra (asyncpg>=0.29, httpx>=0.27)
  - `from openmem import AsyncMemory` raises a clear ImportError
    containing `pip install 'openmem[async]'` when the extra is
    missing (FR-026 / C-EXT-1..3); bare openmem import triggers no
    async dependency resolution.

Sync compatibility:
  - openmem.Memory is byte-identical (signature snapshot committed
    at sdk-python/tests/async/_signatures_baseline.json; SC-008 /
    FR-011)
  - 365 sync tests still pass with no removals
  - Bare-install packaging guard runs in a throwaway venv

Results: 419 passed, 26 skipped, coverage 87.44% (>= 85% gate).
Version bump 0.1.0 -> 0.4.0.
@Ketankhunti
Ketankhunti merged commit 6b48263 into main May 1, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants