Metadata-Version: 2.4
Name: aivm-brain
Version: 0.1.0
Summary: Official Python SDK for the AIVM Brain memory API (memories + ask).
Project-URL: Homepage, https://github.com/AIVMNetwork/aivm-sdk-workspace
Project-URL: Documentation, https://github.com/AIVMNetwork/aivm-sdk-workspace/tree/main/python#readme
Project-URL: Repository, https://github.com/AIVMNetwork/aivm-sdk-workspace
Project-URL: Issues, https://github.com/AIVMNetwork/aivm-sdk-workspace/issues
Project-URL: Changelog, https://github.com/AIVMNetwork/aivm-sdk-workspace/blob/main/python/CHANGELOG.md
Author: AIVM
License: MIT
License-File: LICENSE
Keywords: aivm,api,brain,memory,sdk
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2
Provides-Extra: dev
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: pytest-asyncio; extra == 'dev'
Requires-Dist: respx; extra == 'dev'
Description-Content-Type: text/markdown

# aivm-brain (Python SDK)

Official Python client for the [AIVM Brain](https://aivm.io) memory API — `add`,
`search`, `get`, `list`, `update`, `delete` memories plus `ask`, with typed methods
and models instead of hand-written HTTP. Ships sync (`AivmBrain`) and async
(`AsyncAivmBrain`) clients sharing one transport/error/serialization core.

## Install

```bash
pip install aivm-brain
```

Requires Python ≥ 3.9. Runtime deps: `httpx`, `pydantic` v2.

## Authentication

Use an AIVM agent key (`ak_live_…`). Provide it explicitly or via the
`AIVM_API_KEY` environment variable; if neither is set, constructing the client
raises `ConfigurationError`. The key is sent as `Authorization: Bearer <key>` and
is **never logged**. **Server-side use only** — do not embed a key in a browser or
mobile app.

```python
from aivm_brain import AivmBrain

brain = AivmBrain(api_key="ak_live_...")      # explicit
brain = AivmBrain()                            # or read AIVM_API_KEY from the env
```

Options: `AivmBrain(api_key=None, tenant_id=None, base_url=None, timeout=30.0, max_retries=2)`.
`base_url` defaults to `AIVM_BASE_URL` then `https://brain-be.dev.aivm.io` (override it to
point at a self-hosted brain). Pass
`tenant_id` for a tenant-scoped key (sent as `x-tenant-id`); omit for personal keys.

## Quickstart (sync)

```python
from aivm_brain import AivmBrain

with AivmBrain(api_key="ak_live_...") as brain:
    memory = brain.memories.add(title="Q3 budget", body="…", domain="finance")
    hits = brain.memories.search(query="budget", top_k=5)
    print(hits.answer, [h.id for h in hits.items])

    fetched = brain.memories.get(memory.id)
    updated = brain.memories.update(memory.id, body="…revised…")
    brain.memories.delete(memory.id)

    reply = brain.ask(question="What is the engineering budget?")
    print(reply.answer, reply.citations)
```

## Quickstart (async)

```python
import asyncio
from aivm_brain import AsyncAivmBrain

async def main():
    async with AsyncAivmBrain(api_key="ak_live_...") as brain:
        memory = await brain.memories.add(title="Q3 budget", body="…")
        hits = await brain.memories.search(query="budget")
        print(hits.answer)

asyncio.run(main())
```

## Pagination

`memories.list(...)` returns a `Page` with `.items` and `.next_cursor` (an opaque
string, or `None` on the last page). `memories.list_all(...)` transparently follows
the cursor and yields every memory:

```python
for memory in brain.memories.list_all(domain="finance"):
    print(memory.id, memory.title)

# async
async for memory in brain.memories.list_all():
    print(memory.id)
```

## Errors

Every non-2xx response raises a typed subclass of `AivmError`, each carrying
`.status` (int or `None`), `.code`, `.request_id`, and `.message` (array messages
are joined with `"; "`).

| status | code | class |
|---|---|---|
| 400, 422 | `invalid_request` | `ValidationError` |
| 401 | `unauthorized` | `AuthenticationError` |
| 402 | `payment_required` | `PaymentRequiredError` |
| 403 | `forbidden` | `PermissionError` |
| 404 | `not_found` | `NotFoundError` |
| 409 | `conflict` | `ConflictError` |
| 413 | `payload_too_large` | `PayloadTooLargeError` |
| 429 | `rate_limited` | `RateLimitError` |
| 5xx | `server_error` | `ServerError` |
| — (network) | `unreachable` | `APIConnectionError` |
| — (timeout) | `timeout` | `APITimeoutError` |

```python
from aivm_brain import AivmError, NotFoundError

try:
    brain.memories.get("does-not-exist")
except NotFoundError as exc:
    print(exc.status, exc.code, exc.request_id)
except AivmError as exc:      # catch-all base
    print(exc.message)
```

## Retries & timeout

Requests time out after `timeout` seconds (default 30). Only **idempotent reads**
(`memories.get`, `memories.list`, and the pages behind `list_all`) are retried, on
`429` / `5xx` / network / timeout, with jittered exponential backoff (base ~0.5s,
default `max_retries=2`); a `Retry-After` header on a 429 is honored. Writes
(`add` / `update` / `delete` / `search` / `ask`) are **never** auto-retried, so a
network blip can't double-write.

## Development

```bash
python -m venv .venv
# this box has no ensurepip; bootstrap pip:
curl -sSL https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py && ./.venv/bin/python /tmp/get-pip.py
./.venv/bin/python -m pip install -e '.[dev]'
./.venv/bin/python -m pytest -q
```

The test suite replays the golden fixtures in `../shared/fixtures/` (via `respx`)
so this SDK and the TypeScript SDK behave identically. Tests never hit the network.
