Outhad ContextKit SDK
Long-term memory for AI agents, as an API: one pip install, no databases or models to run
Quick Start • Features • Configuration • All Methods • Docs
Status: the hosted ContextKit cloud is not open for sign-ups yet. If you run your own ContextKit server, point this client at it with
host=(see Configuration).
Why the SDK?
Agents forget everything between sessions. Giving them memory usually means running a vector database, a graph database and an LLM extraction pipeline, and keeping all of it in sync.
ContextKit runs all of that for you. This package is the client:
- Tiny: one dependency (
httpx); no torch, no vector drivers, no model SDKs - Everything server-side: fact extraction, embeddings, vector + graph storage, re-ranking
- Sync and async:
MemoryClientandAsyncMemoryClientshare the same methods - Your models: choose the LLM and embedder per project, or override them per call
Quick Start
Installation
pip install outhad-contextkit-sdk
Get an API key
In the ContextKit dashboard, open API keys → Create key. Keys look like ock_live_... (production) or ock_test_... (sandbox).
export OUTHAD_CONTEXTKIT_API_KEY="ock_live_..."
60-Second Setup
from openai import OpenAI
from outhad_contextkit_sdk import MemoryClient
memory = MemoryClient() # reads OUTHAD_CONTEXTKIT_API_KEY
openai_client = OpenAI()
# Remember
memory.add(messages=[{"role": "user", "content": "I'm allergic to peanuts"}], user_id="alice")
# Recall only what matters
hits = memory.search(query="What should I avoid eating?", user_id="alice", limit=3)
facts = "\n".join(hit["memory"] for hit in hits["results"])
# Give your LLM the facts, not the whole transcript
response = openai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": f"What you know about the user:\n{facts}"},
{"role": "user", "content": "Suggest a snack for me"},
],
)
print(response.choices[0].message.content)
Features
1. Memory That Manages Itself
Facts, not transcripts: the cloud extracts, de-duplicates and updates memories for you.
# Conversations become facts automatically
memory.add(messages=conversation, user_id="alice")
# Short-lived facts forget themselves
memory.add(messages=[{"role": "user", "content": "Pizza party at 7pm tonight"}],
user_id="alice", expiration_date="2026-11-01T19:00:00Z")
memory.get_all(user_id="alice", page=1, page_size=50)
memory.history(memory_id) # every change, in order
memory.pin(memory_id) # lock a critical fact against updates and deletes
# Safe retries: the same key always returns the same result
memory.add_idempotent("signup-12345", messages=conversation, user_id="alice")
What makes it powerful:
- Automatic Fact Extraction: conversations become short, de-duplicated facts
- Scoped Memory: separate memory per
user_id,agent_idorrun_id - Expiry & Pinning:
expiration_datefor temporary facts;pin()protects the ones that must never change - Safe Retries:
add_idempotent()makes network retries byte-identical
2. Retrieval Beyond Vector Search
Graph, time and keyword retrieval in one call. Turn on Graph mode for your project in Dashboard → Settings to unlock the graph features.
# Context graph: related memories come along with the best match
memory.search(query="What should I eat?", user_id="alice", use_context_graph=True)
# Temporal + causal (TCMGM): ask *when* and *why*
memory.search(query="When did the API slow down?", user_id="dev_team", use_tcmgm=True,
time_window={"start": "2026-10-01T00:00:00Z", "end": "2026-10-15T00:00:00Z"})
# Hybrid keyword search: rare names, typos, acronyms
memory.search(query="Acme", user_id="alice", keyword_search=True)
# LLM relevance gate: drop hits that don't actually answer the question
memory.search(query="What food do I like?", user_id="alice", filter_memories=True)
What makes it powerful:
- Context Graph: memories link to each other, so related context arrives together
- Temporal + Causal: timeline filtering plus cause → effect chains
- Hybrid Search: keyword matches fused with vector hits (reciprocal-rank fusion)
- Fine Control:
threshold,top_k,categories,filters,includes/excludes
3. Personalisation That Learns
Results improve as your users react to them.
hits = memory.search(query="revenue forecast", user_id="alice", use_context_graph=True)
# Tell ContextKit what helped. Future searches for this query rank it higher.
memory.feedback(hits["results"][0]["id"], helpful=True,
user_id="alice", query="revenue forecast")
What makes it powerful:
- Feedback Loop: helpful / unhelpful verdicts boost or bury memories for that user
- Learns What Worked: memories that answered a query before get lifted for the same query
- Usage-Aware: memories your agent keeps using rank higher
- Per-Project Tuning: custom instructions, categories and retrieval weights in the dashboard
4. Versioning & Lifecycle
Versions, rollback and freshness: inspect history, restore, archive.
memory.update(memory_id, text="I love sushi and ramen")
memory.versions(memory_id) # the version chain
memory.diff_versions(old_id, new_id) # what changed
memory.rollback(memory_id, to_version=1) # restore an earlier version
memory.decay_score(memory_id) # 0.0 (stale) → 1.0 (fresh)
memory.archive_low(threshold=0.05) # archive what has gone stale
memory.demote_to_cold(memory_id) # cheap storage, restorable any time
What makes it powerful:
- Version Chain: diff any two versions; a rollback is written as a new version
- Decay Scores: recency, usage and feedback blended into one freshness number
- Safe Archival: stale memories leave search results without being deleted
- Cold Storage: demote rarely used memories, then
promote_from_cold()when needed
Note:
update()currently edits a memory in place, so new versions come fromrollback()only.
5. Workspace & Security
Production controls built in.
# Sandbox keys write to a separate collection from live keys
sandbox = MemoryClient(api_key="ock_test_...")
# Projects, members and webhooks are managed in the dashboard and readable here
memory.list_projects()
memory.list_webhooks(memory.project_id)
# Who did what, and when (needs a key with the audit.read scope)
memory.audit_log(limit=20)
What makes it powerful:
- Projects & Roles: per-project models and settings;
READER/WRITER/ADMIN/OWNERmembers - Live & Test Environments:
ock_test_keys never touch production data - Scoped Keys: read-only keys, IP allowlists, and key rotation with a grace period
- Signed Webhooks:
memory.created/memory.updated/memory.deleted, HMAC-SHA256 signed
6. Observability
See what your agents remember and how they use it.
memory.usage() # request counts, latency, endpoint mix
memory.requests(min_status=400) # recent failures
memory.top_users(limit=5) # your most active end-users
memory.graph_snapshot(user_id="alice") # one user's memory graph: nodes + edges
What makes it powerful:
- Usage: counts, latencies and endpoint mix over any
since/untilwindow - Request Log: filter by path, status or end-user to debug a single call
- Top Users: see which end-users drive your traffic
- Graph Snapshot: export a user's memory graph to inspect or visualise
7. Bring Your Own Model
Project defaults, per-call overrides.
# Run one call on a different model; your key is never stored server-side
memory.add(
messages=conversation,
user_id="alice",
llm={"provider": "anthropic", "model": "claude-haiku-4-5-20251001", "api_key": "sk-ant-..."},
embedder={"provider": "openai", "model": "text-embedding-3-large"},
)
What makes it powerful:
- Dashboard Defaults: set the project's LLM and embedder once in Settings
- Per-Call Override:
llm=/embedder=onadd,searchandsummary - Bring Your Own Key: pass
api_keyinside the dict for a single call; it is never persisted - Embedder-Safe: each embedder gets its own vector collection, so switching models never mixes dimensions
Async
AsyncMemoryClient has the same methods as MemoryClient; just await them.
import asyncio
from outhad_contextkit_sdk import AsyncMemoryClient
async def main():
async with AsyncMemoryClient() as memory:
await memory.add(messages=[{"role": "user", "content": "I prefer dark mode"}], user_id="alice")
hits = await memory.search(query="UI preferences?", user_id="alice")
print(hits["results"])
asyncio.run(main())
Configuration
| Setting | Argument | Environment variable | Default |
|---|---|---|---|
| API key | api_key= |
OUTHAD_CONTEXTKIT_API_KEY |
required |
| Server | host= |
OUTHAD_CONTEXTKIT_HOST |
https://api.contextkit.outhad.com |
| Timeout | timeout= |
30 seconds | |
| Local fallback | allow_dev_fallback= |
False. Set True during local development to retry http://localhost:8000 when the default server is unreachable |
Self-hosted server:
memory = MemoryClient(api_key="ock_live_...", host="https://memory.your-company.com")
Requests carry Authorization: Token <key>. When calling the HTTP API directly, Bearer <key> and X-API-Key: <key> work too.
Errors
from outhad_contextkit_sdk import APIError, AuthenticationError
try:
memory.update(memory_id, text="...")
except AuthenticationError:
... # 401: key missing, malformed, revoked or expired
except APIError as error:
print(error.status, error.body) # e.g. 409 when the memory is pinned
All Methods
Memories: add, search, get, update, delete, history
add · add_idempotent · search · get · get_all · get_all_filtered · update · history · delete · delete_all · pin · unpin · batch_update · batch_delete · summary · export · users · delete_users · reset
Personalisation & graph: feedback, backfill, resets
feedback · backfill · reset_cgl · reset_feedback
Versioning & lifecycle: versions, rollback, decay, cold storage
versions · latest_version · diff_versions · rollback · decay_score · tick_decay · archive_low · record_reference · demote_to_cold · promote_from_cold
Workspace: projects, members, webhooks, API keys, audit log
list_projects · get_project · create_project · update_project · delete_project · list_members · add_member · update_member · remove_member · list_webhooks · create_webhook · update_webhook · delete_webhook · list_api_keys · create_api_key · revoke_api_key · rotate_api_key · update_api_key · audit_log
Creating or changing projects, members, webhooks and keys requires a dashboard sign-in; API keys can read them.
Observability: usage, requests, top users, graph snapshot
usage · requests · top_users · graph_snapshot
Documentation
License
Apache-2.0.
Metadata
Release files for outhad-contextkit-sdk 1.10.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 | Provenance | |
|---|---|---|---|---|
| outhad_contextkit_sdk-1.10.0.tar.gz | 26.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| outhad_contextkit_sdk-1.10.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 49.2 kB
Release files / outhad_contextkit_sdk-1.10.0.tar.gz
| Download URL | outhad_contextkit_sdk-1.10.0.tar.gz |
|---|---|
| Size | 26.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7cc18b016e7c6d76ce78cd5492a5b749d442c96187ade39ca7221c179b1960ff
|
|
BLAKE2b-256 checksum How to use checksums |
a64affe28c628ba5fde545bc8fdf3a93b20fd380474abf6bb76c3b78eca73d4d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Release files / outhad_contextkit_sdk-1.10.0-py3-none-any.whl
| Download URL | outhad_contextkit_sdk-1.10.0-py3-none-any.whl |
|---|---|
| Size | 22.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bd67ffa7537fa227d15a4e37fd536319b428f83ca180f97f881d2f27f737934e
|
|
BLAKE2b-256 checksum How to use checksums |
2853edcfff4a407bb876962f6e0d70d4fc3e7c0e316f09aa03c24a0806bc8343
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Security for outhad-contextkit-sdk 1.10.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.