Skip to content
Open
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
158 changes: 158 additions & 0 deletions src/oss/langchain/middleware/dependency-resolution.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
---
title: Dependency resolution
description: Configure and understand middleware dependencies, ordering, and deduplication
---

LangChain middleware can declare other middleware they depend on. Dependency resolution
ensures the correct stack is assembled automatically, merges duplicate identifiers, and
honors ordering constraints.

This guide explains how middleware dependencies work, how to configure
`MiddlewareSpec`, and how the resolver determines execution order.

## Declaring dependencies with `requires()`

Each `AgentMiddleware` subclass can override `requires()` and return a
sequence of `MiddlewareSpec` objects. Every spec describes another middleware
to insert before the requesting middleware runs.

```python
from langchain.agents.middleware import AgentMiddleware, MiddlewareSpec


class RetryMiddleware(AgentMiddleware):
def requires(self) -> list[MiddlewareSpec]:
# Ensure requests are redacted before retries are attempted
return [MiddlewareSpec(factory=PIIMiddleware)]
```

When `create_agent` resolves middleware, it recursively flattens dependencies.
Each dependency executes before the middleware that requested it, and their own
`requires()` declarations are resolved as well.

## `MiddlewareSpec` fields

`MiddlewareSpec` encapsulates dependency metadata:

| Field | Description |
| --- | --- |
| `factory` / `middleware` | Nullary callable that creates the dependency, or a pre-instantiated middleware. One of the two must be provided. |
| `id` | Optional identifier override. Defaults to the dependency's id or class name. |
| `priority` | Optional numeric priority. Higher values run earlier when order ties cannot be broken by other rules. |
| `tags` | Optional sequence of tags for referencing in ordering constraints. |
| `ordering` | Optional [`OrderingConstraints`](#orderingconstraints) specifying additional ordering relationships. |
| `merge_strategy` | Duplicate handling policy: `"first_wins"`, `"last_wins"`, or `"error"`. |

Dependencies that share an `id` are deduplicated according to the merge
strategy:

- `first_wins` (default): keep the first instance and merge subsequent ordering
constraints and tags.
- `last_wins`: replace the existing instance with the latest dependency.
- `error`: raise a `ValueError` if another dependency with the same `id`
is encountered. This mirrors user-supplied duplicates without an explicit strategy.

:::note
Middleware supplied without an explicit `id` receives an auto-generated identifier.
The first instance keeps its class name for backwards compatibility; additional
instances of the same class gain a deterministic module-qualified suffix (for
example, `my.module.Middleware#2`). This allows multiple differently configured
instances of the same middleware class to coexist without triggering duplicate
identifier errors.
:::

## `OrderingConstraints`

Ordering constraints ensure dependencies line up with other middleware:

```python
from langchain.agents.middleware import OrderingConstraints


MiddlewareSpec(
factory=AuthMiddleware,
ordering=OrderingConstraints(
after=("tag:session",),
before=("retry-handler",),
),
)
```

- `after` accepts middleware identifiers or `tag:<name>` references that must
execute before the dependency.
- `before` accepts identifiers or tags that must execute after the dependency.
- Tag references apply the constraint to every middleware with that tag.

Self references are not allowed. Referencing an unknown id or tag raises a
`ValueError` during agent creation.

## Ordering semantics

Middleware resolution produces a deterministic order by applying these rules:

1. **User order is the starting point.** Middleware passed to `create_agent`
retains its relative order when no other constraint applies.
2. **Dependencies run before their requestor.** Declared dependencies are
inserted ahead of the middleware that required them.
3. **Before/after constraints build a directed graph.** Constraints from
`OrderingConstraints` add graph edges between middleware identifiers or tags.
4. **Priority breaks ties.** When multiple candidates can execute next and user
order does not distinguish them, higher `priority` values win. If priorities
are equal, the resolver uses discovery order.
5. **Cycles fail fast.** If constraints introduce a cycle, a
`MiddlewareOrderCycleError` is raised with a human-readable cycle trace.

## Example

```python
from functools import partial
from langchain.agents import create_agent
from langchain.agents.middleware import AgentMiddleware, MiddlewareSpec, OrderingConstraints


class AuditMiddleware(AgentMiddleware):
id = "audit"
tags = ("observability",)

def requires(self) -> list[MiddlewareSpec]:
return [
MiddlewareSpec(
factory=partial(RateLimitMiddleware, limit=10),
merge_strategy="first_wins",
ordering=OrderingConstraints(after=("tag:auth",)),
)
]


class AuthMiddleware(AgentMiddleware):
id = "auth"
tags = ("auth",)


agent = create_agent(
model="openai:gpt-4o",
middleware=[AuthMiddleware(), AuditMiddleware()],
)
```

Resolution order:

1. `RateLimitMiddleware` (after everything tagged `auth`, before `audit`).
2. `AuthMiddleware` (user-supplied middleware).
3. `AuditMiddleware`.

If another middleware also requests the same `RateLimitMiddleware` with
`merge_strategy="first_wins"`, the resolver reuses the original instance and
adds the new ordering constraints.

## Troubleshooting

- **Duplicate id error** – Supply a unique `id` or configure `merge_strategy`
(`"first_wins"`/`"last_wins"`).
- **Cycle detected** – Review the cycle path in the
`MiddlewareOrderCycleError` message and adjust `before`/`after` constraints.
- **Unknown id/tag** – Ensure referenced identifiers are spelled correctly and
that tagged middleware assigns `tags` before resolution.
- **Unexpected ordering** – Remember that higher `priority` values preempt
lower ones when no other constraints apply. Adjust `priority` or add explicit
ordering constraints.
3 changes: 3 additions & 0 deletions src/oss/langchain/middleware/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ Middleware exposes hooks before and after each of those steps:
<Card title="Custom middleware" icon="code" href="/oss/langchain/middleware/custom">
Build your own middleware with hooks and decorators.
</Card>
<Card title="Dependency resolution" icon="sitemap" href="/oss/langchain/middleware/dependency-resolution">
Manage middleware dependencies, ordering, and deduplication.
</Card>
<Card title="Middleware API reference" icon="book" href="https://reference.langchain.com/python/langchain/middleware/">
Complete API reference for middleware.
</Card>
Expand Down