# mubit-sdk

Canonical Python SDK for MuBit.

## Install

```bash
pip install mubit-sdk
```

## Three Layers

The SDK offers three integration depths. Start at the top and drop down only when you need more control.

| Layer | Module | What it does | When to use |
| --- | --- | --- | --- |
| **Learn** | `mubit.learn` | Auto-ingest all LLM interactions + auto-inject relevant lessons before each call + auto-reflect on run end | Zero-config closed-loop — agents learn from past runs with one line of setup |
| **Helpers** | `Client` | 17 explicit methods for memory write/read, context assembly, reflection, multi-agent coordination | Fine-grained control over what gets remembered, queried, and when reflection triggers |
| **Raw** | `client.auth.*`, `client.core.*`, `client.control.*` | Direct 1:1 mappings to every gRPC/HTTP endpoint | Wire debugging, async ingest job polling, compatibility routes |

## Layer 1: Learn (Closed-Loop Memory)

`mubit.learn` extends auto-capture with automatic lesson injection — before each LLM call, relevant lessons are retrieved from MuBit and injected into the system message. After the run, reflection is triggered automatically.

```python
import mubit.learn

mubit.learn.init(api_key="mbt_...", agent_id="my-agent")

# All OpenAI/Anthropic/LiteLLM calls now auto-ingest traces
# AND auto-inject relevant lessons before each call.

import openai
client = openai.OpenAI()
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Fix the auth bug"}],
)
# Lessons about auth bugs are automatically prepended to the system message.
# The interaction is automatically ingested into MuBit.
```

Use the `@run` decorator for scoped runs with automatic reflection:

```python
@mubit.learn.run(agent_id="planner", auto_reflect=True)
def plan_task(task):
    client = openai.OpenAI()
    return client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": task}],
    ).choices[0].message.content
```

Key options: `inject_lessons`, `injection_position` ("system", "prepend", "last_system"), `max_token_budget`, `auto_reflect`, `reflect_after_n_calls`, `cache_ttl_seconds`, `fail_open`.

## Layer 2: Client Helpers

### Quickstart

```python
import os
from mubit import Client

client = Client(
    transport=os.getenv("MUBIT_TRANSPORT", "auto"),
    run_id="sdk-python-demo",
    api_key=os.environ["MUBIT_API_KEY"],
)

client.remember(
    session_id="sdk-python-demo",
    content="If the replay queue stalls, checkpoint before replaying recovery.",
    intent="lesson",
    lesson_type="success",
    lesson_scope="session",
)

answer = client.recall(
    session_id="sdk-python-demo",
    query="What should I do before replaying recovery?",
    entry_types=["lesson", "rule"],
)
print(answer.get("final_answer"))
```

### Methods by Use Case

| Use case | Methods | What they do |
| --- | --- | --- |
| Basic memory | `remember`, `recall` | Ingest content with intent classification; semantic query with evidence scoring |
| Prompt context | `get_context` | Token-budgeted, pre-assembled context block for LLM prompt injection (rules → lessons → facts) |
| Exact artifacts | `archive`, `dereference` | Bit-exact storage with stable reference IDs; retrieval without semantic search |
| Run lifecycle | `checkpoint`, `reflect`, `record_outcome` | Durable pre-compaction state; LLM-powered lesson extraction; reinforcement feedback |
| Multi-agent | `register_agent`, `list_agents`, `handoff`, `feedback` | Scoped read/write access per agent; task transfer between agents |
| Diagnostics | `memory_health`, `diagnose`, `surface_strategies`, `forget` | Staleness metrics; contextual error debugging; lesson clustering; deletion |

Helper APIs use `session_id` as the ergonomic alias for `run_id`.

### The Learning Loop

```
1. remember()         → ingest facts, traces, lessons into MuBit
2. reflect()          → LLM extracts lessons from run evidence
                         (auto-promotes recurring lessons: run → session → global)
3. get_context()      → retrieve relevant lessons for the next LLM call
4. record_outcome()   → reinforce what worked, adjust confidence scores
```

With `mubit.learn`, steps 1–3 happen automatically. With helpers, you orchestrate them yourself.

### MAS / Multi-Agent Example

```python
client.register_agent(
    session_id="sdk-python-demo",
    agent_id="planner",
    role="planner",
    read_scopes=["rule", "lesson", "fact"],
    write_scopes=["lesson", "trace"],
    shared_memory_lanes=["knowledge", "history"],
)

client.checkpoint(
    session_id="sdk-python-demo",
    label="pre-compaction-1",
    context_snapshot="Planner narrowed the failure to token refresh ordering.",
    metadata={"window": 1},
)

strategies = client.surface_strategies(
    session_id="sdk-python-demo",
    lesson_types=["success", "failure"],
    max_strategies=3,
)
print(len(strategies.get("strategies", [])))
```

### Exact References

```python
archived = client.archive(
    session_id="sdk-python-demo",
    artifact_kind="patch_fragment",
    content="--- a/query.py\n+++ b/query.py\n@@ ...",
    labels=["django", "retry"],
    family="patch-repair",
)

exact = client.dereference(
    session_id="sdk-python-demo",
    reference_id=archived["reference_id"],
)
print(exact.get("evidence", {}).get("content"))
```

## Layer 3: Raw Domains

Low-level 1:1 mappings to every API endpoint. Use `client.control.*` for async ingest job polling, `client.core.*` for direct search, `client.auth.*` for user/key management.

```python
# Async ingest with job polling
result = client.control.ingest({"run_id": "...", "items": [...]})
job = client.control.get_ingest_job({"run_id": "...", "job_id": result["job_id"]})
```

## Auto-Capture (`mubit.auto`)

Sits between Learn and Helpers. Monkey-patches LLM clients to auto-capture inputs/outputs, but does NOT retrieve or inject lessons. Use when you want passive trace telemetry.

```python
from mubit.auto import instrument, observe

instrument()

@observe(name="repair-attempt")
def run_attempt():
    ...  # LLM calls auto-captured
```

