Metadata-Version: 2.4
Name: powr-memory-sdk
Version: 1.0.2
Summary: Universal AI Agent Memory SDK: Embedded Local SQLite STM + Central Distributed MCP Server in one unified API.
Author: Lawazia AI Team
License-Expression: MIT
Keywords: ai,agent,memory,stm,mcp,model-context-protocol,working-memory,context,openai,langchain
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: pydantic<3.0,>=2.0
Requires-Dist: mcp>=1.0.0
Requires-Dist: httpx>=0.27
Requires-Dist: PyJWT>=2.8
Requires-Dist: python-dotenv>=1.0.0
Provides-Extra: openai
Requires-Dist: openai>=1.0.0; extra == "openai"
Provides-Extra: server
Requires-Dist: uvicorn>=0.20.0; extra == "server"
Requires-Dist: fastapi>=0.100.0; extra == "server"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Provides-Extra: all
Requires-Dist: openai>=1.0.0; extra == "all"
Requires-Dist: uvicorn>=0.20.0; extra == "all"
Requires-Dist: fastapi>=0.100.0; extra == "all"
Requires-Dist: langchain-core>=0.1.0; extra == "all"
Requires-Dist: pytest>=7.0; extra == "all"

# 🧠 powr-memory-sdk

**Universal AI Agent Memory SDK** — Embedded Local SQLite + Central Distributed MCP Server, one unified API.

Give your AI agents persistent memory across turns, users, and services — in **4 lines of code**.

