Metadata-Version: 2.4
Name: openframe-adapters
Version: 1.2.0
Summary: OpenFrame Microservice Suite - adapter meta-package. Install adapters by name or group.
Author-email: Furious Meteors Engineering <engineering@furiousmeteors.dev>
Maintainer-email: Furious Meteors Engineering <engineering@furiousmeteors.dev>
License: MIT
Project-URL: Homepage, https://github.com/Furious-Meteors/openframe-adapters
Project-URL: Documentation, https://furious-meteors.github.io/openframe-adapters/
Project-URL: Repository, https://github.com/Furious-Meteors/openframe-adapters
Project-URL: Changelog, https://github.com/Furious-Meteors/openframe-adapters/blob/production/.github/CHANGELOG.md
Project-URL: Bug Tracker, https://github.com/Furious-Meteors/openframe-adapters/issues
Keywords: openframe,hexagonal,microservice,postgres,mongodb,redis,kafka,vector-database,adapter,fastapi,modal
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Database
Classifier: Topic :: System :: Distributed Computing
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Provides-Extra: postgres
Requires-Dist: openframe-adapters-db-postgres<2,>=1.1; extra == "postgres"
Provides-Extra: mysql
Requires-Dist: openframe-adapters-db-mysql<2,>=1.1; extra == "mysql"
Provides-Extra: redis
Requires-Dist: openframe-adapters-db-redis<2,>=1.1; extra == "redis"
Provides-Extra: dynamodb
Requires-Dist: openframe-adapters-db-dynamodb<2,>=1.1; extra == "dynamodb"
Provides-Extra: mongo
Requires-Dist: openframe-adapters-db-mongo<2,>=1.1; extra == "mongo"
Provides-Extra: cassandra
Requires-Dist: openframe-adapters-db-cassandra<2,>=1.1; extra == "cassandra"
Provides-Extra: influxdb
Requires-Dist: openframe-adapters-db-influxdb<2,>=1.1; extra == "influxdb"
Provides-Extra: milvus
Requires-Dist: openframe-adapters-db-milvus<2,>=1.1; extra == "milvus"
Provides-Extra: chromadb
Requires-Dist: openframe-adapters-db-chromadb<2,>=1.1; extra == "chromadb"
Provides-Extra: qdrant
Requires-Dist: openframe-adapters-db-qdrant<2,>=1.1; extra == "qdrant"
Provides-Extra: faiss
Requires-Dist: openframe-adapters-db-faiss<2,>=1.1; extra == "faiss"
Provides-Extra: falkordb
Requires-Dist: openframe-adapters-db-falkordb<2,>=1.1; extra == "falkordb"
Provides-Extra: kafka
Requires-Dist: openframe-adapters-queue-kafka<2,>=1.1; extra == "kafka"
Provides-Extra: nats
Requires-Dist: openframe-adapters-queue-nats<2,>=1.1; extra == "nats"
Provides-Extra: rabbitmq
Requires-Dist: openframe-adapters-queue-rabbitmq<2,>=1.1; extra == "rabbitmq"
Provides-Extra: db
Requires-Dist: openframe-adapters[postgres]; extra == "db"
Requires-Dist: openframe-adapters[mysql]; extra == "db"
Requires-Dist: openframe-adapters[redis]; extra == "db"
Requires-Dist: openframe-adapters[dynamodb]; extra == "db"
Requires-Dist: openframe-adapters[mongo]; extra == "db"
Requires-Dist: openframe-adapters[cassandra]; extra == "db"
Requires-Dist: openframe-adapters[influxdb]; extra == "db"
Provides-Extra: vector
Requires-Dist: openframe-adapters[milvus]; extra == "vector"
Requires-Dist: openframe-adapters[chromadb]; extra == "vector"
Requires-Dist: openframe-adapters[qdrant]; extra == "vector"
Requires-Dist: openframe-adapters[faiss]; extra == "vector"
Requires-Dist: openframe-adapters[falkordb]; extra == "vector"
Provides-Extra: queue
Requires-Dist: openframe-adapters[kafka]; extra == "queue"
Requires-Dist: openframe-adapters[nats]; extra == "queue"
Requires-Dist: openframe-adapters[rabbitmq]; extra == "queue"
Provides-Extra: all
Requires-Dist: openframe-adapters[db]; extra == "all"
Requires-Dist: openframe-adapters[vector]; extra == "all"
Requires-Dist: openframe-adapters[queue]; extra == "all"
Provides-Extra: rest-min
Requires-Dist: openframe-adapters[postgres]; extra == "rest-min"
Requires-Dist: openframe-adapters[redis]; extra == "rest-min"
Provides-Extra: rag-stack
Requires-Dist: openframe-adapters[milvus]; extra == "rag-stack"
Requires-Dist: openframe-adapters[falkordb]; extra == "rag-stack"
Requires-Dist: openframe-adapters[redis]; extra == "rag-stack"
Provides-Extra: research-min
Requires-Dist: openframe-adapters[mongo]; extra == "research-min"
Requires-Dist: openframe-adapters[redis]; extra == "research-min"

