cortadel
Official Python SDK for Cortadel — self-hosted long-term temporal graph memory for AI agents. A thin, typed client over the Cortadel REST API.
pip install cortadel
# or: uv add cortadel / poetry add cortadel
Python ≥ 3.10. Ships two facades: an async client and a real blocking client — pick whichever fits your program.
Quickstart (async)
import asyncio
from cortadel import ChatMessage, CortadelClient, SearchOptions
async def main() -> None:
async with CortadelClient("http://localhost:3001", "alice") as cortadel:
# apiKey: pass api_key="<token>" — omit when the server runs with auth disabled
# Store
await cortadel.add("Alice prefers dark mode and ships on Fridays.")
# Recall (hybrid BM25 + vector + RRF)
hits = await cortadel.search("what are alice's preferences?", SearchOptions(top_k=5))
for h in hits.results:
print(h.rrf_score, h.content)
# Ingest a conversation
await cortadel.add_conversation([
ChatMessage(role="user", content="I'm allergic to peanuts."),
ChatMessage(role="assistant", content="Noted — I'll avoid peanut recipes."),
])
# List / get / delete
page = await cortadel.list()
one = await cortadel.get(page.items[0].id)
await cortadel.delete([page.items[0].id])
asyncio.run(main())
Quickstart (blocking)
from cortadel import SyncCortadelClient
with SyncCortadelClient("http://localhost:3001", "alice") as cortadel:
cortadel.add("Alice prefers dark mode and ships on Fridays.")
hits = cortadel.search("what are alice's preferences?")
for h in hits.results:
print(h.rrf_score, h.content)
SyncCortadelClient is a real blocking client, not asyncio.run(...) called once per method —
see cortadel/sync_client.py's module docstring for why that distinction matters (connection-pool
reuse, and correctness when called from a thread that already has a running event loop) and how it
is implemented (one persistent background event loop, for the client's lifetime).
Auth
Pass api_key in either constructor and every request carries Authorization: Bearer <key>. Omit
it (or leave it None) when the server runs with auth disabled — no header is sent, and the
client never mutates an httpx.AsyncClient you bring in either way.
import os
from cortadel import CortadelClient
cortadel = CortadelClient("https://my-box:3001", "alice", api_key=os.environ.get("CORTADEL_API_KEY"))
Reuse a single client per base URL + user. Every call it makes is scoped to the user_id you
construct it with.
Methods
Both clients expose the same seven methods (async def on CortadelClient, blocking on
SyncCortadelClient):
| Method | Returns | Notes |
|---|---|---|
add(text, options=None) |
MemoryCreated |
Store a memory. options.infer (default True) runs background entity/category extraction; False stores verbatim (dedup still applies). |
add_conversation(messages, options=None) |
ConversationResult |
Distill atomic facts from a transcript and store each one. |
search(query, options=None) |
SearchResults |
Hybrid search (BM25 + vector fused with RRF); set options.rerank = "cross_encoder" to rerank. |
list(options=None) |
MemoryList |
Paginated, newest-first. options.size defaults to 20 (a deliberate SDK-wide choice — see below). |
get(memory_id) |
MemoryDetail | None |
None when the memory doesn't exist; never raises for a 404. Content field is .text. |
delete(memory_ids) |
str |
Deletes one or more memories; returns the server's confirmation message. |
health() |
HealthResult |
Database + embedding provider reachability. Does not raise when the server reports itself degraded — a degraded server is a normal return value (status == "degraded"), not an exception. |
ListOptions.size defaults to 20, not the REST contract's own default of 10 — kept in sync
with the .NET and TypeScript SDKs so every Cortadel SDK behaves identically regardless of which
one you're reading examples for.
Errors
Any non-success response — other than a degraded health check, which health() returns instead
of raising — raises a CortadelError:
from cortadel import CortadelClient, CortadelError
async with CortadelClient("http://localhost:3001", "alice") as cortadel:
try:
await cortadel.add("")
except CortadelError as err:
print(err.status, err.code, err.message)
# asyncio.CancelledError from a cancelled task/timeout propagates untouched instead —
# it is never wrapped as a CortadelError.
| Attribute | Meaning |
|---|---|
status |
HTTP status code. 0 when the transport failed before a status was known. |
code |
Machine-readable error code (e.g. not_found, validation_error). |
message |
Human-readable message — for a 400 from model validation, this folds in the |
| server's per-field errors instead of a generic "the request failed". |
Bring your own HTTP client
Both constructors accept http_client: httpx.AsyncClient to reuse an existing client (connection
pooling, proxies, custom TLS, etc. all carry over). It is never mutated or closed by the SDK —
you own its lifecycle either way.
import httpx
from cortadel import CortadelClient
async with httpx.AsyncClient(proxy="http://proxy.internal:8080") as http_client:
cortadel = CortadelClient("http://localhost:3001", "alice", http_client=http_client)
...
License
Apache-2.0
Metadata
Release files for cortadel 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cortadel-1.1.0.tar.gz | 31.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cortadel-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 92.4 kB
Release files / cortadel-1.1.0.tar.gz
| Download URL | cortadel-1.1.0.tar.gz |
|---|---|
| Size | 31.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1e7ba0cbb8e73169ab7d15ec2348613255ce9a540dc5b89c62cb53268fff843b
|
|
BLAKE2b-256 checksum How to use checksums |
e696bd165c3e42a7831dee24df49a2943728f85140c9805a66d9e69b56bd9fab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / cortadel-1.1.0-py3-none-any.whl
| Download URL | cortadel-1.1.0-py3-none-any.whl |
|---|---|
| Size | 61.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3a2b7f5310de2b47a9a38b27e83543c4ab94a8bff67bf34040f1eefcccdb4f57
|
|
BLAKE2b-256 checksum How to use checksums |
e454be562cb7354f4c5fc2856ae28c6f7845d19053f023e2f97ff2e7b94ef479
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|