[![PyPI version](https://img.shields.io/pypi/v/powr-memory-sdk)](https://pypi.org/project/powr-memory-sdk/)
[![Python](https://img.shields.io/pypi/pyversions/powr-memory-sdk)](https://pypi.org/project/powr-memory-sdk/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

---

## ⚡ Quickstart

```bash
pip install powr-memory-sdk
```

```python
from memory_sdk import mem

session = mem("user_123")
session.remember("name", "Gunit")
session.add_user("Mujhe Python sikhna hai")
messages = session.to_messages("You are a helpful assistant.")
# → Pass `messages` directly to any LLM (OpenAI, Groq, Anthropic, Ollama)
```

**That's it.** No server to start, no config file, no database setup.

---

## 🏗️ Two Modes — One API

| | **Local Mode** | **Central Mode** |
|---|---|---|
| **Storage** | Embedded SQLite (your machine) | Deployed MCP Server (shared) |
| **Best for** | Dev, CLI tools, single agent | Production, multi-agent, microservices |
| **Setup** | Zero — just a file path | Zero — uses deployed server by default |
| **Latency** | Sub-millisecond | ~5–20 ms network |
| **API** | ✅ Identical | ✅ Identical |

> **The API is 100% identical across both modes** — switch anytime with one line.

---

## 📦 Installation

```bash
# Basic (local SQLite memory only)
pip install powr-memory-sdk

# With OpenAI support
pip install "powr-memory-sdk[openai]"

# With LangChain support
pip install "powr-memory-sdk[openai,langchain-core]"

# Everything
pip install "powr-memory-sdk[all]"
```

---

## 🚀 Usage

### Zero-Config — `mem` Singleton (Recommended)

```python
from memory_sdk import mem

# Uses local SQLite automatically — safe, zero-config, keeps data on your machine
session = mem("user_123")
session.remember("name", "Ali")
session.remember({"city": "Lahore", "diet": "Vegetarian"})

session.add_user("Dinner mein kya khaaon?")
session.add_assistant("Lahore mein vegetarian ke liye Dal Makhani best hai!")

messages = session.to_messages("You are a helpful assistant.")
# → Ready to pass to any LLM API
```

### Switch Mode — One Line

```python
from memory_sdk import mem

# Custom local SQLite database file
mem.configure(mode="local", db_path="app.db")

# Central MCP server (for distributed / multi-agent architectures)
mem.configure(mode="central", url="http://localhost:8765/mcp")

# Back to default local storage
mem.configure(mode="local")
```

---

## 🔌 Integration Examples

### OpenAI

```python
from openai import OpenAI
from memory_sdk import mem

client = OpenAI()

def chat(user_id: str, message: str) -> str:
    session = mem(user_id)
    session.add_user(message)

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=session.to_messages("You are a helpful assistant.")
    )
    reply = response.choices[0].message.content
    session.add_assistant(reply)
    return reply
```

### AutoOpenAI — 1-Line Drop-in Replacement

```python
# Change just this one line in your existing OpenAI project:
# from openai import OpenAI
from memory_sdk import AutoOpenAI as OpenAI

client = OpenAI(session_id="user_123")  # Memory is fully automatic

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Mera naam Ali hai"}]
)
# SDK automatically saves the message and injects memory on next call
```

### FastAPI

```python
from fastapi import FastAPI
from memory_sdk import mem

app = FastAPI()

@app.on_event("startup")
def setup():
    mem.configure(mode="local", db_path="prod.db", window_size=10)

@app.post("/chat/{user_id}")
async def chat(user_id: str, message: str):
    session = mem(user_id)
    session.add_user(message)
    messages = session.to_messages("You are a helpful assistant.")
    # ... your LLM call here
    reply = "Hello!"
    session.add_assistant(reply)
    return {"reply": reply}
```

### Flask

```python
from flask import Flask, request, jsonify
from memory_sdk import mem

app = Flask(__name__)
mem.configure(mode="local", db_path="app.db")

@app.route("/chat/<user_id>", methods=["POST"])
def chat(user_id):
    message = request.json["message"]
    session = mem(user_id)
    session.add_user(message)
    messages = session.to_messages("You are a helpful assistant.")
    return jsonify({"messages": messages})
```

### LangChain Agent

```python
from langchain_openai import ChatOpenAI
from memory_sdk import mem

llm = ChatOpenAI(model="gpt-4o-mini")

def chat(user_id: str, message: str) -> str:
    session = mem(user_id)
    agent = llm.bind_tools(session.get_langchain_tools())

    session.add_user(message)
    response = agent.invoke(session.to_messages("You are helpful."))
    session.add_assistant(response.content)
    return response.content
```

### Telegram / WhatsApp Bot

```python
from memory_sdk import mem

def handle_message(chat_id: str, message: str) -> str:
    session = mem(chat_id)          # chat_id = unique user identifier
    session.add_user(message)
    messages = session.to_messages("You are a helpful assistant.")
    reply = your_llm(messages)      # your LLM call
    session.add_assistant(reply)
    return reply
```

---

## 📖 Full API Reference

### `mem` — Zero-Config Singleton

```python
from memory_sdk import mem

mem.configure(mode, **kwargs)   # Configure once at startup
session = mem("user_id")        # Get/create a session (callable)
session = mem.session("user_id") # Same — explicit style
mem.close()                     # Release resources
```

### Session Methods

```python
session = mem("user_123")

# ── Save facts ────────────────────────────────────────────────────
session.remember("key", "value")              # single fact
session.remember({"k1": "v1", "k2": "v2"})   # multiple facts at once

# ── Read facts ────────────────────────────────────────────────────
session.recall("key")          # one fact → value or None
session.recall()               # all facts → dict
session.profile()              # all facts → dict (alias)

# ── Conversation ──────────────────────────────────────────────────
session.add_user("...")        # record user message
session.add_assistant("...")   # record assistant reply
session.add_system("...")      # record system message
session.history(limit=10)      # recent messages list

# ── LLM-ready output ──────────────────────────────────────────────
session.to_messages(
    system_prompt="You are helpful.",  # optional base prompt
    include_memory=True,               # inject facts into system message
)
# → [{"role": "system", "content": "..."}, {"role": "user", ...}, ...]

# ── Cleanup ───────────────────────────────────────────────────────
session.clear(keep_state=True)  # clear messages, keep facts
session.forget()                # wipe everything (messages + facts)
```

### `mem.configure()` Options

```python
mem.configure(
    mode="central",          # "central" (default) or "local"
    
    # Central mode options:
    url="http://...",         # MCP server URL (or set MCP_SERVER_URL env var)
    project="my_app",         # namespace for session isolation
    
    # Local mode options:
    db_path="app.db",         # SQLite file path (or ":memory:" for transient)
    window_size=10,           # max recent messages to keep in context
    auto_summarize=False,     # LLM-based rolling summarization
)
```

---

## ⚙️ Environment Variables

Set these instead of hardcoding in code:

```bash
# Central server URL (overrides built-in default)
MCP_SERVER_URL=https://your-server.run.app/mcp

# Alternate env var name
MEMORY_SERVER_URL=https://your-server.run.app/mcp

# Required by central mode. JWT-protected servers must use the same secret
# (at least 32 bytes); JWT_ISSUER and JWT_AUDIENCE default to "memory-store".
JWT_SECRET=<shared-secret-at-least-32-bytes>

# Force a mode (optional)
MEMORY_MODE=local

# Local DB path (optional)
MEMORY_DB_PATH=./data/agent.db

# Model for auto-summarization
MEMORY_SUMMARIZER_MODEL=gpt-4o-mini

# OpenAI key (needed only for auto_summarize=True)
OPENAI_API_KEY=sk-...
```

---

## 🏃 Running Your Own Central Server

```bash
# Install server extras
pip install "powr-memory-sdk[server]"

# Start the MCP memory server
memory-server --port 8765

# Point your app to it
export MCP_SERVER_URL=http://localhost:8765/mcp
```

The bundled `memory-server` is a development server and does not enforce JWT
authentication. For a JWT-protected deployment, use the JWT-enabled memory
store and configure the same `JWT_SECRET` on the server and client.

Or with Docker:

```bash
docker build -t memory-server .
docker run -p 8765:8765 -e PORT=8765 memory-server
```

Then in your code:

```python
from memory_sdk import mem
mem.configure(mode="central", url="http://localhost:8765/mcp")
```

---

## 🌐 Central MCP Server — Live Deployed

A central memory server is already live and ready to use:

```
URL     : https://memory-module-813340036970.asia-south1.run.app/mcp
Region  : asia-south1 (Mumbai)
Protocol: Streamable HTTP (MCP)
```

This is the **default** when you use `mode="central"` without specifying a `url`. All session data is namespaced by `project` so different apps don't interfere.

---

## 🧪 Testing

```bash
# Run all tests
pytest tests/ -v

# With coverage
pytest tests/ --cov=memory_sdk --cov-report=term-missing
```

**Testing tip — use in-memory SQLite:**

```python
# conftest.py
import pytest
from memory_sdk import mem

@pytest.fixture(autouse=True)
def isolate_memory():
    mem.configure(mode="local", db_path=":memory:")
    yield
    mem.close()
```

---

## 🗂️ Project Structure

```
memory-sdk/
├── src/memory_sdk/
│   ├── __init__.py          ← mem singleton + all exports
│   ├── factory.py           ← Memory() factory router
│   ├── base.py              ← BaseMemory / BaseSession protocols
│   ├── local/               ← Embedded SQLite engine
│   │   ├── engine.py
│   │   ├── session.py       ← Full session + sliding window + summarizer
│   │   ├── sqlite_storage.py
│   │   ├── tools.py         ← OpenAI function-call tool schemas
│   │   └── langchain.py     ← LangChain tool wrappers
│   ├── central/             ← Distributed MCP engine
│   │   ├── engine.py
│   │   ├── session.py
│   │   └── client.py        ← Async MCP HTTP client
│   ├── server/
│   │   └── server.py        ← Standalone MCP memory server
│   └── wrappers/
│       └── openai_wrapper.py ← AutoOpenAI drop-in replacement
├── examples/
│   ├── 01_local_mode_quickstart.py
│   ├── 02_central_mode_quickstart.py
│   ├── 03_switching_modes.py
│   └── 04_openai_autopilot.py
├── tests/
└── pyproject.toml
```

---

## 📋 Compatibility

| Framework | Support |
|---|---|
| Python | 3.10+ |
| OpenAI SDK | ✅ |
| LangChain | ✅ |
| FastAPI | ✅ |
| Flask | ✅ |
| Django | ✅ |
| Groq | ✅ (same OpenAI API) |
| Anthropic | ✅ (via `to_messages()`) |
| Ollama | ✅ (via `to_messages()`) |
| Any MCP client | ✅ |

---


