Metadata-Version: 2.4
Name: railtech-mme
Version: 0.1.0
Summary: Official Python SDK for MME (Modular Memory Engine) — tag-graph memory for LLMs, built by Rail Tech.
Project-URL: Homepage, https://railtech.io
Project-URL: Documentation, https://mme.railtech.io
Project-URL: Repository, https://github.com/gokulJinu01/railtech-mme-python
Project-URL: Issues, https://github.com/gokulJinu01/railtech-mme-python/issues
Author-email: "Rail Tech Inc." <gokuljinusclmda@gmail.com>
License: Apache-2.0
License-File: LICENSE
Keywords: agent,claude,llm,mcp,memory,mme,rag,railtech,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software 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: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
Requires-Dist: pytest>=7.4; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.1; extra == 'langchain'
Description-Content-Type: text/markdown

# railtech-mme

[![PyPI version](https://img.shields.io/pypi/v/railtech-mme.svg)](https://pypi.org/project/railtech-mme/)
[![CI](https://github.com/gokulJinu01/railtech-mme-python/actions/workflows/ci.yml/badge.svg)](https://github.com/gokulJinu01/railtech-mme-python/actions/workflows/ci.yml)
[![Python](https://img.shields.io/pypi/pyversions/railtech-mme.svg)](https://pypi.org/project/railtech-mme/)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)

Python SDK for **MME** — the [Modular Memory Engine](https://mme.railtech.io) by Rail Tech.

Tag-graph memory for LLMs. Bounded retrieval. Hard token budgets. Learns from use. No vector DB.

```bash
pip install railtech-mme
```

Requires Python 3.9+.

## Quick start

```python
from railtech_mme import MME

mme = MME(api_key="mme_live_...")          # get one at https://mme.railtech.io

mme.save("I prefer dark chocolate over milk.")
pack = mme.inject("What do I like to eat?")

for item in pack.items:
    print(f"- {item.title}: {item.excerpt}")

mme.feedback(pack_id=pack.pack_id, accepted=True)
```

That's the whole loop: **save** facts as they happen, **inject** them at prompt time, **feedback** to improve future packs.

## Async

```python
import asyncio
from railtech_mme import AsyncMME

async def main():
    async with AsyncMME(api_key="mme_live_...") as mme:
        await mme.save("hello")
        pack = await mme.inject("hello")
        print(pack.items[0].excerpt)

asyncio.run(main())
```

`AsyncMME` is a 1:1 mirror of `MME`. Same methods, same exceptions, same return types.

## LangChain

```bash
pip install "railtech-mme[langchain]"
```

```python
from railtech_mme import MME
from railtech_mme.langchain import MMESaveTool, MMEInjectTool

mme = MME(api_key="mme_live_...")
tools = [MMESaveTool(mme=mme), MMEInjectTool(mme=mme)]
# hand `tools` to your agent — see examples/langchain_agent.py for a runnable demo
```

The tools are LangChain `BaseTool` subclasses, so they drop into any agent that accepts tools (LangChain, LangGraph, AutoGen wrappers, etc.).

## API surface

| Method | What it does |
|---|---|
| `mme.save(content, *, tags=None, section=None, status=None, source=None)` | Persist a memory block. Returns `SaveResult`. |
| `mme.inject(prompt, *, token_budget=2048, limit=None, filters=None, debug=False)` | Retrieve a token-budgeted `Pack`. |
| `mme.feedback(*, pack_id, accepted, item_ids=None, tags=None)` | Mark a pack as useful or not — trains the edge graph. |
| `mme.recent(*, limit=20, section=None)` | List the most recent memories. |
| `mme.delete(memory_id)` | Remove a memory. |
| `mme.tags()` | List all tags known for the org. |

`AsyncMME` exposes the same surface with `async def` / `await`.

### Filters

Narrow a retrieval to a section, a status, or a time window:

```python
import datetime as dt
from railtech_mme import MME, InjectFilters

mme = MME()
pack = mme.inject(
    "what shipped this sprint?",
    filters=InjectFilters(
        section="work",
        since=dt.datetime(2026, 4, 1, tzinfo=dt.timezone.utc),
    ),
)
```

## Auth

Get your `mme_live_...` API key at **https://mme.railtech.io → API Key**.

The SDK reads it from the `RAILTECH_API_KEY` environment variable if you don't pass it explicitly:

```bash
export RAILTECH_API_KEY=mme_live_...
```

```python
from railtech_mme import MME
mme = MME()  # reads from env
```

The SDK exchanges your API key for a short-lived JWT on first use and caches it for the life of the client. Token refresh is automatic on 401.

## Errors

All SDK errors inherit from `MMEError`:

```python
import time
from railtech_mme import MME, MMEError, MMEAuthError, MMERateLimitError

mme = MME()
try:
    mme.save("...")
except MMERateLimitError as e:
    time.sleep(e.retry_after or 60)
except MMEAuthError:
    # API key is invalid or revoked — get a new one
    raise
except MMEError as e:
    print(e.status_code, e.response_body)
```

The full taxonomy: `MMEError` → `MMEAuthError`, `MMEClientError`, `MMERateLimitError`, `MMEServerError`, `MMETimeoutError`, `MMEBudgetExceeded`.

## Architecture — what MME does differently

MME does **not** use vector embeddings. It uses a **bounded tag-graph** that learns from pack accept/reject events. Retrieval is:

1. Extract seed tags from the prompt
2. Spread activation across the tag graph (bounded depth, beam width, decay)
3. Score memories (activation + recency + importance + status − diversity penalty)
4. Pack into a hard token budget greedily

Every pack respects the budget exactly. Every retrieval is explainable — seed tags, bounds, and activation paths come back in the response. Read the [whitepaper](https://mme.railtech.io) for the full picture.

## Examples

Runnable scripts in [`examples/`](examples/):

- [`basic.py`](examples/basic.py) — sync save / inject / feedback
- [`async_basic.py`](examples/async_basic.py) — async equivalent
- [`langchain_agent.py`](examples/langchain_agent.py) — minimal ReAct agent with both MME tools

Each one reads `RAILTECH_API_KEY` from the environment.

## Links

- **Dashboard & API key:** https://mme.railtech.io
- **Issues:** https://github.com/gokulJinu01/railtech-mme-python/issues
- **Changelog:** [CHANGELOG.md](CHANGELOG.md)

## License

Apache-2.0
