chore(langchain): activate mypy warn_return_any rule (#34549)

Co-authored-by: Mason Daugherty <github@mdrxy.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
This commit is contained in:
Christophe Bornet
2026-01-10 04:46:25 +01:00
committed by GitHub
parent cb0d227d8a
commit ecd19ff71f
16 changed files with 135 additions and 125 deletions

View File

@@ -314,7 +314,7 @@ def _resolve_schema(schemas: set[type], schema_name: str, omit_flag: str | None
return TypedDict(schema_name, all_annotations) # type: ignore[operator]
def _extract_metadata(type_: type) -> list:
def _extract_metadata(type_: type) -> list[Any]:
"""Extract metadata from a field type, handling Required/NotRequired and Annotated wrappers."""
# Handle Required[Annotated[...]] or NotRequired[Annotated[...]]
if get_origin(type_) in {Required, NotRequired}:
@@ -364,7 +364,9 @@ def _get_can_jump_to(middleware: AgentMiddleware[Any, Any], hook_name: str) -> l
return []
def _supports_provider_strategy(model: str | BaseChatModel, tools: list | None = None) -> bool:
def _supports_provider_strategy(
model: str | BaseChatModel, tools: list[BaseTool | dict[str, Any]] | None = None
) -> bool:
"""Check if a model supports provider-specific structured output.
Args:
@@ -403,7 +405,7 @@ def _supports_provider_strategy(model: str | BaseChatModel, tools: list | None =
def _handle_structured_output_error(
exception: Exception,
response_format: ResponseFormat,
response_format: ResponseFormat[Any],
) -> tuple[bool, str]:
"""Handle structured output error. Returns `(should_retry, retry_tool_message)`."""
if not isinstance(response_format, ToolStrategy):
@@ -455,10 +457,10 @@ def _chain_tool_call_wrappers(
def composed(
request: ToolCallRequest,
execute: Callable[[ToolCallRequest], ToolMessage | Command],
) -> ToolMessage | Command:
execute: Callable[[ToolCallRequest], ToolMessage | Command[Any]],
) -> ToolMessage | Command[Any]:
# Create a callable that invokes inner with the original execute
def call_inner(req: ToolCallRequest) -> ToolMessage | Command:
def call_inner(req: ToolCallRequest) -> ToolMessage | Command[Any]:
return inner(req, execute)
# Outer can call call_inner multiple times
@@ -477,14 +479,14 @@ def _chain_tool_call_wrappers(
def _chain_async_tool_call_wrappers(
wrappers: Sequence[
Callable[
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]]],
Awaitable[ToolMessage | Command],
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
Awaitable[ToolMessage | Command[Any]],
]
],
) -> (
Callable[
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]]],
Awaitable[ToolMessage | Command],
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
Awaitable[ToolMessage | Command[Any]],
]
| None
):
@@ -504,25 +506,25 @@ def _chain_async_tool_call_wrappers(
def compose_two(
outer: Callable[
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]]],
Awaitable[ToolMessage | Command],
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
Awaitable[ToolMessage | Command[Any]],
],
inner: Callable[
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]]],
Awaitable[ToolMessage | Command],
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
Awaitable[ToolMessage | Command[Any]],
],
) -> Callable[
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]]],
Awaitable[ToolMessage | Command],
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
Awaitable[ToolMessage | Command[Any]],
]:
"""Compose two async wrappers where outer wraps inner."""
async def composed(
request: ToolCallRequest,
execute: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]],
) -> ToolMessage | Command:
execute: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]],
) -> ToolMessage | Command[Any]:
# Create an async callable that invokes inner with the original execute
async def call_inner(req: ToolCallRequest) -> ToolMessage | Command:
async def call_inner(req: ToolCallRequest) -> ToolMessage | Command[Any]:
return await inner(req, execute)
# Outer can call call_inner multiple times
@@ -540,7 +542,7 @@ def _chain_async_tool_call_wrappers(
def create_agent(
model: str | BaseChatModel,
tools: Sequence[BaseTool | Callable | dict[str, Any]] | None = None,
tools: Sequence[BaseTool | Callable[..., Any] | dict[str, Any]] | None = None,
*,
system_prompt: str | SystemMessage | None = None,
middleware: Sequence[AgentMiddleware[StateT_co, ContextT]] = (),
@@ -553,7 +555,7 @@ def create_agent(
interrupt_after: list[str] | None = None,
debug: bool = False,
name: str | None = None,
cache: BaseCache | None = None,
cache: BaseCache[Any] | None = None,
) -> CompiledStateGraph[
AgentState[ResponseT], ContextT, _InputAgentState, _OutputAgentState[ResponseT]
]:
@@ -704,7 +706,7 @@ def create_agent(
# Raw schemas are wrapped in AutoStrategy to preserve auto-detection intent.
# AutoStrategy is converted to ToolStrategy upfront to calculate tools during agent creation,
# but may be replaced with ProviderStrategy later based on model capabilities.
initial_response_format: ToolStrategy | ProviderStrategy | AutoStrategy | None
initial_response_format: ToolStrategy[Any] | ProviderStrategy[Any] | AutoStrategy[Any] | None
if response_format is None:
initial_response_format = None
elif isinstance(response_format, (ToolStrategy, ProviderStrategy)):
@@ -719,13 +721,13 @@ def create_agent(
# For AutoStrategy, convert to ToolStrategy to setup tools upfront
# (may be replaced with ProviderStrategy later based on model)
tool_strategy_for_setup: ToolStrategy | None = None
tool_strategy_for_setup: ToolStrategy[Any] | None = None
if isinstance(initial_response_format, AutoStrategy):
tool_strategy_for_setup = ToolStrategy(schema=initial_response_format.schema)
elif isinstance(initial_response_format, ToolStrategy):
tool_strategy_for_setup = initial_response_format
structured_output_tools: dict[str, OutputToolBinding] = {}
structured_output_tools: dict[str, OutputToolBinding[Any]] = {}
if tool_strategy_for_setup:
for response_schema in tool_strategy_for_setup.schema_specs:
structured_tool_info = OutputToolBinding.from_schema_spec(response_schema)
@@ -872,7 +874,7 @@ def create_agent(
)
def _handle_model_output(
output: AIMessage, effective_response_format: ResponseFormat | None
output: AIMessage, effective_response_format: ResponseFormat[Any] | None
) -> dict[str, Any]:
"""Handle model output including structured responses.
@@ -975,7 +977,9 @@ def create_agent(
return {"messages": [output]}
def _get_bound_model(request: ModelRequest) -> tuple[Runnable, ResponseFormat | None]:
def _get_bound_model(
request: ModelRequest,
) -> tuple[Runnable[Any, Any], ResponseFormat[Any] | None]:
"""Get the model with appropriate tool bindings.
Performs auto-detection of strategy if needed based on model capabilities.
@@ -1025,7 +1029,7 @@ def create_agent(
raise ValueError(msg)
# Determine effective response format (auto-detect if needed)
effective_response_format: ResponseFormat | None
effective_response_format: ResponseFormat[Any] | None
if isinstance(request.response_format, AutoStrategy):
# User provided raw schema via AutoStrategy - auto-detect best strategy based on model
if _supports_provider_strategy(request.model, tools=request.tools):
@@ -1119,7 +1123,7 @@ def create_agent(
structured_response=structured_response,
)
def model_node(state: AgentState, runtime: Runtime[ContextT]) -> dict[str, Any]:
def model_node(state: AgentState[Any], runtime: Runtime[ContextT]) -> dict[str, Any]:
"""Sync model request handler with sequential middleware processing."""
request = ModelRequest(
model=model,
@@ -1174,7 +1178,7 @@ def create_agent(
structured_response=structured_response,
)
async def amodel_node(state: AgentState, runtime: Runtime[ContextT]) -> dict[str, Any]:
async def amodel_node(state: AgentState[Any], runtime: Runtime[ContextT]) -> dict[str, Any]:
"""Async model request handler with sequential middleware processing."""
request = ModelRequest(
model=model,
@@ -1523,7 +1527,7 @@ def _fetch_last_ai_and_tool_messages(
def _make_model_to_tools_edge(
*,
model_destination: str,
structured_output_tools: dict[str, OutputToolBinding],
structured_output_tools: dict[str, OutputToolBinding[Any]],
end_destination: str,
) -> Callable[[dict[str, Any]], str | list[Send] | None]:
def model_to_tools(
@@ -1607,7 +1611,7 @@ def _make_tools_to_model_edge(
*,
tool_node: ToolNode,
model_destination: str,
structured_output_tools: dict[str, OutputToolBinding],
structured_output_tools: dict[str, OutputToolBinding[Any]],
end_destination: str,
) -> Callable[[dict[str, Any]], str | None]:
def tools_to_model(state: dict[str, Any]) -> str | None:

View File

@@ -102,7 +102,9 @@ class HITLResponse(TypedDict):
class _DescriptionFactory(Protocol):
"""Callable that generates a description for a tool call."""
def __call__(self, tool_call: ToolCall, state: AgentState, runtime: Runtime[ContextT]) -> str:
def __call__(
self, tool_call: ToolCall, state: AgentState[Any], runtime: Runtime[ContextT]
) -> str:
"""Generate a description for a tool call."""
...
@@ -203,7 +205,7 @@ class HumanInTheLoopMiddleware(AgentMiddleware[StateT, ContextT]):
self,
tool_call: ToolCall,
config: InterruptOnConfig,
state: AgentState,
state: AgentState[Any],
runtime: Runtime[ContextT],
) -> tuple[ActionRequest, ReviewConfig]:
"""Create an ActionRequest and ReviewConfig for a tool call."""
@@ -277,7 +279,9 @@ class HumanInTheLoopMiddleware(AgentMiddleware[StateT, ContextT]):
)
raise ValueError(msg)
def after_model(self, state: AgentState, runtime: Runtime[ContextT]) -> dict[str, Any] | None:
def after_model(
self, state: AgentState[Any], runtime: Runtime[ContextT]
) -> dict[str, Any] | None:
"""Trigger interrupt flows for relevant tool calls after an `AIMessage`.
Args:
@@ -363,7 +367,7 @@ class HumanInTheLoopMiddleware(AgentMiddleware[StateT, ContextT]):
return {"messages": [last_ai_msg, *artificial_tool_messages]}
async def aafter_model(
self, state: AgentState, runtime: Runtime[ContextT]
self, state: AgentState[Any], runtime: Runtime[ContextT]
) -> dict[str, Any] | None:
"""Async trigger interrupt flows for relevant tool calls after an `AIMessage`.

View File

@@ -19,7 +19,7 @@ if TYPE_CHECKING:
from langgraph.runtime import Runtime
class ModelCallLimitState(AgentState):
class ModelCallLimitState(AgentState[Any]):
"""State schema for `ModelCallLimitMiddleware`.
Extends `AgentState` with model call tracking fields.

View File

@@ -164,7 +164,7 @@ class PIIMiddleware(AgentMiddleware):
@override
def before_model(
self,
state: AgentState,
state: AgentState[Any],
runtime: Runtime,
) -> dict[str, Any] | None:
"""Check user messages and tool results for PII before model invocation.
@@ -259,7 +259,7 @@ class PIIMiddleware(AgentMiddleware):
@hook_config(can_jump_to=["end"])
async def abefore_model(
self,
state: AgentState,
state: AgentState[Any],
runtime: Runtime,
) -> dict[str, Any] | None:
"""Async check user messages and tool results for PII before model invocation.
@@ -280,7 +280,7 @@ class PIIMiddleware(AgentMiddleware):
@override
def after_model(
self,
state: AgentState,
state: AgentState[Any],
runtime: Runtime,
) -> dict[str, Any] | None:
"""Check AI messages for PII after model invocation.
@@ -339,7 +339,7 @@ class PIIMiddleware(AgentMiddleware):
async def aafter_model(
self,
state: AgentState,
state: AgentState[Any],
runtime: Runtime,
) -> dict[str, Any] | None:
"""Async check AI messages for PII after model invocation.

View File

@@ -78,7 +78,7 @@ class _SessionResources:
session: ShellSession
tempdir: tempfile.TemporaryDirectory[str] | None
policy: BaseExecutionPolicy
finalizer: weakref.finalize = field(init=False, repr=False)
finalizer: weakref.finalize = field(init=False, repr=False) # type: ignore[type-arg]
def __post_init__(self) -> None:
self.finalizer = weakref.finalize(
@@ -90,7 +90,7 @@ class _SessionResources:
)
class ShellToolState(AgentState):
class ShellToolState(AgentState[Any]):
"""Agent state extension for tracking shell session resources."""
shell_session_resources: NotRequired[

View File

@@ -269,7 +269,7 @@ class SummarizationMiddleware(AgentMiddleware):
raise ValueError(msg)
@override
def before_model(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
def before_model(self, state: AgentState[Any], runtime: Runtime) -> dict[str, Any] | None:
"""Process messages before model invocation, potentially triggering summarization.
Args:
@@ -305,7 +305,9 @@ class SummarizationMiddleware(AgentMiddleware):
}
@override
async def abefore_model(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
async def abefore_model(
self, state: AgentState[Any], runtime: Runtime
) -> dict[str, Any] | None:
"""Process messages before model invocation, potentially triggering summarization.
Args:

View File

@@ -35,7 +35,7 @@ class Todo(TypedDict):
"""The current status of the todo item."""
class PlanningState(AgentState):
class PlanningState(AgentState[Any]):
"""State schema for the todo middleware."""
todos: Annotated[NotRequired[list[Todo]], OmitFromInput]
@@ -118,7 +118,9 @@ Writing todos takes time and tokens, use it when it is helpful for managing comp
@tool(description=WRITE_TODOS_TOOL_DESCRIPTION)
def write_todos(todos: list[Todo], tool_call_id: Annotated[str, InjectedToolCallId]) -> Command:
def write_todos(
todos: list[Todo], tool_call_id: Annotated[str, InjectedToolCallId]
) -> Command[Any]:
"""Create and manage a structured task list for your current work session."""
return Command(
update={
@@ -178,7 +180,7 @@ class TodoListMiddleware(AgentMiddleware):
@tool(description=self.tool_description)
def write_todos(
todos: list[Todo], tool_call_id: Annotated[str, InjectedToolCallId]
) -> Command:
) -> Command[Any]:
"""Create and manage a structured task list for your current work session."""
return Command(
update={
@@ -246,11 +248,7 @@ class TodoListMiddleware(AgentMiddleware):
return await handler(request.override(system_message=new_system_message))
@override
def after_model(
self,
state: AgentState,
runtime: Runtime,
) -> dict[str, Any] | None:
def after_model(self, state: AgentState[Any], runtime: Runtime) -> dict[str, Any] | None:
"""Check for parallel write_todos tool calls and return errors if detected.
The todo list is designed to be updated at most once per model turn. Since
@@ -299,11 +297,8 @@ class TodoListMiddleware(AgentMiddleware):
return None
async def aafter_model(
self,
state: AgentState,
runtime: Runtime,
) -> dict[str, Any] | None:
@override
async def aafter_model(self, state: AgentState[Any], runtime: Runtime) -> dict[str, Any] | None:
"""Check for parallel write_todos tool calls and return errors if detected.
Async version of `after_model`. The todo list is designed to be updated at

View File

@@ -2,7 +2,7 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from langchain_core.language_models.chat_models import BaseChatModel
from langchain_core.messages import HumanMessage, ToolMessage
@@ -109,8 +109,8 @@ class LLMToolEmulator(AgentMiddleware):
def wrap_tool_call(
self,
request: ToolCallRequest,
handler: Callable[[ToolCallRequest], ToolMessage | Command],
) -> ToolMessage | Command:
handler: Callable[[ToolCallRequest], ToolMessage | Command[Any]],
) -> ToolMessage | Command[Any]:
"""Emulate tool execution using LLM if tool should be emulated.
Args:
@@ -159,8 +159,8 @@ class LLMToolEmulator(AgentMiddleware):
async def awrap_tool_call(
self,
request: ToolCallRequest,
handler: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]],
) -> ToolMessage | Command:
handler: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]],
) -> ToolMessage | Command[Any]:
"""Async version of `wrap_tool_call`.
Emulate tool execution using LLM if tool should be emulated.

View File

@@ -5,7 +5,7 @@ from __future__ import annotations
import asyncio
import time
import warnings
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from langchain_core.messages import ToolMessage
@@ -288,8 +288,8 @@ class ToolRetryMiddleware(AgentMiddleware):
def wrap_tool_call(
self,
request: ToolCallRequest,
handler: Callable[[ToolCallRequest], ToolMessage | Command],
) -> ToolMessage | Command:
handler: Callable[[ToolCallRequest], ToolMessage | Command[Any]],
) -> ToolMessage | Command[Any]:
"""Intercept tool execution and retry on failure.
Args:
@@ -346,8 +346,8 @@ class ToolRetryMiddleware(AgentMiddleware):
async def awrap_tool_call(
self,
request: ToolCallRequest,
handler: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]],
) -> ToolMessage | Command:
handler: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]],
) -> ToolMessage | Command[Any]:
"""Intercept and control async tool execution with retry logic.
Args:

View File

@@ -4,12 +4,7 @@ from __future__ import annotations
import logging
from dataclasses import dataclass
from typing import TYPE_CHECKING, Annotated, Literal, Union
if TYPE_CHECKING:
from collections.abc import Awaitable, Callable
from langchain.tools import BaseTool
from typing import TYPE_CHECKING, Annotated, Any, Literal, Union
from langchain_core.language_models.chat_models import BaseChatModel
from langchain_core.messages import HumanMessage
@@ -24,6 +19,11 @@ from langchain.agents.middleware.types import (
)
from langchain.chat_models.base import init_chat_model
if TYPE_CHECKING:
from collections.abc import Awaitable, Callable
from langchain.tools import BaseTool
logger = logging.getLogger(__name__)
DEFAULT_SYSTEM_PROMPT = (
@@ -42,7 +42,7 @@ class _SelectionRequest:
valid_tool_names: list[str]
def _create_tool_selection_response(tools: list[BaseTool]) -> TypeAdapter:
def _create_tool_selection_response(tools: list[BaseTool]) -> TypeAdapter[Any]:
"""Create a structured output schema for tool selection.
Args:
@@ -227,7 +227,7 @@ class LLMToolSelectorMiddleware(AgentMiddleware):
def _process_selection_response(
self,
response: dict,
response: dict[str, Any],
available_tools: list[BaseTool],
valid_tool_names: list[str],
request: ModelRequest,

View File

@@ -78,10 +78,10 @@ class _ModelRequestOverrides(TypedDict, total=False):
system_message: SystemMessage | None
messages: list[AnyMessage]
tool_choice: Any | None
tools: list[BaseTool | dict]
response_format: ResponseFormat | None
tools: list[BaseTool | dict[str, Any]]
response_format: ResponseFormat[Any] | None
model_settings: dict[str, Any]
state: AgentState
state: AgentState[Any]
@dataclass(init=False)
@@ -92,9 +92,9 @@ class ModelRequest:
messages: list[AnyMessage] # excluding system message
system_message: SystemMessage | None
tool_choice: Any | None
tools: list[BaseTool | dict]
response_format: ResponseFormat | None
state: AgentState
tools: list[BaseTool | dict[str, Any]]
response_format: ResponseFormat[Any] | None
state: AgentState[Any]
runtime: Runtime[ContextT] # type: ignore[valid-type]
model_settings: dict[str, Any] = field(default_factory=dict)
@@ -106,9 +106,9 @@ class ModelRequest:
system_message: SystemMessage | None = None,
system_prompt: str | None = None,
tool_choice: Any | None = None,
tools: list[BaseTool | dict] | None = None,
response_format: ResponseFormat | None = None,
state: AgentState | None = None,
tools: list[BaseTool | dict[str, Any]] | None = None,
response_format: ResponseFormat[Any] | None = None,
state: AgentState[Any] | None = None,
runtime: Runtime[ContextT] | None = None,
model_settings: dict[str, Any] | None = None,
) -> None:
@@ -321,7 +321,7 @@ class AgentState(TypedDict, Generic[ResponseT]):
class _InputAgentState(TypedDict): # noqa: PYI049
"""Input state schema for the agent."""
messages: Required[Annotated[list[AnyMessage | dict], add_messages]]
messages: Required[Annotated[list[AnyMessage | dict[str, Any]], add_messages]]
class _OutputAgentState(TypedDict, Generic[ResponseT]): # noqa: PYI049
@@ -331,9 +331,13 @@ class _OutputAgentState(TypedDict, Generic[ResponseT]): # noqa: PYI049
structured_response: NotRequired[ResponseT]
StateT = TypeVar("StateT", bound=AgentState, default=AgentState)
StateT_co = TypeVar("StateT_co", bound=AgentState, default=AgentState, covariant=True)
StateT_contra = TypeVar("StateT_contra", bound=AgentState, contravariant=True)
StateT = TypeVar("StateT", bound=AgentState[Any], default=AgentState[Any])
StateT_co = TypeVar("StateT_co", bound=AgentState[Any], default=AgentState[Any], covariant=True)
StateT_contra = TypeVar("StateT_contra", bound=AgentState[Any], contravariant=True)
class _DefaultAgentState(AgentState[Any]):
"""AgentMiddleware default state."""
class AgentMiddleware(Generic[StateT, ContextT]):
@@ -343,7 +347,7 @@ class AgentMiddleware(Generic[StateT, ContextT]):
between steps in the main agent loop.
"""
state_schema: type[StateT] = cast("type[StateT]", AgentState)
state_schema: type[StateT] = cast("type[StateT]", _DefaultAgentState)
"""The schema for state passed to the middleware nodes."""
tools: Sequence[BaseTool]
@@ -603,8 +607,8 @@ class AgentMiddleware(Generic[StateT, ContextT]):
def wrap_tool_call(
self,
request: ToolCallRequest,
handler: Callable[[ToolCallRequest], ToolMessage | Command],
) -> ToolMessage | Command:
handler: Callable[[ToolCallRequest], ToolMessage | Command[Any]],
) -> ToolMessage | Command[Any]:
"""Intercept tool execution for retries, monitoring, or modification.
Async version is `awrap_tool_call`
@@ -685,8 +689,8 @@ class AgentMiddleware(Generic[StateT, ContextT]):
async def awrap_tool_call(
self,
request: ToolCallRequest,
handler: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]],
) -> ToolMessage | Command:
handler: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]],
) -> ToolMessage | Command[Any]:
"""Intercept and control async tool execution via handler callback.
The handler callback executes the tool call and returns a `ToolMessage` or
@@ -757,7 +761,7 @@ class _CallableWithStateAndRuntime(Protocol[StateT_contra, ContextT]):
def __call__(
self, state: StateT_contra, runtime: Runtime[ContextT]
) -> dict[str, Any] | Command | None | Awaitable[dict[str, Any] | Command | None]:
) -> dict[str, Any] | Command[Any] | None | Awaitable[dict[str, Any] | Command[Any] | None]:
"""Perform some logic with the state and runtime."""
...
@@ -798,8 +802,8 @@ class _CallableReturningToolResponse(Protocol):
def __call__(
self,
request: ToolCallRequest,
handler: Callable[[ToolCallRequest], ToolMessage | Command],
) -> ToolMessage | Command:
handler: Callable[[ToolCallRequest], ToolMessage | Command[Any]],
) -> ToolMessage | Command[Any]:
"""Intercept tool execution via handler callback."""
...
@@ -981,7 +985,7 @@ def before_model(
_self: AgentMiddleware[StateT, ContextT],
state: StateT,
runtime: Runtime[ContextT],
) -> dict[str, Any] | Command | None:
) -> dict[str, Any] | Command[Any] | None:
return await func(state, runtime) # type: ignore[misc]
# Preserve can_jump_to metadata on the wrapped function
@@ -1006,7 +1010,7 @@ def before_model(
_self: AgentMiddleware[StateT, ContextT],
state: StateT,
runtime: Runtime[ContextT],
) -> dict[str, Any] | Command | None:
) -> dict[str, Any] | Command[Any] | None:
return func(state, runtime) # type: ignore[return-value]
# Preserve can_jump_to metadata on the wrapped function
@@ -1141,7 +1145,7 @@ def after_model(
_self: AgentMiddleware[StateT, ContextT],
state: StateT,
runtime: Runtime[ContextT],
) -> dict[str, Any] | Command | None:
) -> dict[str, Any] | Command[Any] | None:
return await func(state, runtime) # type: ignore[misc]
# Preserve can_jump_to metadata on the wrapped function
@@ -1164,7 +1168,7 @@ def after_model(
_self: AgentMiddleware[StateT, ContextT],
state: StateT,
runtime: Runtime[ContextT],
) -> dict[str, Any] | Command | None:
) -> dict[str, Any] | Command[Any] | None:
return func(state, runtime) # type: ignore[return-value]
# Preserve can_jump_to metadata on the wrapped function
@@ -1332,7 +1336,7 @@ def before_agent(
_self: AgentMiddleware[StateT, ContextT],
state: StateT,
runtime: Runtime[ContextT],
) -> dict[str, Any] | Command | None:
) -> dict[str, Any] | Command[Any] | None:
return await func(state, runtime) # type: ignore[misc]
# Preserve can_jump_to metadata on the wrapped function
@@ -1357,7 +1361,7 @@ def before_agent(
_self: AgentMiddleware[StateT, ContextT],
state: StateT,
runtime: Runtime[ContextT],
) -> dict[str, Any] | Command | None:
) -> dict[str, Any] | Command[Any] | None:
return func(state, runtime) # type: ignore[return-value]
# Preserve can_jump_to metadata on the wrapped function
@@ -1493,7 +1497,7 @@ def after_agent(
_self: AgentMiddleware[StateT, ContextT],
state: StateT,
runtime: Runtime[ContextT],
) -> dict[str, Any] | Command | None:
) -> dict[str, Any] | Command[Any] | None:
return await func(state, runtime) # type: ignore[misc]
# Preserve can_jump_to metadata on the wrapped function
@@ -1516,7 +1520,7 @@ def after_agent(
_self: AgentMiddleware[StateT, ContextT],
state: StateT,
runtime: Runtime[ContextT],
) -> dict[str, Any] | Command | None:
) -> dict[str, Any] | Command[Any] | None:
return func(state, runtime) # type: ignore[return-value]
# Preserve can_jump_to metadata on the wrapped function
@@ -1964,8 +1968,8 @@ def wrap_tool_call(
async def async_wrapped(
_self: AgentMiddleware,
request: ToolCallRequest,
handler: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]],
) -> ToolMessage | Command:
handler: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]],
) -> ToolMessage | Command[Any]:
return await func(request, handler) # type: ignore[arg-type,misc]
middleware_name = name or cast(
@@ -1985,8 +1989,8 @@ def wrap_tool_call(
def wrapped(
_self: AgentMiddleware,
request: ToolCallRequest,
handler: Callable[[ToolCallRequest], ToolMessage | Command],
) -> ToolMessage | Command:
handler: Callable[[ToolCallRequest], ToolMessage | Command[Any]],
) -> ToolMessage | Command[Any]:
return func(request, handler)
middleware_name = name or cast("str", getattr(func, "__name__", "WrapToolCallMiddleware"))

View File

@@ -75,7 +75,7 @@ class StructuredOutputValidationError(StructuredOutputError):
def _parse_with_schema(
schema: type[SchemaT] | dict, schema_kind: SchemaKind, data: dict[str, Any]
schema: type[SchemaT] | dict[str, Any], schema_kind: SchemaKind, data: dict[str, Any]
) -> Any:
"""Parse data using for any supported schema type.

View File

@@ -581,12 +581,12 @@ class _ConfigurableModel(Runnable[LanguageModelInput, Any]):
def __init__(
self,
*,
default_config: dict | None = None,
default_config: dict[str, Any] | None = None,
configurable_fields: Literal["any"] | list[str] | tuple[str, ...] = "any",
config_prefix: str = "",
queued_declarative_operations: Sequence[tuple[str, tuple, dict]] = (),
queued_declarative_operations: Sequence[tuple[str, tuple[Any, ...], dict[str, Any]]] = (),
) -> None:
self._default_config: dict = default_config or {}
self._default_config: dict[str, Any] = default_config or {}
self._configurable_fields: Literal["any"] | list[str] = (
"any" if configurable_fields == "any" else list(configurable_fields)
)
@@ -595,8 +595,10 @@ class _ConfigurableModel(Runnable[LanguageModelInput, Any]):
if config_prefix and not config_prefix.endswith("_")
else config_prefix
)
self._queued_declarative_operations: list[tuple[str, tuple, dict]] = list(
queued_declarative_operations,
self._queued_declarative_operations: list[tuple[str, tuple[Any, ...], dict[str, Any]]] = (
list(
queued_declarative_operations,
)
)
def __getattr__(self, name: str) -> Any:
@@ -629,14 +631,14 @@ class _ConfigurableModel(Runnable[LanguageModelInput, Any]):
msg += "."
raise AttributeError(msg)
def _model(self, config: RunnableConfig | None = None) -> Runnable:
def _model(self, config: RunnableConfig | None = None) -> Runnable[Any, Any]:
params = {**self._default_config, **self._model_params(config)}
model = _init_chat_model_helper(**params)
for name, args, kwargs in self._queued_declarative_operations:
model = getattr(model, name)(*args, **kwargs)
return model
def _model_params(self, config: RunnableConfig | None) -> dict:
def _model_params(self, config: RunnableConfig | None) -> dict[str, Any]:
config = ensure_config(config)
model_params = {
_remove_prefix(k, self._config_prefix): v
@@ -962,7 +964,7 @@ class _ConfigurableModel(Runnable[LanguageModelInput, Any]):
# Explicitly added to satisfy downstream linters.
def bind_tools(
self,
tools: Sequence[dict[str, Any] | type[BaseModel] | Callable | BaseTool],
tools: Sequence[dict[str, Any] | type[BaseModel] | Callable[..., Any] | BaseTool],
**kwargs: Any,
) -> Runnable[LanguageModelInput, AIMessage]:
return self.__getattr__("bind_tools")(tools, **kwargs)
@@ -970,7 +972,7 @@ class _ConfigurableModel(Runnable[LanguageModelInput, Any]):
# Explicitly added to satisfy downstream linters.
def with_structured_output(
self,
schema: dict | type[BaseModel],
schema: dict[str, Any] | type[BaseModel],
**kwargs: Any,
) -> Runnable[LanguageModelInput, dict | BaseModel]:
) -> Runnable[LanguageModelInput, dict[str, Any] | BaseModel]:
return self.__getattr__("with_structured_output")(schema, **kwargs)

View File

@@ -95,7 +95,6 @@ warn_unreachable = true
exclude = ["tests/unit_tests/agents/*"]
# TODO: activate for 'strict' checking
disallow_any_generics = false
warn_return_any = false
[[tool.mypy.overrides]]

View File

@@ -1,4 +1,4 @@
from typing import cast
from typing import Any, cast
import pytest
from langchain_core.language_models import BaseChatModel
@@ -41,7 +41,7 @@ class TestStandard(ChatModelIntegrationTests):
return cast("type[BaseChatModel]", init_chat_model)
@property
def chat_model_params(self) -> dict:
def chat_model_params(self) -> dict[str, Any]:
return {"model": "gpt-4o", "configurable_fields": "any"}
@property

View File

@@ -23,7 +23,7 @@ def remove_request_headers(request: Any) -> Any:
return request
def remove_response_headers(response: dict) -> dict:
def remove_response_headers(response: dict[str, Any]) -> dict[str, Any]:
"""Remove sensitive headers from the response."""
for k in response["headers"]:
response["headers"][k] = "**REDACTED**"
@@ -31,7 +31,7 @@ def remove_response_headers(response: dict) -> dict:
@pytest.fixture(scope="session")
def vcr_config() -> dict:
def vcr_config() -> dict[str, Any]:
"""Extend the default configuration coming from langchain_tests."""
config = base_vcr_config()
config.setdefault("filter_headers", []).extend(_EXTRA_HEADERS)
@@ -42,7 +42,7 @@ def vcr_config() -> dict:
return config
def pytest_recording_configure(config: dict, vcr: VCR) -> None: # noqa: ARG001
def pytest_recording_configure(config: dict[str, Any], vcr: VCR) -> None: # noqa: ARG001
vcr.register_persister(CustomPersister())
vcr.register_serializer("yaml.gz", CustomSerializer())