Metadata-Version: 2.4
Name: mubit-langchain
Version: 0.6.0
Summary: LangChain chat-history adapter backed by MuBit memory engine
Author: Mubit AI
License-Expression: Apache-2.0
Keywords: ai-agent,langchain,memory,mubit
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Requires-Dist: mubit-integration-base<0.7,>=0.6.0
Requires-Dist: mubit-sdk<1.0,>=0.9.0
Provides-Extra: dev
Requires-Dist: langchain-core<2,>=1.2; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Provides-Extra: langchain
Requires-Dist: langchain-core<2,>=1.2; extra == 'langchain'
Description-Content-Type: text/markdown

# `mubit-langchain`

LangChain chat-history adapter backed by [MuBit](https://mubit.ai).

The primary surface is `MubitChatMessageHistory`, a current-contract
`langchain_core.chat_history.BaseChatMessageHistory` implementation that persists
a conversation through MuBit's control plane. `MubitLessonRetriever` surfaces
MuBit's accumulated lessons as `Document` objects, and `MubitCallbackHandler`
closes the v0.7.0 reinforcement loop by crediting the recalled lessons.

Targets `langchain-core>=1.2,<2` (verified against 1.4.x).

## Install

```bash
pip install mubit-langchain[langchain]
```

## Chat history with `RunnableWithMessageHistory`

`MubitChatMessageHistory` implements the current contract — `add_messages` /
`aadd_messages`, the `messages` property / `aget_messages`, and `clear` /
`aclear`. Wire it into `RunnableWithMessageHistory` via `get_session_history`:

```python
from functools import partial

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory

from mubit_langchain import get_session_history

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant."),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{input}"),
])
runnable = prompt | ChatOpenAI(model="gpt-4o-mini")

chain = RunnableWithMessageHistory(
    runnable,
    # Bind connection details; the factory receives the session_id per call.
    partial(get_session_history, api_key="mbt_...", endpoint="http://127.0.0.1:3000"),
    input_messages_key="input",
    history_messages_key="history",
)

chain.invoke(
    {"input": "Remember my name is Ada."},
    config={"configurable": {"session_id": "user-42"}},
)
```

Each turn is written with `remember` and replayed with `recall`, so history
spans the whole conversation (and earlier sessions sharing the run) without a
separate checkpointer.

### Direct usage

```python
from mubit_langchain import MubitChatMessageHistory
from langchain_core.messages import HumanMessage, AIMessage

history = MubitChatMessageHistory(session_id="chat-1", api_key="mbt_...")
history.add_messages([HumanMessage(content="hi"), AIMessage(content="hello!")])
msgs = history.messages          # sync; chronological list[BaseMessage]
msgs = await history.aget_messages()  # async
history.clear()
```

## `BaseChatMessageHistory` interface

| Method | MuBit mapping |
| --- | --- |
| `add_messages(messages)` / `aadd_messages` | `remember` — one entry per message |
| `messages` (property) / `aget_messages()` | `recall` — replayed in write order |
| `clear()` / `aclear()` | `delete_run` |

## Lessons + reinforcement loop (v0.7.0)

`MubitLessonRetriever` pulls recurring success/failure patterns into a chain's
context, and `MubitCallbackHandler` credits exactly those lessons when the chain
finishes — closing the recall → act → credit attribution loop.

```python
from mubit_langchain import MubitLessonRetriever, MubitCallbackHandler

retriever = MubitLessonRetriever(api_key="mbt_...", session_id="s1")
handler = MubitCallbackHandler(api_key="mbt_...", session_id="s1", retriever=retriever)

# Lessons surfaced by the retriever are credited automatically at chain end.
docs = retriever.get_relevant_documents("deploy strategy")        # sync
docs = await retriever.aget_relevant_documents("deploy strategy")  # truly async

result = chain.invoke({"input": ...}, config={"callbacks": [handler]})

# Manual attribution: credit the entries that grounded an answer.
from mubit_langchain import MubitControlClient, extract_entry_ids, extract_citations

client = MubitControlClient(api_key="mbt_...")
recall = client.recall(session_id="s1", query="deploy strategy")
handler.track_recall(recall, cited_only=True)   # capture grounding entries
handler.record_outcome("success", verified_in_production=True)
handler.record_step_outcome("plan", outcome="success", signal=0.7)

entry_ids = extract_entry_ids(recall)            # reference_ids of recalled evidence
citations = extract_citations(recall)            # 0-based grounding indices
```

`MubitCallbackHandler` exposes `record_outcome(entry_ids=..., verified_in_production=...)`
and `record_step_outcome(...)` on its public surface, in addition to the
`on_chain_end` / `on_chain_error` / `on_retriever_end` callback hooks.

## Legacy `BaseMemory` shim (deprecated)

`MubitMemory` and `MubitChatMemory` target the old
`langchain_core.memory.BaseMemory`, which was deprecated in 0.3.1 and **removed**
in langchain-core 1.0+. They are retained only for backwards compatibility,
behind a pydantic import fallback so the package always imports. **Do not use
them for new code** — use `MubitChatMessageHistory` above.

## Config

| Parameter | Default | Purpose |
| --- | --- | --- |
| `endpoint` | `http://127.0.0.1:3000` | MuBit HTTP endpoint |
| `api_key` | `""` | MuBit API key |
| `session_id` | `"default"` | Session/run scope (`run_id`) |
| `user_id` | `""` | Optional user scope |
| `agent_id` | `"langchain"` | Agent attribution |
| `recall_query` | `"conversation history"` | Query used to replay history |
| `limit` | `50` | Max messages fetched per read |
| `sdk_client` | `None` | Inject a pre-built `mubit.Client` (tests) |

## Development

```bash
cd integrations/python/mubit_langchain
python3 -m pytest tests/test_memory.py -v
```

## License

Apache-2.0