# openframe-adapters

A metadata-only package that provides named install shortcuts for the entire
`openframe-adapters` ecosystem. Install one adapter, a category, or everything
— all with a single `pip install` command.

---

## Install surface

### Individual adapters

```bash
# Relational
pip install openframe-adapters[postgres]    # PostgreSQL via asyncpg
pip install openframe-adapters[mysql]       # MySQL via aiomysql

# Key-value
pip install openframe-adapters[redis]       # Redis via redis-py
pip install openframe-adapters[dynamodb]    # DynamoDB via aiobotocore

# Document
pip install openframe-adapters[mongo]       # MongoDB via Motor

# Columnar
pip install openframe-adapters[cassandra]   # Cassandra via cassandra-driver

# Time-series
pip install openframe-adapters[influxdb]    # InfluxDB via influxdb-client

# Vector
pip install openframe-adapters[milvus]      # Milvus via pymilvus
pip install openframe-adapters[chromadb]    # Chroma via chromadb
pip install openframe-adapters[qdrant]      # Qdrant via qdrant-client
pip install openframe-adapters[faiss]       # FAISS via faiss-cpu
pip install openframe-adapters[falkordb]    # FalkorDB via falkordb

# Queues
pip install openframe-adapters[kafka]       # Kafka via aiokafka
pip install openframe-adapters[nats]        # NATS via nats-py
pip install openframe-adapters[rabbitmq]    # RabbitMQ via aio-pika
```

### Groups — one category

```bash
pip install openframe-adapters[db]       # all 7 DB adapters (relational + document + specialist)
pip install openframe-adapters[vector]   # all 5 vector DB adapters
pip install openframe-adapters[queue]    # all 3 queue adapters
```

### Everything

```bash
pip install openframe-adapters[all]      # all 15 individual adapter packages
```

### Convenience combinations

```bash
pip install openframe-adapters[rest-min]      # postgres + redis  (REST API minimum)
pip install openframe-adapters[rag-stack]     # milvus + falkordb + redis  (RAG / inference)
pip install openframe-adapters[research-min]  # mongo + redis  (Research Vault Phase 1)
```

---

## Import paths

The import path is identical regardless of how you installed:

```python
# Whether you ran:
#   pip install openframe-adapters[postgres]
# or:
#   pip install openframe-adapters[all]
# the import is always the same:
from openframe.adapters.db.postgres import PostgresRepository
from openframe.adapters.db.mongo import MongoRepository
```

Each individual adapter package uses Python namespace packages under
`openframe.adapters.*`, so all adapters share the same top-level namespace
without any conflicts.

---

## Wiring adapters into your service

Every adapter wires into your service through a single file — `deps.py` or
`bootstrap/dependencies.py`. This file is the only place in your codebase
that knows which adapter is active. Routes and services never import adapters
directly.

The rule is simple:

