Metadata-Version: 2.4
Name: palimo
Version: 0.1.0
Summary: Memory-as-a-service SDK — add persistent user memory to any LLM app in 5 lines
Project-URL: Homepage, https://github.com/maxpar1/palimo
Project-URL: Documentation, https://github.com/maxpar1/palimo#sdk-quickstart
Project-URL: Repository, https://github.com/maxpar1/palimo
Project-URL: Issues, https://github.com/maxpar1/palimo/issues
License-Expression: MIT
License-File: LICENSE
Keywords: agents,ai,context,llm,memory
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.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.10
Requires-Dist: requests>=2.28
Description-Content-Type: text/markdown

# palimo

Add persistent user memory to any LLM app in 5 lines. Zero ML dependencies on the client side.

Palimo is a memory-as-a-service that runs a fine-tuned memory LLM on your infrastructure. The SDK is a thin HTTP client — your app sends conversation turns, Palimo extracts and maintains memories, and returns packed context strings you inject into your system prompt.

## Install

```bash
pip install palimo
```

## Quick Start

```python
from palimo import MemoryClient

mem = MemoryClient(url="http://localhost:8000", api_key="ik_live_...", user_id="user-123")

# Before calling your LLM — get memory context
context = mem.enrich("What's my dog's name?")

# After your LLM responds — commit the turn for memory processing
mem.commit("What's my dog's name?", "Your dog's name is Max!")
```

That's it. `enrich` returns a context string you prepend to your system prompt. `commit` queues the turn for background memory extraction.

## Usage with OpenAI

```python
from openai import OpenAI
from palimo import MemoryClient

client = OpenAI()
mem = MemoryClient(url="http://localhost:8000", api_key="ik_live_...", user_id="user-123")

def chat(user_message, history=None):
    # 1. Get memory context
    context = mem.enrich(user_message, history)

    # 2. Build messages with memory injected
    messages = []
    if context:
        messages.append({"role": "system", "content": context})
    messages.append({"role": "system", "content": "You are a helpful assistant."})
    if history:
        messages.extend(history)
    messages.append({"role": "user", "content": user_message})

    # 3. Call OpenAI
    response = client.chat.completions.create(
        model="gpt-5.3",
        messages=messages,
    ).choices[0].message.content

    # 4. Commit turn for memory extraction
    mem.commit(user_message, response, history)

    return response
```

## Usage with Anthropic

```python
import anthropic
from palimo import MemoryClient

client = anthropic.Anthropic()
mem = MemoryClient(url="http://localhost:8000", api_key="ik_live_...", user_id="user-123")

def chat(user_message, history=None):
    context = mem.enrich(user_message, history)

    system = "You are a helpful assistant."
    if context:
        system = context + "\n\n" + system

    messages = []
    if history:
        messages.extend(history)
    messages.append({"role": "user", "content": user_message})

    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        system=system,
        messages=messages,
    ).content[0].text

    mem.commit(user_message, response, history)
    return response
```

## Usage with Local LLMs (Ollama, vLLM, llama.cpp)

Any OpenAI-compatible server works:

```python
from openai import OpenAI
from palimo import MemoryClient

# Point at your local server
client = OpenAI(base_url="http://localhost:11434/v1", api_key="unused")
mem = MemoryClient(url="http://localhost:8000", api_key="ik_live_...", user_id="user-123")

def chat(user_message, history=None):
    context = mem.enrich(user_message, history)

    messages = []
    if context:
        messages.append({"role": "system", "content": context})
    if history:
        messages.extend(history)
    messages.append({"role": "user", "content": user_message})

    response = client.chat.completions.create(
        model="llama3",
        messages=messages,
    ).choices[0].message.content

    mem.commit(user_message, response, history)
    return response
```

## One-Shot Convenience

If you don't need fine-grained control, `chat()` wraps enrich + LLM + commit:

```python
from palimo import MemoryClient

mem = MemoryClient(url="http://localhost:8000", api_key="ik_live_...", user_id="user-123")

response = mem.chat(
    messages=[{"role": "user", "content": "I just moved to Berlin"}],
    llm_fn=your_llm_function,  # (messages) -> str
)
```

## API Reference

### `MemoryClient(url, api_key, user_id)`

| Param | Description |
|-|-|
| `url` | Base URL of your Palimo instance |
| `api_key` | API key (`ik_live_...`) |
| `user_id` | User identifier, scoped to your customer account |

### `client.enrich(message, history=None) -> str`

Returns a memory context string to inject into your system prompt. Returns empty string if no memories exist yet.

### `client.commit(message, assistant_response, history=None) -> str`

Queues a conversation turn for memory extraction. Returns a `turn_id`. Processing happens asynchronously — memories are available on the next `enrich` call.

### `client.chat(messages, llm_fn) -> str`

Convenience method: enrich -> call your LLM -> commit, in one call. `llm_fn` is a callable that takes a list of message dicts and returns the assistant's response string.

## How It Works

1. **enrich** retrieves relevant memories via embedding similarity, packs them into a token-budgeted context string (max 2048 tokens), and returns it
2. You inject that context into your LLM call however you want
3. **commit** sends the turn to a background queue where a fine-tuned archivist LLM extracts durable facts (INSERT, UPDATE, SUPERSEDE, DELETE, etc.) into per-user storage
4. Next time you call **enrich**, the new memories are included

The memory LLM runs on your infrastructure. No data leaves your servers. No per-call API costs for memory extraction.
