Create_agent Type Overloads Reject Heterogeneous Middleware List When Explicit State_schema Is Provided
Static type checking fails for create_agent when a heterogeneous list of middleware objects with different state_schema types is combined with an explicit state_schema argument. Runtime behavior works correctly, but type inference collapses all middleware to AgentMiddleware[AgentState, ...], causing `no-matching-overload` errors.
The typing overloads for create_agent constrain the middleware parameter as Sequence[AgentMiddleware[StateT_co, ContextT]], forcing all elements to share the same StateT_co. When a richer middleware (e.g., RichMiddleware[RichState, Any]) is mixed with a plain one, the inferred element type becomes AgentMiddleware[AgentState, Any], which does not match the overload expectations when state_schema is explicitly set to RichState.
1. Define a subclass RichState extending AgentState with an extra field.
2. Define RichMiddleware as AgentMiddleware[RichState, Any] with state_schema = RichState.
3. Define PlainMiddleware as AgentMiddleware[AgentState, Any].
4. Call create_agent(model='...', middleware=[RichMiddleware(), PlainMiddleware()], state_schema=RichState).
5. Run a static type checker (e.g., ty) to observe the overload error.
Fixing Code Block
```python
from typing import Any, Sequence, TypeVar, overload
from langchain_core.language_models.chat_models import BaseChatModel
from langchain_core.tools import BaseTool
from langchain.agents.middleware import AgentMiddleware, AgentState
from langchain.agents.factory import CompiledStateGraph, InputAgentState, OutputAgentState, Checkpointer, BaseStore, BaseCache, TransformerFactory
StateT_co = TypeVar("StateT_co", bound=AgentState, covariant=True)
ContextT = TypeVar("ContextT")
ResponseT = TypeVar("ResponseT")
# Modify the overloads in langchain/agents/factory.py
# Existing overloads that use Sequence[AgentMiddleware[StateT_co, ContextT]] should be replaced with
# more permissive signatures when state_schema is explicitly provided.
# The following overload allows heterogeneous middleware and uses state_schema as the authoritative state type.
@overload
def create_agent(
model: str | BaseChatModel,
tools: Sequence[BaseTool | Callable[..., Any] | dict[str, Any]] | None = None,
*,
system_prompt: str | SystemMessage | None = None,
middleware: Sequence[AgentMiddleware[Any, Any]] = (),
response_format: None = None,
state_schema: type[StateT_co] | None = None,
context_schema: type[ContextT] | None = None,
checkpointer: Checkpointer | None = None,
store: BaseStore | None = None,
interrupt_before: list[str] | None = None,
interrupt_after: list[str] | None = None,
debug: bool = False,
name: str | None = None,
cache: BaseCache[Any] | None = None,
transformers: Sequence[TransformerFactory] | None = None,
) -> CompiledStateGraph[StateT_co, ContextT, InputAgentState, OutputAgentState[Any]]: ...
# Additional overloads for response_format variations similarly need middleware widened to Any.
# Implementation signature remains unchanged; runtime behavior already merges state schemas correctly.
```
The fix relaxes the middleware type from Sequence[AgentMiddleware[StateT_co, ContextT]] to Sequence[AgentMiddleware[Any, Any]] in all overloads where state_schema is not None. Since the explicit state_schema parameter defines the final graph state, the middleware elements do not need to be homogeneous. The runtime implementation already performs the necessary schema merging, and this change only affects static type checking.
Edge Case Audit
Relaxing to Any reduces type safety guarantees for middleware element state compatibility. Developers could pass incompatible middleware without static errors. To mitigate, consider introducing a Protocol that validates middleware.state_schema is a subtype of the provided state_schema, or use an intersection type if available. Rollback: if issues arise, revert the overload changes and require developers to use cast() or untyped middleware as a workaround.