Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 73 additions & 48 deletions packages/powermem-langchain/README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
# powermem-langchain

This package is the LangChain middleware exercise for the VLDB 2026 summer
school branch. It provides a package skeleton, a no-op middleware scaffold,
contract tests, and a runnable OpenAI example. It does not provide a complete
PowerMem middleware implementation.
`powermem-langchain` adds PowerMem long-term memory to LangChain v1 agents by
using agent middleware hooks. It retrieves relevant memories before an agent
run, adds them to model-visible system context, and optionally saves the final
user/assistant interaction after the run.

The goal is to integrate PowerMem as a long-term memory layer for LangChain v1
agents. The provided scaffold is intentionally small; you may adjust it as your
implementation requires, but the memory retrieval, context injection, and
write-back behavior should be implemented through LangChain middleware hooks.
This is useful beyond a single `create_agent` example: LangChain middleware is
the extension point for controlling agent execution, and related projects such
as Deep Agents also compose capabilities through middleware.
## Installation

## Task
From the PowerMem repository:

Implement:

```python
from powermem_langchain import PowerMemMiddleware
```bash
uv pip install --editable . --editable packages/powermem-langchain
```

The middleware should be usable with `create_agent`:
Python 3.11 or newer, PowerMem 1.1.7 or newer, and LangChain v1 are required.

## Usage

```python
from langchain.agents import create_agent
Expand All @@ -42,16 +36,59 @@ agent = create_agent(
)
],
)

result = agent.invoke(
{"messages": [{"role": "user", "content": "How should you answer me?"}]}
)
```

At minimum, the implementation should:
`user_id` is required and is used explicitly for every PowerMem search and
write. The middleware never derives a business user identity from LangChain
runtime state, thread IDs, or environment variables.

- Search PowerMem with the latest user message before the model call.
- Add relevant memories to the model-visible context.
- Save the user and assistant interaction to PowerMem when enabled.
- Skip write-back when `save_interactions=False`.
- Use the explicit `user_id` constructor argument.
- Keep the agent usable when PowerMem search fails.
## Lifecycle behavior

For each agent invocation, the middleware:

1. Finds the latest user message in agent state.
2. Calls `memory.search(query, user_id=user_id, limit=search_limit)` once.
3. Stores a formatted memory context in middleware state.
4. Adds that context to the existing system message for every model call in
the agent loop, without modifying conversation history.
5. After the agent finishes, calls `memory.add(...)` with the latest user
message and the final assistant message.

Retrieved text is labeled as untrusted background information rather than
instructions. The model is told to use only memories relevant to the current
request.

Set `save_interactions=False` to make the integration retrieval-only:

```python
PowerMemMiddleware(
memory=memory,
user_id="user123",
save_interactions=False,
)
```

PowerMem search and write failures are fail-open by default. The middleware
logs a warning and lets the agent return its response. A failed or empty search
also clears the per-run memory context so that stale context cannot be reused.

## Async agents

The middleware implements both synchronous and asynchronous LangChain hooks:

```python
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Use my saved preferences."}]}
)
```

A synchronous PowerMem `Memory` instance works with both `invoke` and
`ainvoke`; synchronous PowerMem work is moved to a worker thread on the async
path. A PowerMem `AsyncMemory`-compatible object can be used with `ainvoke`.

## Tests

Expand All @@ -65,21 +102,20 @@ uv run --no-project \
pytest packages/powermem-langchain/tests -q
```

The tests use a local SQLite PowerMem instance with the noop LLM provider and
mock embedder. They do not require API keys or OceanBase.
The tests use local SQLite PowerMem storage, the noop LLM provider, and a mock
embedder. They do not require API keys or OceanBase.

The tests are a baseline, not a complete design specification. If your solution
adds state fields or edge-case handling, add focused tests for those choices.
If the dependencies are already installed in a repository virtual environment:

## Example
```bash
.venv/bin/python -m pytest packages/powermem-langchain/tests -q
```

The OpenAI example is an end-to-end check for the expected flow:
## OpenAI example

1. Create a PowerMem instance.
2. Seed one memory for the demo user.
3. Create a LangChain agent with `PowerMemMiddleware`.
4. Invoke an OpenAI chat model.
5. Print memory search results before and after the agent run.
The example at `examples/openai_agent.py` performs an end-to-end check by
seeding a memory, invoking a LangChain agent, and printing PowerMem search
results before and after the invocation.

Minimal environment:

Expand All @@ -92,16 +128,7 @@ export DATABASE_PROVIDER=sqlite
export SQLITE_PATH="./data/powermem_langchain_demo.db"
```

Optional demo settings use the `POWERMEM_LANGCHAIN_` prefix:

- `POWERMEM_LANGCHAIN_OPENAI_MODEL`
- `POWERMEM_LANGCHAIN_TEMPERATURE`
- `POWERMEM_LANGCHAIN_USER_ID`
- `POWERMEM_LANGCHAIN_SEARCH_LIMIT`
- `POWERMEM_LANGCHAIN_PROMPT`
- `POWERMEM_LANGCHAIN_SEED_MEMORY`

Run:
Run it from the repository root:

```bash
uv run --no-project \
Expand All @@ -112,6 +139,4 @@ uv run --no-project \
--user-id summer-school-demo
```

With the placeholder middleware, the command can run but will not show the expected
memory injection or write-back behavior. After implementation, the output should
show seeded memories before the agent call and updated memories afterward.
The OpenAI example is a manual integration check and requires a valid API key.
8 changes: 4 additions & 4 deletions packages/powermem-langchain/examples/openai_agent.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Run a PowerMem-backed LangChain agent with an OpenAI chat model.

This example is intentionally written as an end-to-end CLI check. Once
PowerMemMiddleware is implemented and the environment is configured, it should
seed PowerMem, invoke a LangChain agent backed by OpenAI, and print enough
information to verify memory retrieval and write-back behavior.
This example is intentionally written as an end-to-end CLI check. Once the
environment is configured, it seeds PowerMem, invokes a LangChain agent backed
by OpenAI, and prints enough information to verify memory retrieval and
write-back behavior.
"""

from __future__ import annotations
Expand Down
4 changes: 2 additions & 2 deletions packages/powermem-langchain/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ authors = [
requires-python = ">=3.11"
dependencies = [
"powermem>=1.1.7",
"langchain>=1.1.0",
"langchain-core>=1.1.0",
"langchain>=1.1.0,<2.0.0",
"langchain-core>=1.1.0,<2.0.0",
]

[project.optional-dependencies]
Expand Down
Loading
Loading