Metadata-Version: 2.4
Name: memory-model
Version: 0.5.0
Summary: Official Python SDK for MemoryModel API
Author: MemoryModel Team
License: MIT
Keywords: agents,ai,memory,memory-model,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.8
Requires-Dist: pydantic>=2.0.0
Requires-Dist: requests>=2.25.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Description-Content-Type: text/markdown

# memory-model

Official Python SDK for [MemoryModel](https://memorymodel.dev).

## Installation

```bash
pip install memory-model
```

## Quick Start

```python
from memory_model import MemoryClient

client = MemoryClient(
    api_key="sk_live_...",
    default_end_user_id="user_123"
)

# Add a memory
client.add("Project deadline is Friday.")

# Search memories
results = client.search("When is the deadline?")
for memory in results:
    print(f"[{memory.similarity:.2f}] {memory.content}")
```

---

## API Reference

### Constructor

```python
MemoryClient(
    api_key: str,                      # Required
    base_url: str = "https://api.memorymodel.dev",
    default_end_user_id: str = None,
    timeout: int = 30,                 # Seconds
    max_retries: int = 3,              # Retries for 5xx/429
    api_version: str = "v1"            # API version prefix
)
```

### Methods

#### `add(content, *, user_context=None, end_user_id=None)`
```python
response = client.add("Meeting moved to 3pm.", user_context="Calendar update")
print(response.job_id)
```

#### `add_image(image_data, *, user_context=None, end_user_id=None)`
```python
import base64
with open("screenshot.png", "rb") as f:
    img_b64 = base64.b64encode(f.read()).decode()

response = client.add_image(img_b64, user_context="Error screenshot")
```

#### `search(query, *, limit=5, strategy="centroid_aware", end_user_id=None)`
```python
results = client.search("What time is the meeting?", limit=3)
```

#### `list(*, limit=50, memory_type=None, end_user_id=None)`
```python
memories = client.list(limit=10)
```

#### `delete(memory_id, *, end_user_id=None)`
```python
client.delete("mem_abc123")
```

#### `search_image(image_data, *, limit=5, end_user_id=None)` — Visual Similarity Search
```python
import base64

# Load and encode image
with open("query_image.jpg", "rb") as f:
    img_b64 = base64.b64encode(f.read()).decode()

# Find visually similar memories
results = client.search_image(img_b64, limit=5)
for memory in results:
    print(f"Found: {memory.id}")
    if hasattr(memory, 'source_doc'):
        print(f"  → From: {memory.source_doc}, Page: {memory.page_number}")
```

> **Note**: Image search finds memories visually similar to the input image. Only memories ingested via `add_image()` will be matched.

#### `upload_document(file_data, *, file_name="document.pdf", end_user_id=None)` — Upload & Process PDF

Upload a PDF file directly. The file is sent as multipart/form-data, stored on MemoryModel's infrastructure, and automatically queued for processing.

```python
# Read and upload a PDF
with open("contract.pdf", "rb") as f:
    result = client.upload_document(f.read(), file_name="contract.pdf")

print(result.storage_path)  # Where the file was stored
print(result.job_id)         # Processing job ID
```

This method:
1. Uploads the PDF to MemoryModel's storage (max 20MB)
2. Automatically queues server-side processing
3. Extracts text from each page and creates linked memories

#### `add_document(storage_path, *, end_user_id=None)` — Process Pre-Uploaded PDF

If the PDF is already in MemoryModel storage (e.g. uploaded via console), trigger processing by path:

```python
client.add_document("projects/my-project/docs/contract.pdf")
```

> **Tip**: Most users should use `upload_document()`. Use `add_document()` only if the file is already on our storage (e.g. uploaded via the Console UI).

---

## Error Handling

```python
from memory_model import MemoryClient, MemoryClientError

try:
    client.search("test")
except MemoryClientError as e:
    print(e.message)       # Human-readable error
    print(e.status)        # HTTP status (401, 429, 500...)
    print(e.code)          # Error code from API
    print(e.is_retryable)  # True for 5xx/429
```

**Automatic Retry**: The SDK retries on 5xx/429 with exponential backoff (1s → 2s → 4s).

---

## Agent Integration

### LangChain / CrewAI Example

```python
from memory_model import MemoryClient
from langchain_openai import ChatOpenAI
from langchain.schema import SystemMessage, HumanMessage

memory = MemoryClient(api_key="sk_live_...", default_end_user_id="user_123")
llm = ChatOpenAI(model="gpt-4o")

def agent_respond(user_message: str) -> str:
    # 1. Store user message
    memory.add(user_message, user_context="User question")
    
    # 2. Retrieve context
    context = memory.search(user_message, limit=3)
    context_str = "\n".join([f"- {m.content}" for m in context])
    
    # 3. Generate response
    messages = [
        SystemMessage(content=f"Context:\n{context_str}"),
        HumanMessage(content=user_message)
    ]
    response = llm.invoke(messages)
    
    return response.content
```

---

### Image-Based Search Example

```python
import base64
from memory_model import MemoryClient

memory = MemoryClient(api_key="sk_live_...", default_end_user_id="user_123")

def find_similar_images(image_path: str):
    # 1. Load and encode query image
    with open(image_path, "rb") as f:
        img_b64 = base64.b64encode(f.read()).decode()
    
    # 2. Search for visually similar memories
    results = memory.search_image(img_b64, limit=5)
    
    # 3. Results may include source document info
    for mem in results:
        print(f"Found: {mem.id}")
        if hasattr(mem, 'source_doc'):
            print(f"  → From: {mem.source_doc}, Page: {mem.page_number}")
    
    return results
```

