## Description
`DirectMem0Adapter.close()` now deterministically drains or cancels
background writes and releases every initialized client/driver.
Fixes#2897
## Type of Change
- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)
## Changes Made
- Initialize the OpenAI client field to `None` so cleanup is safe before
or after initialization.
- Drain background tasks within a configurable 60-second default, cancel
tasks that exceed the timeout, await cancellation, and retain
completed/cancelled task status.
- Close Mem0, OpenAI, Qdrant, Neo4j, embedder, and graph resources
independently, including async close methods, while continuing cleanup
if one resource fails.
- Clear task and client references and keep `close()` idempotent.
- Add regression tests for task draining, timeout cancellation, all
resource cleanup, and repeated close calls.
## Testing
- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [x] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [x] Manual testing performed
### Test Output
```text
python -m pytest -q tests/test_memory/test_direct_mem0.py tests/test_memory/test_qdrant_env.py
52 passed
ruff check .
All checks passed!
ruff format --check .
1383 files already formatted
python -m mypy headroom
Success: no issues found in 515 source files
python -m pytest -q
Collection blocked in this Windows environment by 174 errors, primarily missing compiled headroom._core; 18 tests skipped.
```
## Real Behavior Proof
- Environment: Windows, Python 3.12, local DirectMem0Adapter instance
using real `httpx.Client` resources.
- Exact command / steps: Assigned real `httpx.Client()` instances to the
adapter's OpenAI and Qdrant resource slots, registered an asynchronous
background task, awaited `adapter.close(timeout=1.0)`, then checked both
clients' `is_closed` state and the task status.
- Observed result: `real httpx clients closed and background task
drained`; both clients reported closed, no pending task IDs remained,
and the task status was `completed`.
- Who maintains it: Headroom Labs maintains this active upstream
repository and memory backend.
- Install surface: No dependencies or install behavior changed. The fix
uses the standard-library asyncio/inspect modules and existing resource
close methods; no native code or runtime network access is introduced.
- Not tested: The complete test suite could not run past collection
because this Windows environment lacks the compiled `headroom._core`
extension.
## Review Readiness
- [x] I have performed a self-review
- [x] This PR is ready for human review
## Checklist
- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes (full
suite blocked by missing native extension; targeted tests pass)
- [x] I did not edit `CHANGELOG.md` - it is generated by release-please
from my Conventional Commit PR title.
## Screenshots (if applicable)
Not applicable.
## Additional Notes
The default close timeout is 60 seconds and can be overridden by callers
that need a shorter shutdown budget.
---------
Co-authored-by: Tejas Chopra <tejas@Tejass-MacBook-Pro.local>