Deployment Profiles¶
Status: Phase 3 (Apache AGE + lean profile) shipped 2026-05-19. Phase 5 quick-win adapters (Memgraph / Kuzu / NetworkX-in-memory / Memcached / SQLite) shipped 2026-05-20. See "Provider matrix" below for the full set and the "Deferred" section for what's still pending.
RNR Enhanced Cognee ships four deployment profiles -- presets that pin
the four ENHANCED_*_PROVIDER env vars to a coherent combination. They
exist so that the same code base can run on a laptop, a 4 GB VPS, an
enterprise cluster, or someone's BYO managed cloud, without forking the
codebase.
A profile is just a set of env vars. There's no global "profile switch"
toggle -- docker compose up -d always brings up whatever the
docker-compose-enhanced-cognee.yml file declares; the profile is
the choice of env vars you export before launching the MCP server.
TL;DR¶
| Profile | Relational | Vector | Graph | Cache | Containers needed | Best for |
|---|---|---|---|---|---|---|
production |
postgres | qdrant | arcadedb | valkey | postgres + qdrant + arcadedb + valkey | Production; what the default compose ships |
apache_only |
postgres | qdrant | apache_age | valkey | postgres (with AGE) + qdrant + valkey | All-Apache-2.0 stack, no JVM container |
lean |
postgres | pgvector | apache_age | in_memory | postgres (with AGE + vector) | Laptops / small VPS / offline (1 container) |
byo |
(env) | (env) | (env) | (env) | none (all external) | Managed cloud / multi-tenant SaaS |
All four resolve through src/db_factory.py. The ENHANCED_*_PROVIDER
env vars are the public interface; *_BACKEND legacy aliases are still
honoured at lower precedence.
production (default)¶
The default 4-database stack since Phase 2:
ENHANCED_RELATIONAL_PROVIDER=postgres
ENHANCED_VECTOR_PROVIDER=qdrant
ENHANCED_GRAPH_PROVIDER=arcadedb # Apache-2.0; flipped from neo4j in Phase 2
ENHANCED_CACHE_PROVIDER=valkey # Apache-2.0; replaced redis in Phase 1
docker compose -f docker/docker-compose-enhanced-cognee.yml up -d brings
this up out of the box. This is the recommended path for any deployment
that has multi-GB headroom and wants the best operational experience
(graph-native indexes, multi-protocol API, dedicated cache pub/sub).
100% Apache-2.0 across the database tier. The only AGPL-tinged
components are in the optional monitoring stack
(docker/docker-compose-monitoring.yml) and are being swapped to SigNoz
+ Superset in Phase 4.
apache_only¶
Same as production but with the graph layer collapsed into Postgres:
ENHANCED_RELATIONAL_PROVIDER=postgres
ENHANCED_VECTOR_PROVIDER=qdrant
ENHANCED_GRAPH_PROVIDER=apache_age # Apache AGE Postgres extension
ENHANCED_CACHE_PROVIDER=valkey
Use when you want:
- One fewer container (no Java runtime)
- A single Postgres-shaped operational surface (backups, monitoring, tooling)
- The strictest interpretation of "Apache-2.0 throughout" (AGE is ASF)
You'll need to enable AGE inside the Postgres container -- see the
apache_only setup section below.
lean¶
Smallest viable stack -- one Postgres container plus the MCP server.
Updated 2026-05-20 to use pgvector now that the adapter has shipped:
ENHANCED_RELATIONAL_PROVIDER=postgres
ENHANCED_VECTOR_PROVIDER=pgvector # was: qdrant; pgvector shipped 2026-05-20
ENHANCED_GRAPH_PROVIDER=apache_age
ENHANCED_CACHE_PROVIDER=in_memory # process-local dict
deploy/profiles/lean.yaml documents the exact set. One container:
Postgres (with the vector extension auto-installed by the pgvector
adapter on first create_collection, and the age extension created
manually -- see the apache_only setup section below).
Limitations (intentional):
- Cache loses state on process restart (
in_memory) - No pub/sub -- realtime sync degrades gracefully but pub/sub channels noop
- Single-process only -- a second MCP process won't share cache state
- pgvector adapter has a narrow Qdrant API surface; see its sub-section below for what's supported
lean is not recommended for production; it exists so that "give me
the smallest possible RNR Enhanced Cognee" works as a single Postgres
container.
byo (Bring Your Own infra)¶
ENHANCED_RELATIONAL_PROVIDER=postgres
ENHANCED_VECTOR_PROVIDER=qdrant
ENHANCED_GRAPH_PROVIDER=arcadedb # or neo4j / apache_age
ENHANCED_CACHE_PROVIDER=valkey # or redis
# Connection details for each tier point to your existing infra
POSTGRES_HOST=postgres.internal
POSTGRES_PORT=5432
QDRANT_HOST=qdrant.internal
NEO4J_URI=bolt://graph.internal:7687 # or ARCADEDB_URI
REDIS_HOST=cache.internal
No compose file is involved -- the MCP server just connects to whatever endpoints you point it at. Common when you're on AWS RDS for Postgres, Qdrant Cloud for vectors, etc.
Phase 5 adapter sub-sections¶
ArcadeDB adapter -- dual transport (Bolt + HTTP/JSON)¶
src/db_adapters/graph_arcadedb.py ships two transports selectable
via ARCADEDB_TRANSPORT:
| Transport | Default | When to pick it |
|---|---|---|
bolt (default since Phase 2) |
Yes | Existing Neo4j deployments where you want the same wire protocol -- requires ArcadeDB's optional Bolt plugin (configured in docker/docker-compose-enhanced-cognee.yml). Lowest latency. |
http (added 2026-05-20) |
No | Stock arcadedata/arcadedb Docker image without the Bolt plugin, CI runners that can't install JVM extensions, browser-bridge integrations. Higher per-request latency due to HTTP framing, but no extra dependencies. |
HTTP transport env vars:
ENHANCED_GRAPH_PROVIDER=arcadedb
ARCADEDB_TRANSPORT=http
ARCADEDB_HOST=localhost # default
ARCADEDB_HTTP_PORT=2480 # ArcadeDB Studio + command API port
ARCADEDB_DATABASE=cognee_graph # default
ARCADEDB_USER=root # default
ARCADEDB_PASSWORD=your-db-password # default
The HTTP transport wraps POST /api/v1/command/{db} and exposes the
same neo4j.Driver-shaped surface as the Bolt path: .session() /
.run(cypher, parameters) / iteration / .single() / .data().
Call sites don't need to know which transport is in use.
Bolt transport env vars (unchanged from Phase 2):
ENHANCED_GRAPH_PROVIDER=arcadedb
ARCADEDB_TRANSPORT=bolt # default; can be omitted
ARCADEDB_URI=bolt://localhost:27687
ARCADEDB_USER=root
ARCADEDB_PASSWORD=your-db-password
When NOT to use the HTTP transport: if you need bulk writes (the
HTTP API does one round-trip per .run() call; Bolt streams).
Single-request-per-Cypher latency is ~5-10ms higher.
Memgraph adapter¶
The Memgraph adapter is structurally identical to the ArcadeDB / Neo4j
adapter (it's another Bolt-protocol target). Its default URI is
bolt://localhost:7687 (Memgraph's standard port; differs from our
ArcadeDB host-port 27687). Note on licence: Memgraph is BSL 1.1
with a 4-year delay to Apache-2.0, so it's not OSI-permissive today.
Use only if your distribution model is OK with BSL.
Kuzu adapter¶
Embedded -- runs in-process, no Docker container. Filesystem-backed
via the KUZU_DB_PATH env var (default ./kuzu_data). Cypher
parameters are first-class. No async session API -- wrap
session.run() in asyncio.to_thread at the call site if you need
to interleave with async I/O. The adapter raises a clear
NotImplementedError for get_async_graph_driver() on the kuzu
provider.
NetworkX in-memory adapter -- supported query matrix¶
Pure-Python BSD library. Not a Cypher engine. Only three query
shapes are parsed; everything else raises NotImplementedError:
| Cypher | Supported | Notes |
|---|---|---|
RETURN <literal> |
✅ | RETURN 1 / RETURN 'ok' / RETURN true / RETURN null |
MATCH (n) RETURN COUNT(n) [AS alias] |
✅ | Returns the NX graph's number_of_nodes() |
MATCH (n) DETACH DELETE n |
✅ | Clears the graph in-place |
Parameterised queries ($param) |
❌ | Raises NotImplementedError |
| Anything else | ❌ | Raises NotImplementedError with a pointer here |
Suitable for: unit tests, the ping path in bin/preflight.py, and the
"lean profile on a laptop where graph workload is tiny".
Memcached adapter -- supported method matrix¶
The redis-shaped surface is necessarily narrow because memcached has no equivalent of pub/sub, sorted sets, or hash tables:
| Method | Supported | Notes |
|---|---|---|
ping() |
✅ | Maps to memcached's version() |
get(key) |
✅ | |
set(key, value, ex=ttl) |
✅ | |
delete(*keys) |
✅ | |
exists(*keys) |
✅ | Implemented via get() since memcached has no native EXISTS |
expire(key, seconds) |
✅ | Maps to memcached's touch() |
close() |
✅ | |
keys(pattern) |
❌ | Memcached intentionally has no key enumeration |
flushdb() |
❌ | Memcached's flush_all() wipes the entire server; not exposed under the redis name |
| Pub/sub | ❌ | Memcached has none |
DuckDB relational adapter -- columnar OLAP¶
src/db_adapters/relational_duckdb.py (shipped 2026-05-20) is an
MIT-licensed embedded columnar database. Useful when the workload
is analytics-heavy (group-by, aggregate, window functions) but you
don't want a separate Postgres server. Exposes the same
asyncpg.Pool-shape as the other relational adapters; sync DuckDB
calls are wrapped with asyncio.to_thread.
| Concern | Impact |
|---|---|
| Parameter style | ? placeholders (positional). Not compatible with asyncpg $1 / $2 |
| Concurrency | Single writer per file. Use :memory: for ephemeral, per-process file path for concurrent writers |
| pgvector / array | No pgvector; arrays via LIST type with a different syntax |
database kwarg |
Used as the DuckDB file path. Falls back to DUCKDB_DB_PATH, then POSTGRES_DB, then ./enhanced_cognee.duckdb |
DUCKDB_READ_ONLY=true |
Opens the file read-only -- useful for BI / Superset readers alongside a writer process |
When to pick DuckDB: analytics workloads on a single host where you
want OLAP power without standing up Postgres. The Superset BI
dashboards in monitoring/superset-dashboards/ can target DuckDB
directly.
MySQL / MariaDB relational adapter¶
src/db_adapters/relational_mysql.py (shipped 2026-05-20). Targets
MySQL 8+ / MariaDB 10+ / Percona Server / Vitess via the
asyncmy Apache-2.0 driver. Same asyncpg.Pool-shaped surface as
the rest of the relational tier.
| Concern | Impact |
|---|---|
| Parameter style | %s placeholders (DB-API style). Not compatible with asyncpg $1 / $2 |
| Array type | None natively -- remodel as JSON |
| Upsert syntax | INSERT ... ON DUPLICATE KEY UPDATE (MySQL form), not ON CONFLICT DO UPDATE |
vector(N) columns |
MariaDB 11.7+ has a vector type; MySQL 8.x does not |
| Env vars | MYSQL_HOST / MYSQL_PORT / MYSQL_DB / MYSQL_USER / MYSQL_PASSWORD |
| Provider value | Both mysql and mariadb route to this adapter |
When to pick MySQL / MariaDB: you already operate one of these at scale, or your hosting provider only offers managed MySQL (Aurora MySQL, Cloud SQL for MySQL, Azure DB for MySQL, PlanetScale).
MS SQL Server relational adapter¶
src/db_adapters/relational_mssql.py (shipped 2026-05-20, PR 11).
Wraps aioodbc (Apache-2.0) against the Microsoft ODBC Driver for
SQL Server. Targets SQL Server 2019+, Azure SQL Database, Azure SQL
Managed Instance.
| Concern | Impact |
|---|---|
| Parameter style | ? placeholders (DB-API). Not compatible with asyncpg $1 / $2 |
| Limit clause | TOP n or OFFSET / FETCH NEXT -- not LIMIT |
| Identity column | IDENTITY(1,1) -- not SERIAL |
| Quoting | [brackets] for reserved identifiers |
| JSON | NVARCHAR(MAX) + JSON_VALUE() -- no native JSONB |
| Env vars | MSSQL_HOST / MSSQL_PORT / MSSQL_DB / MSSQL_USER / MSSQL_PASSWORD / MSSQL_DRIVER |
| Provider value | Both mssql and sqlserver route to this adapter |
| Prerequisite | Microsoft ODBC Driver 18 for SQL Server (~50 MB host install) |
When to pick MS SQL: enterprise license already covers it, your team operates SQL Server elsewhere, or you're on Azure SQL.
Oracle Database relational adapter¶
src/db_adapters/relational_oracle.py (shipped 2026-05-20, PR 11).
Wraps Oracle's official oracledb driver (Universal Permissive
Licence) which has a native async API. Targets Oracle 19c / 21c /
23ai, Autonomous Database, Exadata.
| Concern | Impact |
|---|---|
| Parameter style | :1 / :2 / :param (Oracle bind variables) |
| Limit clause | FETCH FIRST n ROWS ONLY |
| Empty string == NULL | Oracle treats '' as NULL (historic) |
| Identifiers | Uppercase unless double-quoted |
| JSON | Native JSON type in 21c+; CLOB for older versions |
| Env vars | ORACLE_DSN (preferred) OR ORACLE_HOST / port / database; ORACLE_USER / ORACLE_PASSWORD; optional ORACLE_THICK_MODE + ORACLE_LIB_DIR |
| Modes | Thin mode (Python-only) by default; thick mode requires Oracle Instant Client |
When to pick Oracle: enterprise license already covers it, you're on OCI Autonomous Database, or your team standardises on Oracle.
IBM Db2 relational adapter¶
src/db_adapters/relational_db2.py (shipped 2026-05-20, PR 11).
Wraps IBM's ibm_db driver (Apache-2.0 binding over IBM's CLI).
Sync-only, wrapped with asyncio.to_thread. Targets Db2 LUW 11.5+,
Db2 on Cloud, Db2 Warehouse on Cloud.
| Concern | Impact |
|---|---|
| Parameter style | ? placeholders |
| Limit clause | FETCH FIRST n ROWS ONLY |
| Identifiers | Uppercase by default |
| Auto-increment | GENERATED ALWAYS AS IDENTITY |
| JSON | Native in 11.1+; BLOB for older |
| Env vars | DB2_HOST / DB2_PORT / DB2_DB / DB2_USER / DB2_PASSWORD / DB2_PROTOCOL |
| Prerequisite | IBM CLI library; bundled with ibm_db on Linux x86_64; manual on other platforms |
When to pick Db2: you operate Db2 on IBM Z / Power Systems, or you're on IBM Cloud.
Snowflake cloud data warehouse adapter¶
src/db_adapters/relational_snowflake.py (shipped 2026-05-20, PR 12).
Wraps Snowflake's official snowflake-connector-python (Apache-2.0).
Cloud-native columnar warehouse for analytical workloads.
| Concern | Impact |
|---|---|
| Parameter style | %s (positional) or %(name)s (named) -- not asyncpg $1 |
| Connection | Account-locator-based (no host/port) -- SNOWFLAKE_ACCOUNT required |
| JSON | VARIANT semi-structured type (Snowflake-native) |
| Vector | No native vector type -- pair with qdrant/chroma/weaviate/milvus |
| Identifiers | Uppercase unless double-quoted |
| Env vars | SNOWFLAKE_ACCOUNT (required) / SNOWFLAKE_USER / SNOWFLAKE_PASSWORD / SNOWFLAKE_WAREHOUSE / SNOWFLAKE_DATABASE / SNOWFLAKE_SCHEMA / SNOWFLAKE_ROLE (optional) |
When to pick Snowflake: analytics live in Snowflake already, time- travel / zero-copy clone / compute-storage separation is valuable.
Databricks SQL Warehouse adapter¶
src/db_adapters/relational_databricks.py (shipped 2026-05-20, PR 12).
Wraps databricks-sql-connector (Apache-2.0) against a Databricks
SQL Warehouse endpoint.
| Concern | Impact |
|---|---|
| Parameter style | ? positional or %(name)s named |
| SQL dialect | Spark SQL (mostly ANSI; Databricks-specific MERGE / OPTIMIZE) |
| JSON | STRING + parse_json / json_query helpers |
| Vector | Managed Vector Search is separate; not via SQL Warehouse |
| Auth | Bearer token (PAT or OAuth M2M); no username/password |
| Env vars | DATABRICKS_SERVER_HOSTNAME (required) / DATABRICKS_HTTP_PATH (required) / DATABRICKS_ACCESS_TOKEN (required) / DATABRICKS_CATALOG (default hive_metastore) / DATABRICKS_SCHEMA (default default) |
| Connection | Per-acquire (no native async pool in the connector) |
When to pick Databricks: lakehouse + Unity Catalog + downstream ML notebooks already live there.
CockroachDB (via the postgres adapter)¶
CockroachDB speaks the Postgres wire protocol, so the existing
postgres adapter works against a Cockroach cluster with no code
changes. Set:
ENHANCED_RELATIONAL_PROVIDER=postgres
POSTGRES_HOST=cockroach.internal
POSTGRES_PORT=26257
POSTGRES_DB=defaultdb
POSTGRES_USER=root
POSTGRES_PASSWORD=your-db-password
Caveats vs vanilla Postgres:
- Cockroach uses serializable isolation by default (some Postgres
applications expect read-committed). Set
default_transaction_isolationif needed. - Cockroach doesn't support all Postgres extensions -- in particular,
pgvector is not supported as of 2026-05. Pair the Cockroach
relational tier with a separate vector backend (
qdrant,chroma,weaviate,milvus, etc). - AGE (Apache AGE) is also not supported; pair with
arcadedb/neo4j/memgraphfor the graph tier.
SQLite relational adapter -- compatibility caveats vs asyncpg¶
| Concern | Impact |
|---|---|
| Array / JSONB / pgvector types | Not supported -- schemas relying on these won't run unmodified |
| Parameter style | ? placeholders (aiosqlite native), not asyncpg's $1 / $2. Adapter does not translate query syntax |
| Connection pool | Single connection per acquire() -- no pooling. SQLite's write lock bounds concurrency |
database kwarg |
Used as the SQLite file path (defaults to SQLITE_DB_PATH env, then POSTGRES_DB, then enhanced_cognee.db). Pass :memory: for an ephemeral DB |
Use only for tests / lean profile. Production workloads must
pick postgres.
pgvector adapter -- supported method matrix¶
src/db_adapters/vector_pgvector.py (shipped 2026-05-20) is a
QdrantClient-shaped shim over psycopg2 + the pgvector Postgres
extension. Storage model: one Postgres table per Qdrant "collection",
prefixed with PGVECTOR_TABLE_PREFIX (default ec_vec_).
| Method | Supported | Notes |
|---|---|---|
get_collections() |
✅ | Lists tables matching the prefix |
get_collection(name) |
✅ | Raises if the table doesn't exist (matches Qdrant) |
create_collection(name, vectors_config) |
✅ | Auto-creates vector extension + IVFFLAT index. Distance: Cosine / Euclid / Dot |
upsert(name, points) |
✅ | ON CONFLICT (id) DO UPDATE for idempotency |
count(name) |
✅ | |
search(name, query_vector, limit, score_threshold) |
✅ | Supports single-must FieldCondition query_filter via the shared translator (see below). |
delete(name, points_selector) -- ID list |
✅ | |
delete(name, FilterSelector(filter=Filter(must=[FieldCondition(...)]))) |
✅ | Single must FieldCondition only -- enough for gdpr_manager.delete_user_data |
delete(...) with should / must_not / multi-condition filters |
❌ | Raises NotImplementedError; query first and pass IDs |
search() with query_filter |
✅ (single must FieldCondition) | Routes through src/db_adapters/_vector_filter.py to a WHERE payload ->> %s = %s JSONB extract. Compound filters raise NotImplementedError. |
| Named vectors / sparse vectors | ❌ | Not modelled in the schema |
| Payload field indexes | ❌ | All filters are evaluated against the JSONB column |
| Quantization / on-disk vectors / snapshots | ❌ | Out of scope for the minimum viable adapter |
The adapter sanitises collection names to alphanumeric + underscore before formatting them into the table identifier, so the table-prefix is not a SQL-injection surface.
When to pick pgvector: the lean profile, or any deployment where
"one Postgres" is operationally preferred over "Postgres + Qdrant".
Stick with qdrant for production-scale workloads that need rich
filters, named vectors, or large-collection performance.
LanceDB adapter¶
src/db_adapters/vector_lancedb.py (shipped 2026-05-20). Embedded
MIT-licensed columnar vector store via Apache Arrow + Lance file
format. Runs in-process -- no Docker container, no network
protocol. Each Qdrant "collection" maps to a Lance table at the
configured LANCEDB_URI filesystem path. Suitable for laptop /
offline / single-process deployments where pgvector's Postgres-stack
requirement is overkill.
| Method | Supported | Notes |
|---|---|---|
get_collections() / get_collection(name) / count(name) |
✅ | |
create_collection(name, vectors_config) |
✅ | Cosine / Euclid / Dot supported via LanceDB's metric parameter |
upsert(name, points) |
✅ | LanceDB has no native ON-CONFLICT; we delete-then-add atomically per batch |
search(name, query_vector, limit, score_threshold) |
✅ | Returns ScoredPoint-shape hits; score normalised (1 - distance) |
delete() by ID list |
✅ | |
delete() by single must FieldCondition |
✅ | LIKE substring match on JSON-encoded payload (GDPR shape) |
search() with query_filter |
✅ (single must FieldCondition) | Routes through the shared src/db_adapters/_vector_filter.py translator: emits a LanceDB WHERE payload LIKE '%"key":"value"%' clause. Compound filters (should / must_not / multiple must) raise NotImplementedError -- query the points first and pass IDs. |
| Multi-condition / nested delete filters | ❌ |
Chroma adapter¶
src/db_adapters/vector_chroma.py (shipped 2026-05-20). Apache-2.0
vector store with native Collection semantics. Network mode if
CHROMA_HOST is set, otherwise persistent in-process at
CHROMA_PATH. Chroma's API maps almost 1:1 onto Qdrant's, so the
adapter is shorter than pgvector / lancedb -- mostly straight
pass-throughs.
| Method | Supported | Notes |
|---|---|---|
get_collections() / get_collection(name) / count(name) |
✅ | |
create_collection(name, vectors_config) |
✅ | Distance metric via metadata={"hnsw:space": metric} |
upsert(name, points) |
✅ | Native collection.upsert(ids, embeddings, metadatas) |
search(name, query_vector, limit, score_threshold) |
✅ | Score = 1 - distance |
delete() by ID list |
✅ | |
delete() by single must FieldCondition |
✅ | Native where={key: value} map |
search() with query_filter |
✅ (single must FieldCondition) | Routes through _vector_filter.py to Chroma's native where={"key": value} map. Compound filters raise NotImplementedError. |
| Multi-condition / nested delete filters | ❌ |
Weaviate adapter¶
src/db_adapters/vector_weaviate.py (shipped 2026-05-20). BSD-3
vector database with rich GraphQL/REST API. Uses the v4
weaviate-client Python SDK. Each Qdrant collection maps to a
Weaviate Collection with a single payload TEXT property (JSON-
serialised). IDs are converted to UUIDs (passthrough if already
UUID, UUID5 hash otherwise).
| Method | Supported | Notes |
|---|---|---|
get_collections() / get_collection(name) / count(name) |
✅ | Count via Weaviate's aggregate.over_all(total_count=True) |
create_collection(name, vectors_config) |
✅ | Distance: cosine / l2-squared / dot |
upsert(name, points) |
✅ | Via v4 batch dynamic context manager |
search(name, query_vector, limit, score_threshold) |
✅ | near_vector(...) with distance metadata |
delete() by ID list |
✅ | Iterative data.delete_by_id(uuid) |
delete() by single must FieldCondition |
✅ | LIKE on payload property (GDPR shape) |
search() with query_filter |
✅ (single must FieldCondition) | Routes through _vector_filter.py to a Filter.by_property("payload").like('*"key":"value"*') chain. Compound filters raise NotImplementedError. |
| Multi-condition / nested delete filters | ❌ | |
| Hybrid search (BM25 + vector) | ❌ | Not exposed by the qdrant-shaped API |
Milvus adapter¶
src/db_adapters/vector_milvus.py (shipped 2026-05-20). Apache-2.0
vector database aimed at billion-scale workloads. Uses the v2
pymilvus MilvusClient API. Each Qdrant collection maps to a
Milvus collection with id (VARCHAR PK) + vector + payload
(JSON-encoded).
| Method | Supported | Notes |
|---|---|---|
get_collections() / get_collection(name) / count(name) |
✅ | |
create_collection(name, vectors_config) |
✅ | Distance: COSINE / L2 / IP via metric_type |
upsert(name, points) |
✅ | Native idempotent MilvusClient.upsert() |
search(name, query_vector, limit, score_threshold) |
✅ | Score normalised based on metric (COSINE / IP raw; L2 = 1-distance) |
delete() by ID list |
✅ | |
delete() by single must FieldCondition |
✅ | Milvus filter='payload like "..."' expression |
search() with query_filter |
✅ (single must FieldCondition) | Routes through _vector_filter.py to a Milvus filter='payload like "%\"key\":\"value\"%"' expression. Compound filters raise NotImplementedError. |
| Multi-condition / nested delete filters | ❌ |
ArangoDB adapter¶
src/db_adapters/graph_arangodb.py (shipped 2026-05-20). Apache-2.0
multi-model DB; native AQL query language. The adapter translates
the narrow Cypher subset RNR Enhanced Cognee uses into AQL via the
python-arango client. All graph nodes live in a single
ArangoDB document collection (default cognee_graph_nodes,
configurable via ARANGO_COLLECTION_NAME).
| Cypher | AQL translation |
|---|---|
RETURN <literal> |
RETURN <literal> |
MATCH (n) RETURN COUNT(n) [AS alias] |
FOR d IN @@col COLLECT WITH COUNT INTO c RETURN c |
MATCH (n) DETACH DELETE n |
FOR d IN @@col REMOVE d IN @@col |
MATCH (n) RETURN n |
FOR d IN @@col RETURN d |
CREATE (n:Label {props}) |
Parameterised form only (raises if literal props) |
| Multi-hop / WHERE clauses | ❌ raises NotImplementedError -- python-arango doesn't ship a Cypher engine; you'd need a real translator (3-5 days of work) for arbitrary Cypher. Switch to arcadedb / neo4j for full Cypher coverage. |
Async session API: ✅ -- get_async_graph_driver() returns an
_AsyncArangoDriver that wraps sync python-arango calls in
asyncio.to_thread. The threading-boundary overhead is modest for
typical workloads.
NebulaGraph adapter¶
src/db_adapters/graph_nebulagraph.py (shipped 2026-05-20).
Apache-2.0 distributed graph database via nebula3-python.
NebulaGraph 3+ supports openCypher mode natively, so most Cypher
queries pass through verbatim; the only rewrite is RETURN <literal>
-> YIELD <literal> (nGQL's literal-projection form). All graph
state lives in a single space (default cognee_space, configurable
via NEBULA_SPACE_NAME).
| Feature | Status |
|---|---|
Connectivity ping (RETURN 1 -> YIELD 1) |
✅ |
MATCH (n) RETURN COUNT(n) AS c |
✅ (openCypher passthrough) |
MATCH (n) DETACH DELETE n |
✅ (openCypher passthrough) |
| Multi-hop / WHERE clauses with property comparisons | ✅ for the queries NebulaGraph's openCypher mode supports natively. NebulaGraph 3+ is strict about tag schemas though -- properties must be declared on the tag first; that's a NebulaGraph constraint, not the adapter's. |
| Parameterised Cypher | ❌ -- nGQL parameter binding differs from Bolt-style. Inline literals or switch to arcadedb. |
| Async session API | ✅ -- get_async_graph_driver() returns an _AsyncNebulaDriver wrapping the sync nebula3-python client in asyncio.to_thread. |
Ladybug adapter¶
src/db_adapters/graph_ladybug.py (shipped 2026-05-20). Upstream
Cognee's experimental in-process graph engine; already a hard core
dependency. The adapter mirrors the networkx_inmemory shape -- a
narrow Cypher parser (RETURN <literal> / MATCH (n) RETURN
COUNT(n) / MATCH (n) DETACH DELETE n / MATCH (n) RETURN n) on
top of ladybug's native graph API.
| Feature | Status |
|---|---|
Connectivity ping (RETURN 1) |
✅ -- doesn't import ladybug; works as a no-op pre-flight |
MATCH (n) RETURN COUNT(n) [AS alias] |
✅ |
MATCH (n) DETACH DELETE n |
✅ |
MATCH (n) RETURN n |
✅ |
| Parameterised Cypher | ❌ -- Ladybug's API isn't a Cypher engine; inline literals. |
| Async session API | ✅ -- get_async_graph_driver() returns an _AsyncLadybugDriver wrapping the in-process sync ladybug graph in asyncio.to_thread. |
| Multi-hop / WHERE | ❌ -- Ladybug's API doesn't expose pattern-matching. Switch to arcadedb / neo4j for those queries. |
Apache AGE adapter -- supported query matrix¶
src/db_adapters/graph_apache_age.py is a hand-rolled shim that wraps
Cypher in AGE's SELECT * FROM cypher('graph', $$ ... $$) SQL form. It
covers the slice of Cypher RNR Enhanced Cognee call sites use today; complex
Cypher features will raise NotImplementedError with a pointer back to
this file.
| Feature | Status | Notes |
|---|---|---|
Connectivity ping (RETURN 1) |
OK | Used by bin/preflight.py and recovery_manager._validate_neo4j. |
Scalar projections (MATCH (n) RETURN COUNT(n)) |
OK | Returns _AGERecord with [0] / ["alias"] access. |
Write queries (MATCH ... DETACH DELETE, CREATE) |
OK | Must still close with AS (result agtype) per AGE 1.5 rules. |
| Iterating multiple records | OK | for record in result: ... yields _AGERecord instances. |
Parameterised Cypher ($param syntax) |
OK (Phase 5) | Passed via AGE's three-arg cypher(graph, $$ ... $$, agtype_map) form. Reject Cypher containing $$ (would break the dollar-quoted block; raises ValueError). |
| Async session API | OK (Phase 5) | get_async_graph_driver() returns an asyncpg-backed _AsyncAGEDriver. Use async with driver.session() as s: then await s.run(cypher). |
Returning native graph elements (MATCH (n) RETURN n) |
OK (Phase 5) | ::vertex decodes to _AGENode, ::edge to _AGERelationship, ::path to List[_AGENode \| _AGERelationship]. Same surface as neo4j.graph.Node / Relationship: .id / .labels / .type / .start_node / .end_node / node["prop"] / dict(node.items()). Path endpoints are stub _AGENodes with only the id populated -- if you need their labels/properties, issue a follow-up MATCH (n) WHERE id(n) = ... (AGE's edge payload doesn't inline endpoint data). |
APOC procedures (apoc.*) |
not supported | AGE has no APOC equivalent; switch to neo4j if you need APOC. |
If you hit something that doesn't work and isn't on the matrix yet, open an issue with the failing Cypher and we'll either add support or expand the row.
Setup steps¶
production¶
Nothing to do beyond the default install.sh / install.ps1. The
default compose already wires the production profile env vars into
the MCP server's environment.
apache_only¶
- Use a Postgres image that bundles AGE -- e.g.
apache/age:PG16_latestorapache/age:PG14_latest(replace the official postgres line indocker-compose-enhanced-cognee.yml). - Comment out the
arcadedbservice block. The MCP server'sdepends_onblock needs thearcadedb:line removed too. - Set the env vars:
- On first boot, create the graph (one-time):
- Run the smoke test:
lean¶
Same as apache_only but also:
- Set
ENHANCED_CACHE_PROVIDER=in_memory - Optionally skip the Qdrant container if you don't need vector search
(Phase 5's
pgvectoradapter will close this loop) - Drop the Valkey container from the compose file
byo¶
Point each *_HOST / *_URI / *_USER / *_PASSWORD env var at your
own infrastructure. No Compose file is required; the MCP server will
just connect.
Provider matrix¶
Full set of providers wired into src/db_factory.py as of 2026-05-20.
"Status" reflects whether the adapter is feature-complete; see the
per-tier deep-dives below for query-matrix limitations.
Relational tier (ENHANCED_RELATIONAL_PROVIDER)¶
| Provider | Status | Optional dep | Env vars consulted |
|---|---|---|---|
postgres (default) |
✅ full | core (asyncpg) |
POSTGRES_HOST / POSTGRES_PORT / POSTGRES_DB / POSTGRES_USER / POSTGRES_PASSWORD |
sqlite |
🟡 partial -- testing / lean only | [relational-sqlite] (aiosqlite) |
SQLITE_DB_PATH (falls back to POSTGRES_DB) |
mysql / oracle |
📋 not on roadmap |
Vector tier (ENHANCED_VECTOR_PROVIDER)¶
| Provider | Status | Optional dep | Env vars consulted |
|---|---|---|---|
qdrant (default) |
✅ full | core (qdrant-client) |
QDRANT_HOST / QDRANT_PORT / QDRANT_API_KEY |
pgvector |
🟡 narrow API (no filter in search) | [vector-pgvector] (psycopg2 + pgvector) |
PGVECTOR_HOST / PGVECTOR_PORT / PGVECTOR_DB / PGVECTOR_USER / PGVECTOR_PASSWORD / PGVECTOR_TABLE_PREFIX (falls through to POSTGRES_*) |
lancedb |
🟡 narrow API (no filter in search) | [vector-lancedb] (lancedb + pyarrow) |
LANCEDB_URI (filesystem path; default ./lancedb_data) |
chroma |
🟡 narrow API (no filter translation) | [vector-chroma] (chromadb) |
CHROMA_HOST / CHROMA_PORT (network mode) or CHROMA_PATH (persistent in-process) |
weaviate |
🟡 narrow API (no filter translation) | [vector-weaviate] (weaviate-client>=4) |
WEAVIATE_HOST / WEAVIATE_PORT / WEAVIATE_API_KEY |
milvus |
🟡 narrow API (no filter translation) | [vector-milvus] (pymilvus>=2.4) |
MILVUS_HOST / MILVUS_PORT / MILVUS_TOKEN (or MILVUS_USER + MILVUS_PASSWORD) |
The vector tier's API surface (Qdrant's
QdrantClient) is rich (get_collections/create_collection/upsert/search/ etc.). All five non-Qdrant adapters cover the narrow slice RNR Enhanced Cognee actually uses; everything else (named vectors, sparse vectors, payload indexes, quantization, rich search filters, hybrid search) raises a clearNotImplementedError. The first adapter shipped (pgvector) set the_PgVectorClientduck-typing pattern; the remaining four (lancedb, chroma, weaviate, milvus, shipped 2026-05-20) follow the same_CollectionDescription/_SearchHitshape so callers don't have to special-case the provider.
Graph tier (ENHANCED_GRAPH_PROVIDER)¶
| Provider | Status | Optional dep | Env vars consulted |
|---|---|---|---|
arcadedb (default) |
✅ full | core (neo4j) |
ARCADEDB_URI / ARCADEDB_USER / ARCADEDB_PASSWORD |
neo4j |
✅ full (legacy) | core (neo4j) |
NEO4J_URI / NEO4J_USER / NEO4J_PASSWORD |
apache_age |
🟡 narrow Cypher subset (params + async since 2026-05-20) | core (psycopg2) |
AGE_HOST / AGE_PORT / AGE_DB / AGE_USER / AGE_PASSWORD / AGE_GRAPH_NAME (falls through to POSTGRES_*) |
memgraph |
✅ full | [graph-memgraph] (reuses neo4j) |
MEMGRAPH_URI / MEMGRAPH_USER / MEMGRAPH_PASSWORD |
kuzu |
🟡 sync-only | [graph-kuzu] |
KUZU_DB_PATH |
networkx_inmemory |
🟡 narrow Cypher subset (testing only) | core (networkx) |
-- |
arangodb |
🟡 narrow Cypher subset translated to AQL | [graph-arangodb] (python-arango) |
ARANGO_HOST / ARANGO_PORT / ARANGO_DB / ARANGO_USER / ARANGO_PASSWORD / ARANGO_COLLECTION_NAME |
nebulagraph |
🟡 narrow Cypher subset via NebulaGraph openCypher mode | [graph-nebulagraph] (nebula3-python) |
NEBULA_HOST / NEBULA_PORT / NEBULA_USER / NEBULA_PASSWORD / NEBULA_SPACE_NAME |
ladybug |
🟡 narrow Cypher subset (upstream Cognee default) | [graph-ladybug] (ladybug already core) |
LADYBUG_DB_PATH |
Cache tier (ENHANCED_CACHE_PROVIDER)¶
| Provider | Status | Optional dep | Env vars consulted |
|---|---|---|---|
valkey (default) |
✅ full | core (redis) |
REDIS_HOST / REDIS_PORT / REDIS_PASSWORD |
redis |
✅ full (alias) | core (redis) |
Same as valkey (wire-compatible). |
redis_compat |
✅ full (alias) | core (redis) |
For wire-compatible Redis forks (KeyDB / Garnet / Dragonfly). Signals operational intent vs valkey/redis. Same env vars. |
in_memory |
🟡 single-process only | none -- pure dict | -- |
memcached |
🟡 no keys() / flushdb() |
[cache-memcached] (pymemcache) |
MEMCACHED_HOST / MEMCACHED_PORT |
Status legend¶
- ✅ full -- complete neo4j-driver / asyncpg / QdrantClient / redis surface, suitable for production
- 🟡 partial -- works for a specific use case; see per-adapter sub-section below for the supported-query / API matrix
- 📋 deferred -- not yet wired; raise an issue if you need it shipped now
Legacy *_BACKEND aliases (RELATIONAL_BACKEND / VECTOR_BACKEND /
GRAPH_BACKEND / CACHE_BACKEND) are still honoured at lower
precedence than the canonical ENHANCED_*_PROVIDER names.
Optional-dep install¶
To install only the adapters you actually use:
pip install "RNR-Enhanced-Cognee[graph-memgraph]" # adds neo4j driver
pip install "RNR-Enhanced-Cognee[graph-kuzu]"
pip install "RNR-Enhanced-Cognee[graph-networkx]"
pip install "RNR-Enhanced-Cognee[cache-memcached]"
pip install "RNR-Enhanced-Cognee[relational-sqlite]"
Or all of them at once: pip install "RNR-Enhanced-Cognee[graph-memgraph,graph-kuzu,graph-networkx,cache-memcached,relational-sqlite]".
Related docs¶
docs/HANDOVER.mdsection 4 -- phase-by-phase roadmapdocs/STRATEGY.mdsection 4.1 -- profile designdocs/STRATEGY.mdDR-08 -- env-var routing decisiondocs/ARCADEDB_MIGRATION.md-- Phase 2 ArcadeDB swapdocs/LICENSE_AUDIT.md-- per-component licence detail