> **One adapter** — wire directly with `lru_cache`.
> **Two or more adapters** — use `PluginRegistry`.
> The trigger to upgrade is adding a second adapter.

### Stage 1 — One adapter

Install one adapter and wire it directly. Four lines. No registry needed.

```python
# bootstrap/dependencies.py
from functools import lru_cache
from openframe.adapters.db.postgres import PostgresRepository, PostgresSettings
from openframe.core.tracing import TracingProxy
from src.adapters.item_repository import ItemPostgresRepository
from src.application.services.item_service import ItemService

@lru_cache(maxsize=1)
def _get_repository() -> ItemPostgresRepository:
    return ItemPostgresRepository(PostgresSettings())

def get_item_service() -> ItemService:
    return ItemService(TracingProxy(_get_repository(), prefix="repository.item"))
```

`PostgresSettings()` reads `DATABASE_URL` from env at startup.
`lru_cache(maxsize=1)` constructs the repository once per process.
`TracingProxy` wraps it for automatic OTel spans on every call.

### Stage 2 — Two or more adapters

When a second adapter is needed, replace `lru_cache` with `PluginRegistry`.
The registry handles startup ordering, health aggregation, and graceful
shutdown across all adapters.

```python
# bootstrap/dependencies.py
from openframe.adapters.db.postgres import PostgresPlugin, PostgresSettings
from openframe.adapters.db.redis import RedisPlugin, RedisSettings
from openframe.core.plugins import PluginRegistry
from openframe.core.tracing import TracingProxy
from src.application.services.item_service import ItemService
from src.application.services.session_service import SessionService

_registry: PluginRegistry | None = None

async def initialise() -> None:
    global _registry
    _registry = PluginRegistry()
    _registry.register(PostgresPlugin(PostgresSettings()))  # capability: "persistence"
    _registry.register(RedisPlugin(RedisSettings()))        # capability: "cache"
    await _registry.initialize_all()   # fails fast if any backend unreachable

async def shutdown() -> None:
    if _registry:
        await _registry.shutdown_all()  # never raises

def get_item_service() -> ItemService:
    repo = TracingProxy(
        _registry.get("persistence").get_repository(),
        prefix="repository.item",
    )
    return ItemService(repo)

def get_session_service() -> SessionService:
    cache = TracingProxy(
        _registry.get("cache").get_repository(),
        prefix="cache.session",
    )
    return SessionService(cache)
```

Wire `initialise()` and `shutdown()` in your FastAPI lifespan:

```python
from contextlib import asynccontextmanager
from fastapi import FastAPI
from openframe.core.middleware import TelemetryMiddleware
from openframe.core.telemetry import setup_telemetry
from src.bootstrap import dependencies

@asynccontextmanager
async def lifespan(app: FastAPI):
    setup_telemetry()
    await dependencies.initialise()
    yield
    await dependencies.shutdown()

app = FastAPI(lifespan=lifespan)
app.add_middleware(TelemetryMiddleware)
```

### Plugin capabilities

Every `*Plugin` class declares a `capability` string. This is the key used
by `registry.get()`. The capability taxonomy is a convention across the entire
OpenFrame ecosystem — use these exact strings:

| Capability | Adapters | Use for |
|---|---|---|
| `"persistence"` | Postgres, Mongo, MySQL, DynamoDB, Cassandra | Primary data store |
| `"cache"` | Redis | Fast ephemeral store, sessions, rate limits |
| `"queue"` | Kafka, NATS, RabbitMQ | Message publishing and consumption |
| `"vector"` | Milvus, Qdrant, ChromaDB, FAISS, FalkorDB | Vector similarity search |
| `"timeseries"` | InfluxDB | Time-series metrics and events |

Using a different string for the same category breaks `registry.get()` across
services. Always use the strings from this table.

### The env-var swap exception

Switching between adapters via an environment variable (`PERSISTENCE_BACKEND=postgres`
vs `PERSISTENCE_BACKEND=mongo`) is still Stage 1 — because only one adapter
is active at any moment. Use `lru_cache` direct wiring with a conditional:

