# How to Integrate memU with OpenRouter for Multi-Provider LLM Access

> Integrate memU with OpenRouter to access multiple LLM providers. Configure an OpenRouter profile in MemoryService for seamless chat and embedding operations via HTTPLLMClient and OpenRouterLLMBackend.

- Repository: [NevaMind AI/memU](https://github.com/nevamind-ai/memu)
- Tags: how-to-guide
- Published: 2026-02-19

---

**You integrate memU with OpenRouter by declaring an OpenRouter profile in `MemoryService` with `provider: "openrouter"`, which automatically routes requests through `HTTPLLMClient` using the `OpenRouterLLMBackend` for chat and embedding operations.**

The NevaMind-AI/memU framework abstracts LLM provider logic into modular backend classes, allowing seamless switching between providers without changing application code. To access multiple LLM providers through OpenRouter's unified API, you configure a profile that specifies OpenRouter-specific parameters, and memU handles the rest—from request formatting to response parsing—according to the source code in [`src/memu/llm/backends/openrouter.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/llm/backends/openrouter.py).

## Architecture Overview

memU isolates provider-specific logic in dedicated backend classes while exposing a uniform interface through `MemoryService`. When you integrate memU with OpenRouter, the framework utilizes three core components working in concert.

- **`OpenRouterLLMBackend`** – Located in [`src/memu/llm/backends/openrouter.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/llm/backends/openrouter.py), this class inherits from the generic `LLMBackend` and constructs OpenAI-compatible request payloads for chat, vision, and summary endpoints.
- **`_OpenRouterEmbeddingBackend`** – Implemented in [`src/memu/llm/http_client.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/llm/http_client.py) (lines 52-63), this class handles embedding requests using OpenRouter's OpenAI-compatible embedding interface.
- **`HTTPLLMClient`** – Defined in [`src/memu/llm/http_client.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/llm/http_client.py), this generic async HTTP client delegates to the appropriate backend based on the `provider` string in your profile, routing calls to OpenRouter's `/api/v1/chat/completions` or `/api/v1/embeddings` endpoints.

## Step-by-Step Integration Guide

### Configure the OpenRouter Profile

In `MemoryService`, pass an `llm_profiles` dictionary containing your OpenRouter configuration. The `provider` field must be set to `"openrouter"` to trigger backend selection.

Required parameters include:
- `base_url`: `"https://openrouter.ai"`
- `api_key`: Your OpenRouter API key
- `chat_model`: The target model (e.g., `"anthropic/claude-3.5-sonnet"`)
- `embed_model`: Optional embedding model (e.g., `"openai/text-embedding-3-small"`)

### Initialize MemoryService

When you instantiate `MemoryService` from [`src/memu/app/service.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/app/service.py), it lazily creates an `HTTPLLMClient` for each profile. The client reads the `provider` value and loads `OpenRouterLLMBackend` for chat operations and `_OpenRouterEmbeddingBackend` for embeddings.

### Execute LLM Operations

Once initialized, use `service.llm_client` to execute async operations like `summarize()`, `chat()`, or `embed()`. The client automatically invokes the backend's `build_*_payload` methods to create OpenAI-compatible JSON, sends the HTTP request, and uses `parse_*_response` methods to extract results.

## Code Examples

### Basic Setup and Chat Completion

Create a service instance with an OpenRouter profile and generate a summary:

```python
from memu.app import MemoryService
import os

service = MemoryService(
    llm_profiles={
        "default": {
            "provider": "openrouter",          # ← selects OpenRouter backend

            "client_backend": "httpx",         # uses HTTPLLMClient

            "base_url": "https://openrouter.ai",
            "api_key": os.getenv("OPENROUTER_API_KEY"),
            "chat_model": "anthropic/claude-3.5-sonnet",
            "embed_model": "openai/text-embedding-3-small",
        },
    },
)

# Summarize text asynchronously

summary, raw = await service.llm_client.summarize(
    "Explain the benefits of using OpenRouter with memU.",
    max_tokens=150,
)
print(summary)

```

### Generating Embeddings

Generate vector embeddings through OpenRouter's embedding endpoint:

```python
embeddings, raw = await service.llm_client.embed(
    ["memU", "OpenRouter", "LLM integration"]
)
print(embeddings[0][:5])   # first 5 dimensions of the first vector

```

### Full Memory Workflow

Process conversation files and persist categorized memory to markdown, as demonstrated in [`examples/example_4_openrouter_memory.py`](https://github.com/NevaMind-AI/memU/blob/main/examples/example_4_openrouter_memory.py):

```python
import asyncio, os
from memu.app import MemoryService

async def generate_memory_md(categories, output_dir):
    os.makedirs(output_dir, exist_ok=True)
    for cat in categories:
        name = cat.get("name", "unknown")
        summary = cat.get("summary", "")
        path = os.path.join(output_dir, f"{name}.md")
        with open(path, "w", encoding="utf-8") as f:
            f.write(summary.replace("<content>", "").replace("</content>", "").strip()
                    or "*No content available*")

async def main():
    api_key = os.getenv("OPENROUTER_API_KEY")
    service = MemoryService(
        llm_profiles={
            "default": {
                "provider": "openrouter",
                "client_backend": "httpx",
                "base_url": "https://openrouter.ai",
                "api_key": api_key,
                "chat_model": "anthropic/claude-3.5-sonnet",
                "embed_model": "openai/text-embedding-3-small",
            },
        },
    )
    conv_files = [
        "examples/resources/conversations/conv1.json",
        "examples/resources/conversations/conv2.json",
        "examples/resources/conversations/conv3.json",
    ]
    categories = []
    for f in conv_files:
        if os.path.exists(f):
            result = await service.memorize(resource_url=f, modality="conversation")
            categories = result.get("categories", [])
    await generate_memory_md(categories, "examples/output/openrouter_example")
    print("Memory categories written to examples/output/openrouter_example/")

if __name__ == "__main__":
    asyncio.run(main())

```

## Key Implementation Details

### Backend Selection Logic

Inside `HTTPLLMClient.__init__`, the `_load_backend` and `_load_embedding_backend` methods map the `provider` string to concrete classes. When `provider` equals `"openrouter"`, the client instantiates `OpenRouterLLMBackend` for chat/vision/summary tasks and `_OpenRouterEmbeddingBackend` for embedding requests. This mapping occurs in [`src/memu/llm/http_client.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/llm/http_client.py).

### Request Payload Construction

For every LLM operation, `HTTPLLMClient` delegates payload construction to the active backend. The `OpenRouterLLMBackend.build_chat_payload` and `build_summary_payload` methods generate OpenAI-compatible JSON containing `model`, `messages`, and `temperature` fields. OpenRouter accepts these payloads at its `/api/v1/chat/completions` endpoint. After receiving the HTTP response, the client passes the JSON to `parse_chat_response` or `parse_summary_response` to extract generated text or embedding vectors.

## Summary

- **Profile-based configuration** – Set `provider: "openrouter"` in your `MemoryService` profile to enable OpenRouter integration.
- **Automatic backend selection** – `HTTPLLMClient` automatically loads `OpenRouterLLMBackend` and `_OpenRouterEmbeddingBackend` based on the provider name.
- **OpenAI-compatible protocol** – The backend constructs standard OpenAI request formats that OpenRouter consumes, enabling access to multiple underlying LLM providers through a single interface.
- **Async workflow support** – All operations including `memorize`, `summarize`, and `embed` are fully async and compatible with OpenRouter's REST API.

## Frequently Asked Questions

### Do I need to modify the backend code to add new OpenRouter models?

No. The `OpenRouterLLMBackend` in [`src/memu/llm/backends/openrouter.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/llm/backends/openrouter.py) uses the model string you provide in the profile (e.g., `"anthropic/claude-3.5-sonnet"`). Simply update the `chat_model` or `embed_model` field in your profile to switch models; no code changes are required.

### Can I use different embedding and chat models from different providers?

Yes. OpenRouter aggregates multiple providers, so you can specify `chat_model: "anthropic/claude-3.5-sonnet"` and `embed_model: "openai/text-embedding-3-small"` in the same profile. The `_OpenRouterEmbeddingBackend` and `OpenRouterLLMBackend` handle the respective API calls independently.

### How does `MemoryService` handle API errors from OpenRouter?

The `HTTPLLMClient` in [`src/memu/llm/http_client.py`](https://github.com/NevaMind-AI/memU/blob/main/src/memu/llm/http_client.py) manages HTTP-level errors and response parsing. If OpenRouter returns an error (e.g., rate limiting or invalid model), the client propagates the HTTP exception or parsing error through the standard async error handling mechanism, allowing you to catch and handle failures in your application logic.

### Is it possible to use multiple LLM providers simultaneously in one application?

Yes. You can define multiple profiles in the `llm_profiles` dictionary passed to `MemoryService`, each with different `provider` values (e.g., one for OpenRouter, one for native OpenAI). `MemoryService` lazily creates separate `HTTPLLMClient` instances for each profile, enabling you to route specific operations to different providers within the same application.