Skip to main content

Outhad ContextKit SDK

Long-term memory for AI agents, as an API: one pip install, no databases or models to run

PyPI version Python 3.9+ License

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: MemoryClient and AsyncMemoryClient share 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_id or run_id
  • Expiry & Pinning: expiration_date for temporary facts; pin() protects the ones that must never change
  • Safe Retries: add_idempotent() makes network retries byte-identical

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 from rollback() 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 / OWNER members
  • 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 / until window
  • 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= on add, search and summary
  • Bring Your Own Key: pass api_key inside 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)

Source distribution for outhad-contextkit-sdk 1.10.0
File Size Uploaded Provenance
outhad_contextkit_sdk-1.10.0.tar.gz 26.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for outhad-contextkit-sdk 1.10.0
File Interpreter ABI Platform
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.

Release history Release notifications | RSS feed

This release

1.10.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page