```python
@lru_cache(maxsize=1)
def _get_repository():
    backend = os.environ.get("PERSISTENCE_BACKEND", "postgres")
    if backend == "postgres":
        return ItemPostgresRepository(PostgresSettings())
    elif backend == "mongo":
        return ItemMongoRepository(MongoSettings())
    raise ValueError(f"Unknown PERSISTENCE_BACKEND: {backend!r}")
```

`PluginRegistry` is for services that need multiple adapters simultaneously,
not for services that swap between adapters via configuration.

### Full wiring reference

For the complete guide — upgrade path from Stage 1 to Stage 2, lifespan
wiring, and architecture diagrams — see the
[Composition Root](https://furious-meteors.github.io/openframe-core/developer-guide/composition-root/)
page in the `openframe-core` documentation.

---

## Package inventory

| Extra | `pip install` | Package installed | Async driver |
|---|---|---|---|
| `postgres` | `openframe-adapters[postgres]` | `openframe-adapters-db-postgres` | `asyncpg` |
| `mysql` | `openframe-adapters[mysql]` | `openframe-adapters-db-mysql` | `aiomysql` |
| `redis` | `openframe-adapters[redis]` | `openframe-adapters-db-redis` | `redis-py` (asyncio) |
| `dynamodb` | `openframe-adapters[dynamodb]` | `openframe-adapters-db-dynamodb` | `aiobotocore` |
| `mongo` | `openframe-adapters[mongo]` | `openframe-adapters-db-mongo` | `Motor` |
| `cassandra` | `openframe-adapters[cassandra]` | `openframe-adapters-db-cassandra` | `cassandra-driver` |
| `influxdb` | `openframe-adapters[influxdb]` | `openframe-adapters-db-influxdb` | `influxdb-client` |
| `milvus` | `openframe-adapters[milvus]` | `openframe-adapters-db-milvus` | `pymilvus` |
| `chromadb` | `openframe-adapters[chromadb]` | `openframe-adapters-db-chromadb` | `chromadb` |
| `qdrant` | `openframe-adapters[qdrant]` | `openframe-adapters-db-qdrant` | `qdrant-client` |
| `faiss` | `openframe-adapters[faiss]` | `openframe-adapters-db-faiss` | `faiss-cpu` |
| `falkordb` | `openframe-adapters[falkordb]` | `openframe-adapters-db-falkordb` | `falkordb` |
| `kafka` | `openframe-adapters[kafka]` | `openframe-adapters-queue-kafka` | `aiokafka` |
| `nats` | `openframe-adapters[nats]` | `openframe-adapters-queue-nats` | `nats-py` |
| `rabbitmq` | `openframe-adapters[rabbitmq]` | `openframe-adapters-queue-rabbitmq` | `aio-pika` |
| `db` | `openframe-adapters[db]` | all 7 DB adapters above | — |
| `vector` | `openframe-adapters[vector]` | all 5 vector adapters above | — |
| `queue` | `openframe-adapters[queue]` | all 3 queue adapters above | — |
| `all` | `openframe-adapters[all]` | all 15 adapter packages | — |

---

## Core dependency

[`openframe-core`](https://pypi.org/project/openframe-core/) is installed
automatically as a transitive dependency — you never need to declare it
separately. Every individual adapter package pins `openframe-core>=1.0,<2`,
so installing any extra brings core in as part of the resolution.

---

## Versioning

Each adapter package is versioned independently and published to PyPI under
its own name (e.g. `openframe-adapters-db-postgres`). This meta-package pins
all of them at `>=1.0,<2`, so patch and minor releases are picked up
automatically the next time you run `pip install --upgrade`. Only a major
version bump in an individual adapter requires a meta-package update.

When a new adapter is added to the ecosystem, only this `pyproject.toml`
changes — one new line in `[project.optional-dependencies]` and an update to
the relevant group. No other file in the monorepo is touched.
