Metadata-Version: 2.2
Name: unicontext
Version: 0.1.0
Summary: Universal Python SDK for AI memory/context providers with HTTP-first architecture
Author-email: MetacognitionAI <hello.metacognitionlabs@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/metacoglabs/unicontext
Project-URL: Documentation, https://github.com/metacoglabs/unicontext#readme
Project-URL: Repository, https://github.com/metacoglabs/unicontext
Project-URL: Issues, https://github.com/metacoglabs/unicontext/issues
Keywords: ai,memory,context,sdk,mem0,zep,letta,supermemory
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx<1.0,>=0.25
Provides-Extra: mem0
Requires-Dist: mem0ai>=0.1; extra == "mem0"
Provides-Extra: supermemory
Requires-Dist: supermemory>=0.1; extra == "supermemory"
Provides-Extra: zep
Requires-Dist: zep-cloud>=0.1; extra == "zep"
Provides-Extra: letta
Requires-Dist: letta-client>=0.1; extra == "letta"
Provides-Extra: all
Requires-Dist: mem0ai>=0.1; extra == "all"
Requires-Dist: supermemory>=0.1; extra == "all"
Requires-Dist: zep-cloud>=0.1; extra == "all"
Requires-Dist: letta-client>=0.1; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest<9,>=7; extra == "dev"
Requires-Dist: pytest-asyncio<1.0,>=0.23; extra == "dev"
Requires-Dist: ruff<1.0,>=0.4; extra == "dev"
Requires-Dist: mypy<2.0,>=1.0; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"

# UniContext

Universal Python SDK for agent memory and context with a unified API across multiple providers.

UniContext is HTTP-first: built-in providers call provider REST APIs via `httpx`. Switch providers by changing the provider name and API key.

[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Features

- Unified, provider-agnostic client API
- Typed models and input validation
- Consistent error types across providers
- Shared HTTP transport (connection pooling)

## Installation

From PyPI:

```bash
pip install unicontext
```

From source (development):

```bash
pip install -e .
```

Optional development tools:

```bash
pip install -e ".[dev]"
```

## Quick start

This example uses Letta (archives mode) because it has synchronous IDs you can `get()` and `delete()` immediately.

```python
from unicontext import MemoryInput, Scope, SearchQuery, create_client

client = create_client(provider="letta", api_key="YOUR_LETTA_API_KEY")
scope = Scope(user_id="user_123")

created = client.add(MemoryInput(text="User prefers Python for backend"), scope)

hits = client.search(SearchQuery(query="backend", scope=scope, limit=5))
for hit in hits:
    print(hit.record.content)

fetched = client.get(memory_id=created.id, scope=scope)
print(fetched.content)

client.delete(memory_id=created.id, scope=scope)
```

## Switching providers

```python
from unicontext import create_client

mem0 = create_client(provider="mem0", api_key="YOUR_MEM0_API_KEY")
supermemory = create_client(provider="supermemory", api_key="YOUR_SUPERMEMORY_API_KEY")
zep = create_client(provider="zep", api_key="YOUR_ZEP_API_KEY")
letta = create_client(provider="letta", api_key="YOUR_LETTA_API_KEY")
```

## Core concepts

### Scope

`Scope` selects where the memory lives (user, agent, thread, tags).

```python
from unicontext import Scope

Scope(user_id="user_123")
Scope(user_id="user_123", thread_id="thread_456")
Scope(user_id="user_123", tags=["preferences", "onboarding"])
```

### Inputs

`MemoryInput` must provide exactly one of `text` or `messages`.

```python
from unicontext import MemoryInput

MemoryInput(text="User prefers dark mode")
MemoryInput(messages=[{"role": "user", "content": "Hi"}])
```

## Provider notes

UniContext normalizes the public API, but providers have different semantics.

### Mem0

- `add()` is asynchronous; the returned ID may be an event ID.
- Use `search()` after processing completes to retrieve the finalized memory ID.

```python
import time

from unicontext import MemoryInput, Scope, SearchQuery, create_client

client = create_client(provider="mem0", api_key="YOUR_MEM0_API_KEY")
scope = Scope(user_id="user_123")

client.add(MemoryInput(text="User likes espresso"), scope)
time.sleep(20)

hits = client.search(SearchQuery(query="espresso", scope=scope, limit=5))
if hits:
    memory_id = hits[0].record.id
    client.delete(memory_id=memory_id, scope=scope)
```

### Supermemory

- Scope is primarily tag-driven.
- `delete_by_scope()` requires at least one tag to reduce accidental deletes.

### Zep

- Dual routing: if `scope.thread_id` is set, `add()` writes to a thread; otherwise it writes to the knowledge graph.
- `search()` targets the knowledge graph.
- `get()` and `delete()` are not supported for graph records.

### Letta

- Default mode uses archives and passages.
- If you do not provide an archive ID, UniContext auto-creates and caches one per `scope.user_id`.

## Error handling

```python
from unicontext import Scope, SearchQuery, create_client
from unicontext.exceptions import AuthError, ProviderError, ValidationError

client = create_client(provider="letta", api_key="YOUR_LETTA_API_KEY")
scope = Scope(user_id="user_123")

try:
    client.search(SearchQuery(query="hello", scope=scope, limit=5))
except ValidationError as e:
    print(e.message)
except AuthError as e:
    print(e.message)
except ProviderError as e:
    print(e.message)
```

## Development

Run unit tests:

```bash
python -m pytest -v
```

Run the pre-release checks:

```bash
python scripts/pre_release.py
```
