Metadata-Version: 2.4
Name: totalrecall-sdk
Version: 0.1.2
Summary: Official Python SDK for TotalRecall - Memory Infrastructure for AI
Author-email: TotalRecall <hello@totalrecall.dev>
License: MIT
Project-URL: Homepage, https://totalrecall.dev
Project-URL: Documentation, https://totalrecall.dev/docs
Project-URL: Repository, https://github.com/tope-olajide/total-recall
Project-URL: Issues, https://github.com/tope-olajide/total-recall/issues
Keywords: ai,memory,llm,qwen,semantic-search,embeddings,vector,rag
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.24.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Provides-Extra: crewai
Requires-Dist: crewai-tools>=0.1.0; extra == "crewai"

# TotalRecall Python SDK

> **Build AI that remembers.** The official Python SDK for [TotalRecall](https://totalrecall.dev) — production-grade memory infrastructure for AI applications.

[![Python Version](https://img.shields.io/pypi/pyversions/totalrecall-sdk.svg)](https://pypi.org/project/totalrecall-sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Features

- 🧠 **Persistent Memory** — Store memories forever with semantic embeddings
- 🔍 **Semantic Search** — Find memories by meaning, not keywords
- 🤖 **Auto Extraction** — AI automatically extracts memories from conversations
- 🔄 **Intelligent Forgetting** — Qwen decides what to keep, archive, or delete
- 📊 **Quality Scoring** — Monitor memory health with composite scores
- 🔗 **Conflict Detection** — Automatically detect contradictory information
- 📝 **Memory Versioning** — Track how knowledge evolves over time

## Installation

```bash
pip install totalrecall-sdk
```

## Quick Start

```python
import asyncio
from totalrecall import TotalRecall

async def main():
    client = TotalRecall(
        api_key="tr_your_api_key",
        project_id="my-project-id",
    )

    # Store a memory
    await client.create_memory(
        content="User prefers dark mode and Vim editor",
        category="preference",
        importance=0.8,
    )

    # Search memories
    results = await client.search_memories(
        query="What editor does the user prefer?",
    )
    print(results)
    # [SearchResult(content='User prefers dark mode and Vim editor', score=0.95, ...)]

    # Get formatted context for your LLM
    context = await client.search_as_context(
        "Tell me about the user's preferences"
    )
    # "- [preference] User prefers dark mode and Vim editor (relevance: 95%)"

    await client.close()

asyncio.run(main())
```

## API Reference

### Configuration

```python
client = TotalRecall(
    api_key="tr_your_api_key",        # Required: Your API key
    project_id="proj_123",            # Required: Project ID
    base_url="https://totalrecall.theatomicshift.com/api",  # Optional: API base URL
    max_retries=3,                     # Optional: Retry attempts (default: 3)
    timeout=30.0,                      # Optional: Timeout in seconds (default: 30)
)
```

### Context Manager

```python
async with TotalRecall(api_key="...", project_id="...") as client:
    results = await client.search_memories(query="test")
# Client automatically closed
```

### Memory Operations

#### `create_memory(content, ...)`

Create a new memory.

```python
memory = await client.create_memory(
    content="User is allergic to peanuts",
    category="health",
    label="allergy",
    importance=0.95,
    metadata={"source": "conversation", "conversation_id": "conv_123"},
)
```

#### `get_memory(memory_id)`

Retrieve a memory by ID.

```python
memory = await client.get_memory("mem_abc123")
print(memory.content, memory.importance, memory.version)
```

#### `list_memories(limit, offset, category)`

List all memories for the project.

```python
memories = await client.list_memories(limit=50, category="preference")
```

#### `update_memory(memory_id, ...)`

Update an existing memory.

```python
updated = await client.update_memory(
    "mem_abc123",
    content="User now prefers VS Code",
    importance=0.9,
)
```

#### `delete_memory(memory_id)`

Delete a memory (soft delete — sets `is_active: False`).

```python
await client.delete_memory("mem_abc123")
```

### Search Operations

#### `search_memories(query, ...)`

Search memories by semantic similarity.

```python
results = await client.search_memories(
    query="What are the user's coding preferences?",
    category="preference",
    limit=10,
    threshold=0.7,
)

for result in results:
    print(f"{result.content} (score: {result.score})")
```

#### `search_as_context(query, limit, max_tokens)`

Search and format results as context for LLM injection. Automatically handles token budgeting.

```python
context = await client.search_as_context(
    "Tell me about this user",
    limit=10,
    max_tokens=2000,
)

# Use in your LLM prompt:
prompt = f"""
You are a helpful assistant. Here's what you know about the user:

{context}

User asks: {user_question}
"""
```

### Stats & Versions

#### `get_stats()`

Get memory statistics for the project.

```python
stats = await client.get_stats()
print(f"Total: {stats.total}, Active: {stats.active}")
print(f"Avg importance: {stats.avg_importance}")
```

#### `get_versions(memory_id)`

Get version history for a memory.

```python
versions = await client.get_versions("mem_abc123")
for v in versions:
    print(f"v{v.version}: {v.content} ({v.created_at})")
```

### AI Memory Intelligence

#### `detect_conflicts(content, category)`

Detect contradictions between new content and existing memories.

```python
conflicts = await client.detect_conflicts(
    content="User hates React",
    category="opinion",
)

if conflicts:
    print(f"Found {len(conflicts)} conflicts:")
    for c in conflicts:
        print(f"  - {c.conflict_type}: '{c.existing_content}' vs '{c.new_content}'")
```

#### `merge_memories(memory_ids, content)`

Merge multiple memories into one.

```python
merged = await client.merge_memories(
    memory_ids=["mem_abc", "mem_def"],
    content="User prefers dark mode, Vim, and TypeScript",
)
```

### Intelligent Forgetting

#### `run_forgetting_cycle(dry_run, max_api_calls)`

Run the forgetting cycle to evaluate and clean up memories.

```python
result = await client.run_forgetting_cycle(dry_run=False, max_api_calls=10)

print(f"Evaluated {result.total_evaluated} memories")
print(f"Kept: {result.kept}, Archived: {result.archived}, Deleted: {result.deleted}")
```

#### `preview_forgetting_cycle(max_api_calls)`

Preview what the forgetting cycle would do without making changes.

```python
preview = await client.preview_forgetting_cycle(max_api_calls=5)
print("Dry run results:")
for a in preview.actions:
    print(f"  {a.action}: '{a.content}' ({a.reason})")
```

### Quality & Summary

#### `get_quality_score()`

Get the memory health score for the project.

```python
quality = await client.get_quality_score()
print(f"Health: {quality.overall_score}/100")
print(f"Freshness: {quality.freshness}, Diversity: {quality.diversity}")
print(f"Categories: {quality.category_distribution}")
```

#### `get_summary()`

Get an AI-generated summary of all project memories.

```python
summary = await client.get_summary()
print(summary)
# "This project tracks a software developer who prefers dark mode..."
```

### Webhooks

#### `create_webhook(name, url, events)`

Register a webhook for memory events.

```python
webhook = await client.create_webhook(
    name="Slack Notifications",
    url="https://hooks.slack.com/xxx",
    events=["memory.created", "memory.updated"],
)
print(f"Secret: {webhook.secret}")  # Only shown on creation!
```

#### `list_webhooks()`

List all webhooks for the project.

```python
webhooks = await client.list_webhooks()
for w in webhooks:
    print(f"{w.name}: {'active' if w.is_active else 'inactive'}")
```

#### `delete_webhook(webhook_id)`

Delete a webhook.

```python
await client.delete_webhook("wh_abc123")
```

#### `toggle_webhook(webhook_id)`

Toggle a webhook's active state.

```python
toggled = await client.toggle_webhook("wh_abc123")
print(f"{'active' if toggled.is_active else 'inactive'}")
```

#### `test_webhook(webhook_id)`

Send a test event to a webhook.

```python
result = await client.test_webhook("wh_abc123")
print(f"Delivered to {result.triggered}/{result.total} webhooks")
```

### Audit Logs

#### `get_audit_logs(limit, offset, action, resource)`

Get audit logs for the project.

```python
logs = await client.get_audit_logs(limit=50, action="memory.create")
for log in logs:
    print(f"{log.action} on {log.resource} at {log.created_at}")
```

#### `get_audit_log_stats()`

Get audit log statistics.

```python
stats = await client.get_audit_log_stats()
print(f"Total events: {stats.total}")
print(f"Action breakdown: {stats.action_breakdown}")
```

### Batch Operations

#### `create_memories_batch(inputs)`

Create multiple memories at once.

```python
memories = await client.create_memories_batch([
    {"content": "User likes TypeScript", "category": "preference"},
    {"content": "User works at Acme Corp", "category": "work"},
    {"content": "User prefers dark mode", "category": "preference"},
])
```

#### `search_batch(queries, limit)`

Search multiple queries at once.

```python
results = await client.search_batch(
    ["What does the user do for work?", "What are their preferences?"],
    limit=5,
)
work_results, pref_results = results
```

## Error Handling

The SDK raises `TotalRecallError` with descriptive messages. Client errors (4xx) are raised immediately, while server errors (5xx) are retried with exponential backoff.

```python
from totalrecall import TotalRecall
from totalrecall.client import TotalRecallError

try:
    memory = await client.get_memory("nonexistent")
except TotalRecallError as e:
    print(f"Error ({e.status_code}): {e}")
    # Error (404): Memory not found
```

## Type Hints

The SDK is fully typed with dataclasses and type hints for excellent IDE support.

```python
from totalrecall import TotalRecall, Memory, SearchResult

client: TotalRecall = TotalRecall(
    api_key="tr_...",
    project_id="my-project",
)

results: list[SearchResult] = await client.search_memories(query="test")
```

## License

MIT © 2026 TotalRecall
