Skip to content

Commit ecd19ff

Browse files
cbornetmdrxy
andauthored
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>
1 parent cb0d227 commit ecd19ff

16 files changed

Lines changed: 135 additions & 125 deletions

File tree

libs/langchain_v1/langchain/agents/factory.py

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ def _resolve_schema(schemas: set[type], schema_name: str, omit_flag: str | None
314314
return TypedDict(schema_name, all_annotations) # type: ignore[operator]
315315

316316

317-
def _extract_metadata(type_: type) -> list:
317+
def _extract_metadata(type_: type) -> list[Any]:
318318
"""Extract metadata from a field type, handling Required/NotRequired and Annotated wrappers."""
319319
# Handle Required[Annotated[...]] or NotRequired[Annotated[...]]
320320
if get_origin(type_) in {Required, NotRequired}:
@@ -364,7 +364,9 @@ def _get_can_jump_to(middleware: AgentMiddleware[Any, Any], hook_name: str) -> l
364364
return []
365365

366366

367-
def _supports_provider_strategy(model: str | BaseChatModel, tools: list | None = None) -> bool:
367+
def _supports_provider_strategy(
368+
model: str | BaseChatModel, tools: list[BaseTool | dict[str, Any]] | None = None
369+
) -> bool:
368370
"""Check if a model supports provider-specific structured output.
369371
370372
Args:
@@ -403,7 +405,7 @@ def _supports_provider_strategy(model: str | BaseChatModel, tools: list | None =
403405

404406
def _handle_structured_output_error(
405407
exception: Exception,
406-
response_format: ResponseFormat,
408+
response_format: ResponseFormat[Any],
407409
) -> tuple[bool, str]:
408410
"""Handle structured output error. Returns `(should_retry, retry_tool_message)`."""
409411
if not isinstance(response_format, ToolStrategy):
@@ -455,10 +457,10 @@ def compose_two(outer: ToolCallWrapper, inner: ToolCallWrapper) -> ToolCallWrapp
455457

456458
def composed(
457459
request: ToolCallRequest,
458-
execute: Callable[[ToolCallRequest], ToolMessage | Command],
459-
) -> ToolMessage | Command:
460+
execute: Callable[[ToolCallRequest], ToolMessage | Command[Any]],
461+
) -> ToolMessage | Command[Any]:
460462
# Create a callable that invokes inner with the original execute
461-
def call_inner(req: ToolCallRequest) -> ToolMessage | Command:
463+
def call_inner(req: ToolCallRequest) -> ToolMessage | Command[Any]:
462464
return inner(req, execute)
463465

464466
# Outer can call call_inner multiple times
@@ -477,14 +479,14 @@ def call_inner(req: ToolCallRequest) -> ToolMessage | Command:
477479
def _chain_async_tool_call_wrappers(
478480
wrappers: Sequence[
479481
Callable[
480-
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]]],
481-
Awaitable[ToolMessage | Command],
482+
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
483+
Awaitable[ToolMessage | Command[Any]],
482484
]
483485
],
484486
) -> (
485487
Callable[
486-
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]]],
487-
Awaitable[ToolMessage | Command],
488+
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
489+
Awaitable[ToolMessage | Command[Any]],
488490
]
489491
| None
490492
):
@@ -504,25 +506,25 @@ def _chain_async_tool_call_wrappers(
504506

505507
def compose_two(
506508
outer: Callable[
507-
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]]],
508-
Awaitable[ToolMessage | Command],
509+
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
510+
Awaitable[ToolMessage | Command[Any]],
509511
],
510512
inner: Callable[
511-
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]]],
512-
Awaitable[ToolMessage | Command],
513+
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
514+
Awaitable[ToolMessage | Command[Any]],
513515
],
514516
) -> Callable[
515-
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]]],
516-
Awaitable[ToolMessage | Command],
517+
[ToolCallRequest, Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]]],
518+
Awaitable[ToolMessage | Command[Any]],
517519
]:
518520
"""Compose two async wrappers where outer wraps inner."""
519521

520522
async def composed(
521523
request: ToolCallRequest,
522-
execute: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command]],
523-
) -> ToolMessage | Command:
524+
execute: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]],
525+
) -> ToolMessage | Command[Any]:
524526
# Create an async callable that invokes inner with the original execute
525-
async def call_inner(req: ToolCallRequest) -> ToolMessage | Command:
527+
async def call_inner(req: ToolCallRequest) -> ToolMessage | Command[Any]:
526528
return await inner(req, execute)
527529

528530
# Outer can call call_inner multiple times
@@ -540,7 +542,7 @@ async def call_inner(req: ToolCallRequest) -> ToolMessage | Command:
540542

541543
def create_agent(
542544
model: str | BaseChatModel,
543-
tools: Sequence[BaseTool | Callable | dict[str, Any]] | None = None,
545+
tools: Sequence[BaseTool | Callable[..., Any] | dict[str, Any]] | None = None,
544546
*,
545547
system_prompt: str | SystemMessage | None = None,
546548
middleware: Sequence[AgentMiddleware[StateT_co, ContextT]] = (),
@@ -553,7 +555,7 @@ def create_agent(
553555
interrupt_after: list[str] | None = None,
554556
debug: bool = False,
555557
name: str | None = None,
556-
cache: BaseCache | None = None,
558+
cache: BaseCache[Any] | None = None,
557559
) -> CompiledStateGraph[
558560
AgentState[ResponseT], ContextT, _InputAgentState, _OutputAgentState[ResponseT]
559561
]:
@@ -704,7 +706,7 @@ def check_weather(location: str) -> str:
704706
# Raw schemas are wrapped in AutoStrategy to preserve auto-detection intent.
705707
# AutoStrategy is converted to ToolStrategy upfront to calculate tools during agent creation,
706708
# but may be replaced with ProviderStrategy later based on model capabilities.
707-
initial_response_format: ToolStrategy | ProviderStrategy | AutoStrategy | None
709+
initial_response_format: ToolStrategy[Any] | ProviderStrategy[Any] | AutoStrategy[Any] | None
708710
if response_format is None:
709711
initial_response_format = None
710712
elif isinstance(response_format, (ToolStrategy, ProviderStrategy)):
@@ -719,13 +721,13 @@ def check_weather(location: str) -> str:
719721

720722
# For AutoStrategy, convert to ToolStrategy to setup tools upfront
721723
# (may be replaced with ProviderStrategy later based on model)
722-
tool_strategy_for_setup: ToolStrategy | None = None
724+
tool_strategy_for_setup: ToolStrategy[Any] | None = None
723725
if isinstance(initial_response_format, AutoStrategy):
724726
tool_strategy_for_setup = ToolStrategy(schema=initial_response_format.schema)
725727
elif isinstance(initial_response_format, ToolStrategy):
726728
tool_strategy_for_setup = initial_response_format
727729

728-
structured_output_tools: dict[str, OutputToolBinding] = {}
730+
structured_output_tools: dict[str, OutputToolBinding[Any]] = {}
729731
if tool_strategy_for_setup:
730732
for response_schema in tool_strategy_for_setup.schema_specs:
731733
structured_tool_info = OutputToolBinding.from_schema_spec(response_schema)
@@ -872,7 +874,7 @@ def check_weather(location: str) -> str:
872874
)
873875

874876
def _handle_model_output(
875-
output: AIMessage, effective_response_format: ResponseFormat | None
877+
output: AIMessage, effective_response_format: ResponseFormat[Any] | None
876878
) -> dict[str, Any]:
877879
"""Handle model output including structured responses.
878880
@@ -975,7 +977,9 @@ def _handle_model_output(
975977

976978
return {"messages": [output]}
977979

978-
def _get_bound_model(request: ModelRequest) -> tuple[Runnable, ResponseFormat | None]:
980+
def _get_bound_model(
981+
request: ModelRequest,
982+
) -> tuple[Runnable[Any, Any], ResponseFormat[Any] | None]:
979983
"""Get the model with appropriate tool bindings.
980984
981985
Performs auto-detection of strategy if needed based on model capabilities.
@@ -1025,7 +1029,7 @@ def _get_bound_model(request: ModelRequest) -> tuple[Runnable, ResponseFormat |
10251029
raise ValueError(msg)
10261030

10271031
# Determine effective response format (auto-detect if needed)
1028-
effective_response_format: ResponseFormat | None
1032+
effective_response_format: ResponseFormat[Any] | None
10291033
if isinstance(request.response_format, AutoStrategy):
10301034
# User provided raw schema via AutoStrategy - auto-detect best strategy based on model
10311035
if _supports_provider_strategy(request.model, tools=request.tools):
@@ -1119,7 +1123,7 @@ def _execute_model_sync(request: ModelRequest) -> ModelResponse:
11191123
structured_response=structured_response,
11201124
)
11211125

1122-
def model_node(state: AgentState, runtime: Runtime[ContextT]) -> dict[str, Any]:
1126+
def model_node(state: AgentState[Any], runtime: Runtime[ContextT]) -> dict[str, Any]:
11231127
"""Sync model request handler with sequential middleware processing."""
11241128
request = ModelRequest(
11251129
model=model,
@@ -1174,7 +1178,7 @@ async def _execute_model_async(request: ModelRequest) -> ModelResponse:
11741178
structured_response=structured_response,
11751179
)
11761180

1177-
async def amodel_node(state: AgentState, runtime: Runtime[ContextT]) -> dict[str, Any]:
1181+
async def amodel_node(state: AgentState[Any], runtime: Runtime[ContextT]) -> dict[str, Any]:
11781182
"""Async model request handler with sequential middleware processing."""
11791183
request = ModelRequest(
11801184
model=model,
@@ -1523,7 +1527,7 @@ def _fetch_last_ai_and_tool_messages(
15231527
def _make_model_to_tools_edge(
15241528
*,
15251529
model_destination: str,
1526-
structured_output_tools: dict[str, OutputToolBinding],
1530+
structured_output_tools: dict[str, OutputToolBinding[Any]],
15271531
end_destination: str,
15281532
) -> Callable[[dict[str, Any]], str | list[Send] | None]:
15291533
def model_to_tools(
@@ -1607,7 +1611,7 @@ def _make_tools_to_model_edge(
16071611
*,
16081612
tool_node: ToolNode,
16091613
model_destination: str,
1610-
structured_output_tools: dict[str, OutputToolBinding],
1614+
structured_output_tools: dict[str, OutputToolBinding[Any]],
16111615
end_destination: str,
16121616
) -> Callable[[dict[str, Any]], str | None]:
16131617
def tools_to_model(state: dict[str, Any]) -> str | None:

libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ class HITLResponse(TypedDict):
102102
class _DescriptionFactory(Protocol):
103103
"""Callable that generates a description for a tool call."""
104104

105-
def __call__(self, tool_call: ToolCall, state: AgentState, runtime: Runtime[ContextT]) -> str:
105+
def __call__(
106+
self, tool_call: ToolCall, state: AgentState[Any], runtime: Runtime[ContextT]
107+
) -> str:
106108
"""Generate a description for a tool call."""
107109
...
108110

@@ -203,7 +205,7 @@ def _create_action_and_config(
203205
self,
204206
tool_call: ToolCall,
205207
config: InterruptOnConfig,
206-
state: AgentState,
208+
state: AgentState[Any],
207209
runtime: Runtime[ContextT],
208210
) -> tuple[ActionRequest, ReviewConfig]:
209211
"""Create an ActionRequest and ReviewConfig for a tool call."""
@@ -277,7 +279,9 @@ def _process_decision(
277279
)
278280
raise ValueError(msg)
279281

280-
def after_model(self, state: AgentState, runtime: Runtime[ContextT]) -> dict[str, Any] | None:
282+
def after_model(
283+
self, state: AgentState[Any], runtime: Runtime[ContextT]
284+
) -> dict[str, Any] | None:
281285
"""Trigger interrupt flows for relevant tool calls after an `AIMessage`.
282286
283287
Args:
@@ -363,7 +367,7 @@ def after_model(self, state: AgentState, runtime: Runtime[ContextT]) -> dict[str
363367
return {"messages": [last_ai_msg, *artificial_tool_messages]}
364368

365369
async def aafter_model(
366-
self, state: AgentState, runtime: Runtime[ContextT]
370+
self, state: AgentState[Any], runtime: Runtime[ContextT]
367371
) -> dict[str, Any] | None:
368372
"""Async trigger interrupt flows for relevant tool calls after an `AIMessage`.
369373

libs/langchain_v1/langchain/agents/middleware/model_call_limit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from langgraph.runtime import Runtime
2020

2121

22-
class ModelCallLimitState(AgentState):
22+
class ModelCallLimitState(AgentState[Any]):
2323
"""State schema for `ModelCallLimitMiddleware`.
2424
2525
Extends `AgentState` with model call tracking fields.

libs/langchain_v1/langchain/agents/middleware/pii.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ def _process_content(self, content: str) -> tuple[str, list[PIIMatch]]:
164164
@override
165165
def before_model(
166166
self,
167-
state: AgentState,
167+
state: AgentState[Any],
168168
runtime: Runtime,
169169
) -> dict[str, Any] | None:
170170
"""Check user messages and tool results for PII before model invocation.
@@ -259,7 +259,7 @@ def before_model(
259259
@hook_config(can_jump_to=["end"])
260260
async def abefore_model(
261261
self,
262-
state: AgentState,
262+
state: AgentState[Any],
263263
runtime: Runtime,
264264
) -> dict[str, Any] | None:
265265
"""Async check user messages and tool results for PII before model invocation.
@@ -280,7 +280,7 @@ async def abefore_model(
280280
@override
281281
def after_model(
282282
self,
283-
state: AgentState,
283+
state: AgentState[Any],
284284
runtime: Runtime,
285285
) -> dict[str, Any] | None:
286286
"""Check AI messages for PII after model invocation.
@@ -339,7 +339,7 @@ def after_model(
339339

340340
async def aafter_model(
341341
self,
342-
state: AgentState,
342+
state: AgentState[Any],
343343
runtime: Runtime,
344344
) -> dict[str, Any] | None:
345345
"""Async check AI messages for PII after model invocation.

libs/langchain_v1/langchain/agents/middleware/shell_tool.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class _SessionResources:
7878
session: ShellSession
7979
tempdir: tempfile.TemporaryDirectory[str] | None
8080
policy: BaseExecutionPolicy
81-
finalizer: weakref.finalize = field(init=False, repr=False)
81+
finalizer: weakref.finalize = field(init=False, repr=False) # type: ignore[type-arg]
8282

8383
def __post_init__(self) -> None:
8484
self.finalizer = weakref.finalize(
@@ -90,7 +90,7 @@ def __post_init__(self) -> None:
9090
)
9191

9292

93-
class ShellToolState(AgentState):
93+
class ShellToolState(AgentState[Any]):
9494
"""Agent state extension for tracking shell session resources."""
9595

9696
shell_session_resources: NotRequired[

libs/langchain_v1/langchain/agents/middleware/summarization.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ def __init__(
269269
raise ValueError(msg)
270270

271271
@override
272-
def before_model(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
272+
def before_model(self, state: AgentState[Any], runtime: Runtime) -> dict[str, Any] | None:
273273
"""Process messages before model invocation, potentially triggering summarization.
274274
275275
Args:
@@ -305,7 +305,9 @@ def before_model(self, state: AgentState, runtime: Runtime) -> dict[str, Any] |
305305
}
306306

307307
@override
308-
async def abefore_model(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
308+
async def abefore_model(
309+
self, state: AgentState[Any], runtime: Runtime
310+
) -> dict[str, Any] | None:
309311
"""Process messages before model invocation, potentially triggering summarization.
310312
311313
Args:

libs/langchain_v1/langchain/agents/middleware/todo.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class Todo(TypedDict):
3535
"""The current status of the todo item."""
3636

3737

38-
class PlanningState(AgentState):
38+
class PlanningState(AgentState[Any]):
3939
"""State schema for the todo middleware."""
4040

4141
todos: Annotated[NotRequired[list[Todo]], OmitFromInput]
@@ -118,7 +118,9 @@ class PlanningState(AgentState):
118118

119119

120120
@tool(description=WRITE_TODOS_TOOL_DESCRIPTION)
121-
def write_todos(todos: list[Todo], tool_call_id: Annotated[str, InjectedToolCallId]) -> Command:
121+
def write_todos(
122+
todos: list[Todo], tool_call_id: Annotated[str, InjectedToolCallId]
123+
) -> Command[Any]:
122124
"""Create and manage a structured task list for your current work session."""
123125
return Command(
124126
update={
@@ -178,7 +180,7 @@ def __init__(
178180
@tool(description=self.tool_description)
179181
def write_todos(
180182
todos: list[Todo], tool_call_id: Annotated[str, InjectedToolCallId]
181-
) -> Command:
183+
) -> Command[Any]:
182184
"""Create and manage a structured task list for your current work session."""
183185
return Command(
184186
update={
@@ -246,11 +248,7 @@ async def awrap_model_call(
246248
return await handler(request.override(system_message=new_system_message))
247249

248250
@override
249-
def after_model(
250-
self,
251-
state: AgentState,
252-
runtime: Runtime,
253-
) -> dict[str, Any] | None:
251+
def after_model(self, state: AgentState[Any], runtime: Runtime) -> dict[str, Any] | None:
254252
"""Check for parallel write_todos tool calls and return errors if detected.
255253
256254
The todo list is designed to be updated at most once per model turn. Since
@@ -299,11 +297,8 @@ def after_model(
299297

300298
return None
301299

302-
async def aafter_model(
303-
self,
304-
state: AgentState,
305-
runtime: Runtime,
306-
) -> dict[str, Any] | None:
300+
@override
301+
async def aafter_model(self, state: AgentState[Any], runtime: Runtime) -> dict[str, Any] | None:
307302
"""Check for parallel write_todos tool calls and return errors if detected.
308303
309304
Async version of `after_model`. The todo list is designed to be updated at

0 commit comments

Comments
 (0)