Python SDK for Dakera AI — the memory engine for AI agents
| Dakera | Others | |
|---|---|---|
| LoCoMo Recall@20 | 88.2% (1,536 Q, LLM-judged retrieval recall) | not directly comparable |
| Deployment | Single binary, Docker one-liner | External vector DB + embedding service required |
| Embeddings | Built-in — no OpenAI key needed | Requires external embedding API |
| Search modes | Vector · BM25 · Hybrid · Knowledge Graph | Usually one or two |
| Transport | HTTP + gRPC | HTTP only |
→ Try the playground · Full benchmark results · dakera.ai
docker run -d \
--name dakera \
-p 3000:3000 \
-e DAKERA_ROOT_API_KEY=dk-mykey \
ghcr.io/dakera-ai/dakera:latest
curl http://localhost:3000/health # → {"status":"ok"}For persistent storage with Docker Compose:
curl -sSfL https://raw.githubusercontent.com/Dakera-AI/dakera-deploy/main/docker/docker-compose.yml \
-o docker-compose.yml
DAKERA_API_KEY=dk-mykey docker compose up -dFull deployment guide (Docker Compose, Kubernetes, Helm): dakera-deploy
pip install dakeraFor async support (AsyncDakeraClient):
pip install dakera[async]Works with LangChain, LlamaIndex, CrewAI, AutoGen, and any Python agent framework.
from dakera import DakeraClient
client = DakeraClient(base_url="http://localhost:3000", api_key="dk-mykey")
client.store_memory(agent_id="my-agent", content="User prefers brevity", importance=0.9)Full example — store, recall, upsert, and hybrid search:
from dakera import DakeraClient
client = DakeraClient(base_url="http://localhost:3000", api_key="dk-mykey")
# Store an agent memory
client.store_memory(
agent_id="my-agent",
content="User prefers concise responses with code examples",
importance=0.9,
tags=["preference"],
)
# Recall memories (semantic search)
response = client.recall(agent_id="my-agent", query="what does the user prefer?", top_k=5)
for m in response.memories:
print(f"[{m.importance:.2f}] {m.content}")
# Upsert vectors
client.upsert("my-namespace", vectors=[
{"id": "vec1", "values": [0.1, 0.2, 0.3], "metadata": {"category": "docs"}},
])
# Hybrid search (vector + BM25)
results = client.hybrid_search("my-namespace", query="completed task", top_k=5, vector_weight=0.7)
for r in results:
print(r.id, r.score)import asyncio
from dakera import AsyncDakeraClient
async def main():
client = AsyncDakeraClient(base_url="http://localhost:3000", api_key="dk-mykey")
response = await client.recall(agent_id="my-agent", query="preferences", top_k=5)
for m in response.memories:
print(m.content)
asyncio.run(main())- Agent Memory — store, recall, search, and forget memories with importance scoring
- Sessions — group memories by conversation with auto-consolidation on session end
- Knowledge Graph — traverse memory relationships, find paths, export graphs
- Vector Search — ANN queries with metadata filters and batch operations
- Full-Text Search — BM25 ranking with stemming and stop-word filtering
- Hybrid Search — combine vector similarity with keyword matching
- Text Auto-Embedding — server-side embedding generation (no local model needed)
- Namespaces — isolated vector stores per project, tenant, or use case
- Feedback Loop — upvote/downvote/flag memories to improve recall quality
- T-I-F Reliability —
TifScoreandevaluate_tif()for Truth-Indeterminacy-Falsity scoring of memory reliability - Entity Extraction — GLiNER NER for automatic entity detection
- Streaming — SSE event subscriptions for real-time memory updates
- Sync + Async — full parity between
DakeraClientandAsyncDakeraClient - Typed Models — full type annotations with strict mypy, PEP 561
py.typedmarker - Retry & Rate Limiting — built-in exponential backoff,
Retry-Afterhonoured on503/429, and rate-limit header tracking - Attachments & Records — upload audio/images, transcribe or index them into memories, store multi-representation records (server v0.12+)
- Agents, Keys & Session Lifecycle — create agents, edit keys and grant prefix patterns, rotate with a grace period,
whoami, session idle timeouts andtouch(server v0.12.2+) - Filter DSL —
F.eq(),F.gt(),F.contains()typed filter builder
Version 0.14.1 of this SDK adds support for Dakera server v0.12.3; the features
below need server v0.12.3 or later. It stays compatible with v0.12.0 – v0.12.2 servers:
the new request fields are sent only when you set them, the new response fields are
None from an older server.
recall(..., neighborhood_mode=):"inline"(the server default) appends session-adjacent context after thetop_kranked results, each markedretrieval == "neighborhood"(itsscoreis from a different search and is not comparable);"separate"keepsmemoriesto at mosttop_kresults and puts the context inresponse.context_memories, each withcontext_of(the ranked memory it belongs to);"off"fetches none.response.rankedis the ranked results alone.include_summaries=Falsekeeps session summaries out.- Results (recall and
search_memories()) carrymatched_ids/matched_textwhen the server collapses derived sentence sub-memories into their source. POST /admin/keys,POST /v1/namespaces/{ns}/keys,POST /admin/backupsandPOST /admin/encryption/rotate-keyrefuse unknown fields with422: the SDK sends only the fields the server reads (create_key(permissions=, expires_at=)andcreate_backup(include_data=)are deprecated and never sent).ops_shutdown()needs a globalsuper_adminkey (admingets403).- Event time:
store_memory(..., event_time=)andBatchStoreMemoryItem(event_time=)record when something happened (Unix seconds, RFC 3339 or an awaredatetime), so imported or replayed history is ranked and filtered by its own dates. - Ranking: each recall result carries
rank_score, non-increasing down the list;recall(..., rerank_candidates=N)bounds the reranker's work andresponse.rerank_reportsays what it did (cappedwhen the server's cap applied). - Long text: a store whose text is longer than the embedding model reads returns
truncated,content_tokensandembedded_tokens(batch:response.truncated). - Ignored fields: store, batch store, recall and batch recall answers list request
fields the server did not use in
warnings. - Restorable deduplicated memories:
list_deduplicated(agent_id)andrestore_deduplicated(agent_id, memory_id);autopilot_update_config(dedup_mode=, trash_retention_hours=, ...)controls automatic deduplication. get_memory(None, memory_id)finds a memory without its agent id; session ids may contain:; recalling an agent that has stored nothing returns no memories.
response = client.recall("my-agent", "which port does the API use?", top_k=3,
neighborhood_mode="separate")
for m in response.memories: # at most 3, ranked
print(m.score, m.content, m.matched_text)
for c in response.context_memories or []:
print(" context of", c.context_of, ":", c.content)A document becomes chunk memories of an agent, sized for the embedding model, each
with its heading breadcrumb and a _dakera_document provenance record (document,
page range, heading path) that recall results carry. Imports run as server jobs.
from dakera import ChunkingOptions
caps = client.capabilities()
if caps.supports_documents:
accepted = client.import_document(
"docs-bot", open("README.md").read(), media_type="text/markdown",
document_id="readme", tags=["docs"], chunking=ChunkingOptions(max_tokens=300),
)
job = client.wait_for_document_job(accepted.job_id, "docs-bot", timeout=120)
print(job.status, job.message)
# PDF / DOCX: upload the file, then import the attachment
ref = client.upload_attachment("library", "manual.pdf").attachment_ref
client.import_attachment_document("library", ref, "docs-bot", document_id="manual-v3")
for doc in client.iter_documents("docs-bot"):
print(doc.document_id, doc.title, doc.chunk_count)
for m in client.recall("docs-bot", "how do I install it?").ranked:
if m.document: # cite the source
print(m.document.title, m.document.page_start, m.document.breadcrumb)
client.delete_document("manual-v3", "docs-bot")Version 0.14.0 of this SDK adds support for Dakera server v0.12.2 and stays
compatible with v0.12.0 and v0.12.1 servers: every new request field is sent
only when you set it, and every new response field is optional (None / []
from an older server). The v0.12.2-only routes answer 404 / 405 on an older
server.
- Agents —
create_agent(agent_id)(POST /v1/agents) creates an agent's memory namespace before its first memory (created=Falsefor an existing one). - Keys —
update_key()/update_namespace_key()rename a key or replace itsnamespaces(all_namespaces=Truegrants every namespace);rotate_key(key_id, grace_secs=N)keeps the old key working up to 7 days (old_key_id,old_key_expires_atin the answer;RotateKeyResponse.from_dict()types it);whoami()(GET /v1/auth/whoami);KeyInfo.grants_version/inert_namespaces;create_key()sendsscope(defaultread),namespaces(exact names orp*prefix patterns such as["_dakera_agent_mlx-*"]) andexpires_in_days. - Sessions —
start_session(..., idle_timeout_secs=N),touch_session()(SessionTouchResponse:session_state,idle_deadline_at), and the session fieldslast_activity_at,ended_reason(client|idle),idle_since,idle_timeout_secs(Session.from_dict()types them).store_memory()returnssession_statewhen the memory went into a session;BatchStoreMemoryResponse.ended_sessionslists ended sessions a batch stored into.update_config(session_idle_timeout_secs=N)sets the server-wide timeout.ChatMemorySession.create(..., idle_timeout_secs=N)and.touch(). - Listings —
agent_memories(..., include_derived=, content_preview_chars=, offset=),session_memories(..., content_preview_chars=, limit=, offset=),wake_up(..., include_derived=), andcontent_preview_charsonfull_knowledge_graph()/cross_agent_network(). With a preview each memory or node carriescontent_lenandcontent_truncated; read a truncated memory in full withget_memory()before showing or editing it. - Derived data —
derivations_status()anddrain_derivations(timeout_secs=)(GET /admin/derivations/status,POST /admin/derivations/drain; a running drain is aConflictError). - Capabilities v2 —
capabilities().auth,.naming,.sessions;NamespaceInfo.kind(agent/data/system). - Additive fields —
duplicates_skipped_changed(deduplicate),CompressResponse.summaries_skipped,unavailableon node-wide endpoints (NamespaceUnavailableonttl_stats(),memory_type_stats(),storage_tier_overview(); passed through on the dict-returning ones such asops_stats()),unavailableonlist_agents()entries,MemoryEvent.reason.
These are server changes; the SDK does not hide them.
- Sessions are authorized by their agent. A key needs Read/Write on
_dakera_agent_<agent_id>; a_dakera_sessionsgrant is no longer needed and is reported ininert_namespaces. A key without grants lists no sessions.end_session()with a Read key is a403. - Sessions end automatically after 4 h without activity by default
(
DAKERA_SESSION_IDLE_TIMEOUT_SECS), withended_reason: "idle". Activity is a memory stored / updated with the session, a session-scoped recall or search, ortouch_session(). Storing into an ended session still succeeds: checksession_state/ended_sessions. On upgrade, sessions already idle longer than the timeout are closed on the first passes. - Stricter validation (
400, the message names the field). Invalid keynamespacesentries; reserved markers (thedakera-curatedtag,_dakera_*metadata keys other than_dakera_content_date/_dakera_lang, idsmem_s+ 24 hex characters); metadata over 100 fields;ttl_secondsover 100 years; agent ids over 241 bytes;_dakera_embedding_modelsis reserved. - The memory content limit is in UTF-8 bytes (default 100000,
DAKERA_MAX_MEMORY_CONTENT_BYTES), not characters. It now also applies toupdate_memory()(a memory stored above the limit may only be updated to content no larger than it is) and to theend_session()summary. - Listings exclude derived records by default.
agent_memories()andwake_up()no longer return the derived sentence sub-memories; passinclude_derived=Truefor the previous listing. - Legacy
foo*key entries stay inert until the key'snamespacesare saved again (update_key(..., namespaces=[...]));grants_versionis0for such keys.
from dakera import DakeraClient
client = DakeraClient("http://localhost:3000", api_key="your-key")
client.create_agent("mlx-dev")
session = client.start_session("mlx-dev", idle_timeout_secs=2 * 3600)
stored = client.store_memory("mlx-dev", "User prefers dark mode", session_id=session["id"])
if stored.get("session_state") == "ended":
session = client.start_session("mlx-dev")
client.touch_session(session["id"]) # keep an idle session open
page = client.agent_memories("mlx-dev", limit=50, content_preview_chars=200)
print(client.whoami().scope)Version 0.13.0 of this SDK adds support for Dakera server v0.12.0
(operator upgrade guide: docs/v0.12/UPGRADE.md in the server release; release notes in the
Dakera changelog).
Compatible with both v0.11.108 and v0.12.0 servers. Everything new is additive:
calls that do not use a v0.12 feature send exactly what they sent before, and
the v0.12-only calls fail with a clear error (NotFoundError / 405) on a v0.11
server.
- Health and readiness — a v0.12 server binds its port while models load and
answers
503+Retry-Afteron/health.client.is_ready()/client.wait_until_ready()use/health/ready(a503is never "healthy");health_ready()andhealth_live()map to/health/readyand/health/live. - Errors and retries — every error body is JSON.
503raisesServiceUnavailableError(aServerError) and the retry logic waits for the server'sRetry-After.413raisesPayloadTooLargeError(.is_quotafor a namespace quota,.is_oversizefor an over-size request),501raisesFeatureNotAvailableError(detailsnames theDAKERA_*switch),409raisesConflictError;NotFoundError.resourcesays what was not found. GET /v1/capabilities—client.capabilities(): models (bge-m3,colbert-small), index kinds (ivfpq), search mode (rabitq), record kinds and dtypes, query languages, plusscoring,attachments,vision. Unknown strings parse as unknown enum members instead of raising.- Attachments (opt-in on the server,
DAKERA_ATTACHMENTS) —upload_attachment,list_attachments,download_attachment,delete_attachment,store_memory(..., attachment_ref=...),transcribe_attachment/get_transcription_job/wait_for_transcriptionand, withDAKERA_VISION,index_attachment/wait_for_index. - Records (opt-in,
DAKERA_RECORDS) —upsert_records/get_record: one primary vector plus nameddense,token_multivectororpatch_multivectorrepresentations stored asf32,f16ori8. - Per-request
langonstore_memory,store_memories_batch,update_memory,recall,search_memoriesandextract_entities. - Namespace config —
replace_namespace_ner_config()(PUT) replaces the entity-extraction config; the v0.12 server'sPATCHmerges and refuses unknown fields. - Fix — async
extract_entities()senttextinstead ofcontent.
from dakera import DakeraClient, Record, Representation, RepresentationKind, BlockDType
client = DakeraClient("http://localhost:3000", api_key="your-key")
client.wait_until_ready(timeout=120)
caps = client.capabilities()
if caps.supports_attachments:
up = client.upload_attachment("_dakera_agent_a1", "note.wav")
job = client.transcribe_attachment("_dakera_agent_a1", up.attachment_ref, "a1", lang="en")
done = client.wait_for_transcription("_dakera_agent_a1", up.attachment_ref, job.job_id)
if caps.supports_records:
client.upsert_records("docs", [Record(
id="r1", values=[0.1, 0.2, 0.3, 0.4],
representations=[Representation(
"tokens", [[0.1, 0.2], [0.3, 0.4]],
kind=RepresentationKind.TOKEN_MULTIVECTOR, store_as=BlockDType.F16)])])Note: the v0.12 server's gRPC port requires an API key. This SDK speaks REST only.
from dakera import DakeraClient, RetryConfig
# Self-hosted
client = DakeraClient(base_url="http://your-server:3000", api_key="your-key")
# Cloud (early access)
client = DakeraClient(base_url="http://<your-server-ip>:3000", api_key="your-key")
# With custom retry config
client = DakeraClient(
base_url="http://localhost:3000",
api_key="your-key",
retry_config=RetryConfig(max_retries=5, base_delay=0.2),
)TealTiger is a governance middleware for AI agents that enforces cost limits, decision policies, and delegation rules. Use Dakera as the persistent backend for all TealTiger artefacts:
pip install dakera[tealtiger] tealtigerimport asyncio
from dakera.async_client import AsyncDakeraClient
from dakera.integrations.tealtiger import (
DakeraCostStorage,
DakeraDecisionStore,
DakeraDelegationHelper,
)
client = AsyncDakeraClient("http://localhost:3000", api_key="dk-mykey")
# Drop-in async CostStorage backend — passes directly to TealTiger client
cost_storage = DakeraCostStorage(client)
from tealtiger import TealOpenAI, TealOpenAIConfig
teal_client = TealOpenAI(config=TealOpenAIConfig(cost_storage=cost_storage))
# Governance decision audit trail with idempotency checks (all methods are async)
decision_store = DakeraDecisionStore(client)
# receipt_id = await decision_store.store_receipt("my-agent", decision)
# is_duplicate = await decision_store.is_terminal("my-agent", correlation_id)
# Multi-hop delegation chain traversal via memory knowledge graph
delegation = DakeraDelegationHelper(client)
await delegation.link_delegation(child_id=child_mem_id, parent_id=parent_mem_id, agent_id="my-agent")
chain = await delegation.get_delegation_chain("my-agent", root_id, max_depth=5)All cost records, decision receipts, and delegation chains are stored in Dakera memory with importance-weighted retention (DENY receipts at 0.95 outlast ALLOW at 0.80) and full knowledge-graph traversal for audit purposes.
See examples/tealtiger_governance.py for a complete walkthrough. Join the integration discussion or visit the TealTiger repo.
See the examples/ directory:
basic_usage.py— vectors, namespaces, queries, filtershybrid_search.py— full-text, vector, and hybrid searchollama_memory_chat.py— persistent memory for a local Ollama chat loopollama_memory_proxy.py— transparent memory proxy in front of Ollama (/api/chat)tealtiger_governance.py— TealTiger governance middleware
| Documentation | Full API reference and guides |
| Python SDK docs | Python-specific reference |
| Benchmark | LoCoMo evaluation results |
| dakera.ai | Website and early access |
| GitHub Org | All public repos |
| dakera-deploy | Self-hosting guide |
| SDK | Package |
|---|---|
| dakera-js | @dakera-ai/dakera (npm) |
| dakera-rs | dakera-client (crates.io) |
| dakera-go | github.com/dakera-ai/dakera-go |
| dakera-cli | CLI tool |
| dakera-mcp | MCP server for Claude/Cursor |
dakera.ai · Docs · Benchmark · Request Early Access
Built with Rust. Single binary. Zero external dependencies.
