Metadata-Version: 2.4
Name: octamem
Version: 1.0.1
Summary: Official OctaMem SDK - Access your OctaMem memories from Python
Project-URL: Homepage, https://octamem.com
Project-URL: Documentation, https://docs.octamem.com
Project-URL: Repository, https://github.com/alphatradeai/octamem-python
Project-URL: Issues, https://github.com/alphatradeai/octamem-python/issues
Author-email: OctaMem <support@octamem.com>
License: MIT License
        
        Copyright (c) OctaMem
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: ai,memory,memory-service,octamem,sdk
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
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 :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.25.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: typing-extensions>=4.5.0
Provides-Extra: dev
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-httpx>=0.21.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# octamem

[![PyPI version](https://img.shields.io/pypi/v/octamem.svg)](https://pypi.org/project/octamem/)
[![Python versions](https://img.shields.io/pypi/pyversions/octamem.svg)](https://pypi.org/project/octamem/)
[![License: MIT](https://img.shields.io/pypi/l/octamem.svg)](https://pypi.org/project/octamem/)

**Memory as a Service for Python** — Add persistent, searchable memory to your AI agents, chatbots, and applications with a few lines of code.

Official Python SDK for [OctaMem](https://octamem.com). Sync and async clients, full type hints, and automatic retries.

---

## ⚡ Quick Start

Get from zero to a working call in under 30 seconds.

**1. Install**

```bash
pip install octamem
```

**2. Run**

```python
from octamem import OctaMem

client = OctaMem(api_key="your-api-key-here")

# Search memories (previous_context scopes the query to a conversation or topic)
results = client.get(query="What did we decide about the launch date?", previous_context="Q1 product planning meeting")
print(results)

# Add a memory (previous_context links this to a conversation or topic)
client.add(content="Launch date set for April 15. Beta opens March 20.", previous_context="Q1 product planning meeting")

# Get usage and limits
details = client.details()
print(details)
```

**Using an environment variable instead:** set `OCTAMEM_API_KEY` and call `OctaMem()` with no arguments.

---

## Use cases

- **AI agents & chatbots** — Give your bot a persistent memory so it remembers context and past conversations.
- **Personal knowledge bases** — Store and search notes, decisions, and learnings by natural language.
- **Multi-tenant apps** — One API key per user or tenant; isolate memories per client.
- **RAG and retrieval** — Use OctaMem as the memory layer and query with natural language instead of managing embeddings yourself.

---

## Installation

**From PyPI (recommended)**

```bash
pip install octamem
```

**From source (editable)**

```bash
pip install -e /path/to/octamem-python
```

Then `import octamem` works from any Python file in that environment.

---

## API at a glance

| Method | Parameters | Returns | Description |
|--------|-------------|---------|-------------|
| `get(...)` | `query`, `previous_context=""` (or `params: GetParams`) | `SearchResult` | Search memories by natural language. Sends `{"query", "previousContext"}` to `/api/memory/search`. |
| `add(...)` | `content`, `previous_context=""` (or `params: AddParams`) | `AddResult` | Store content in memory. Sends `{"content", "previousContext"}` to `/api/memory/add`. |
| `details()` | *(none)* | `MemoryDetails` | Get usage and limits. Sends `{}` to `/api/memory/details`. |

All methods exist on **sync** (`OctaMem`) and **async** (`AsyncOctaMem`); async uses `await client.get(...)` etc.

**Parameter styles** — Keyword arguments (recommended) or a params dict:

```python
# Keyword style (recommended) — e.g. chatbot recalling a support conversation
client.get(query="What did the customer say about their billing issue?", previous_context="Support conversation with customer #4521")
client.add(content="Customer requested a refund for the duplicate charge. Ticket escalated.", previous_context="Support conversation with customer #4521")

# Params dict (keys: query, previousContext / content, previousContext)
client.get(params={"query": "What are the user's notification preferences?", "previousContext": "User settings"})
client.add(params={"content": "User prefers weekly digest and push for mentions only.", "previousContext": "User settings"})
```

---

## Response types (from the SDK)

The SDK returns Pydantic models; all allow extra fields from the API (`.model_dump()` for dicts).

| Method | Return type | Defined attributes | Extra |
|--------|-------------|--------------------|-------|
| `get(...)` | `SearchResult` | `pending_id: str \| None` | Allowed |
| `add(...)` | `AddResult` | `success: bool` (default `True`) | Allowed |
| `details()` | `MemoryDetails` | `usage: int \| None`, `limit: int \| None` | Allowed |

**Example usage:**

```python
from octamem import OctaMem

client = OctaMem(api_key="your-api-key")

# Search — SearchResult (pending_id + any extra API fields)
results = client.get(query="What did we decide about the launch date?", previous_context="Q1 product planning meeting")
print(results)
print(results.pending_id)
print(results.model_dump())

# Add — AddResult (success + any extra API fields)
add = client.add(content="Launch date set for April 15. Beta opens March 20.", previous_context="Q1 product planning meeting")
print(add.success)

# Details — MemoryDetails (usage, limit)
details = client.details()
print(details)
print(details.usage, details.limit)
```

---

## Sync vs async

- **Sync (`OctaMem`)** — Use in scripts, CLI tools, or any blocking context. Simple `client.get()`, `client.add()`, `client.details()`.
- **Async (`AsyncOctaMem`)** — Use in async frameworks (FastAPI, asyncio, etc.). Same API with `await`: `await client.get()`, `await client.add()`, `await client.details()`.

Same capabilities; pick the style that matches your stack.

**Async example**

```python
import asyncio
from octamem import AsyncOctaMem

async def main():
    client = AsyncOctaMem(api_key="your-api-key")
    results = await client.get(query="What are the user's notification preferences?", previous_context="User onboarding flow")
    await client.add(content="User chose weekly digest emails and in-app notifications only.", previous_context="User onboarding flow")
    details = await client.details()
    print(details)
    await client.close()

asyncio.run(main())
```

Or with a context manager:

```python
async with AsyncOctaMem(api_key="your-api-key") as client:
    results = await client.get(query="What did we decide about the launch?", previous_context="Product roadmap sync")
    await client.add(content="Ship MVP by end of Q2. Skip dark mode for v1.", previous_context="Product roadmap sync")
    details = await client.details()
    print(details)
```

---

## Configuration

Constructor options (same for `OctaMem` and `AsyncOctaMem`). All except `api_key` are keyword-only.

| Parameter | Default | Type | Description |
|-----------|---------|------|-------------|
| `api_key` | `os.environ.get("OCTAMEM_API_KEY")` | `str \| None` | API key. Prefer env for security. |
| `base_url` | `"https://platform.octamem.com"` | `str` | API base URL. |
| `timeout` | `30.0` | `float` | Request timeout in seconds. |
| `max_retries` | `3` | `int` | Retries on connection/timeout/rate-limit/5xx. |
| `retry_delay` | `1.0` | `float` | Base delay between retries (seconds). |

```python
# With explicit api_key (or omit to use OCTAMEM_API_KEY env var)
client = OctaMem(
    api_key="your-api-key",
    base_url="https://platform.octamem.com",
    timeout=30.0,
    max_retries=3,
    retry_delay=1.0,
)
```

---

## Error handling and retries

The client retries on connection errors, timeouts, rate limits, and 5xx responses. You can still handle specific errors and implement custom retry logic.

**Catch specific errors**

```python
from octamem import OctaMem, AuthenticationError, RateLimitError, APIConnectionError, ValidationError

client = OctaMem(api_key="your-api-key")

try:
    client.add(content="New note", previous_context="")
except AuthenticationError:
    print("Invalid or missing API key")
except RateLimitError as e:
    print(f"Rate limited. Retry after: {e.retry_after}s")
except APIConnectionError:
    print("Network error")
except ValidationError as e:
    print(f"Validation error: {e.message}")
```

**Retry on rate limit (example)**

The SDK already retries with backoff; if you want custom logic (e.g. log and retry once):

```python
import time
from octamem import OctaMem, RateLimitError

client = OctaMem(api_key="your-api-key")

def add_with_retry(content: str, previous_context: str = "", max_attempts: int = 2):
    for attempt in range(max_attempts):
        try:
            return client.add(content=content, previous_context=previous_context)
        except RateLimitError as e:
            if attempt == max_attempts - 1:
                raise
            wait = e.retry_after or 60
            time.sleep(wait)

add_with_retry("Important note", previous_context="")
```

**Exception hierarchy**

```
OctaMemError (base)
├── AuthenticationError      # 401
├── MethodNotSupportedError  # 403
├── NotFoundError            # 404
├── ValidationError         # 400
├── RateLimitError           # 429 (has .retry_after)
├── APIError                 # Other API errors
├── APIConnectionError       # Network
├── APITimeoutError          # Timeout
└── InternalServerError      # 5xx
```

---

## Context managers

Both clients support context managers so connections are closed cleanly.

```python
# Sync — e.g. script recalling a past conversation
with OctaMem(api_key="your-api-key") as client:
    results = client.get(query="What did the customer ask for?", previous_context="Support chat session #8823")
    details = client.details()
    print(details)

# Async — e.g. agent storing meeting takeaways
async with AsyncOctaMem(api_key="your-api-key") as client:
    results = await client.get(query="What were the action items?", previous_context="Weekly standup March 15")
    await client.add(content="Alice: finish API docs. Bob: review auth flow.", previous_context="Weekly standup March 15")
    details = await client.details()
    print(details)
```

---

## Type safety

The SDK is fully typed (PEP 561, `py.typed`). Exported types:

- **Params:** `GetParams` (`query`, `previousContext`), `AddParams` (`content`, `previousContext`)
- **Responses:** `SearchResult`, `AddResult`, `MemoryDetails`

Use them for IDE autocomplete and type checking. Note: in params dicts the key is `previousContext` (camelCase) to match the API.

```python
from octamem import OctaMem, GetParams, AddParams, MemoryDetails, SearchResult, AddResult

client = OctaMem(api_key="your-api-key")

# Params dicts use previousContext (camelCase) — e.g. scoping to a feature or conversation
params: GetParams = {"query": "What did we decide about pricing?", "previousContext": "Sales strategy call"}
results: SearchResult = client.get(params)

add_params: AddParams = {"content": "Free tier: 10k memories. Pro: unlimited.", "previousContext": "Sales strategy call"}
out: AddResult = client.add(add_params)

details: MemoryDetails = client.details()
print(details)
```

---

## Optional: `previous_context`

Both `get` and `add` accept an optional `previous_context` argument (sent as `previousContext` in the request body). Use it to scope or link memories to a conversation or topic.

```python
from octamem import OctaMem

client = OctaMem(api_key="your-api-key")

# Keyword arguments (Python: previous_context) — scope search and add to the same conversation or topic
results = client.get(query="What did the user prefer for notifications?", previous_context="Onboarding conversation")
add = client.add(content="User selected email digest weekly and Slack for alerts.", previous_context="Onboarding conversation")
details = client.details()
print(details)
```

---

## Requirements

- Python 3.9+
- httpx
- pydantic

---

## Why OctaMem?

- **Memory as a service** — No need to run your own vector DB or manage embeddings; focus on your product.
- **Natural language search** — Query with questions or phrases instead of building search pipelines.
- **Built for AI** — Designed for agents and chatbots that need persistent, searchable memory across sessions.
- **Simple API** — Three main operations: search, add, details. Sync and async, with retries and type safety out of the box.

---

## License

MIT
