Skip to content

chore(langchain): speed up todo list middleware init#36311

Merged
Eugene Yurtsev (eyurtsev) merged 3 commits intomasterfrom
eugene/optimzie_todo_list
Mar 27, 2026
Merged

chore(langchain): speed up todo list middleware init#36311
Eugene Yurtsev (eyurtsev) merged 3 commits intomasterfrom
eugene/optimzie_todo_list

Conversation

@eyurtsev
Copy link
Copy Markdown
Collaborator

@eyurtsev Eugene Yurtsev (eyurtsev) commented Mar 27, 2026

Speed up todo list middleware initialization

@eyurtsev Eugene Yurtsev (eyurtsev) changed the title chore(langchain_v1): speed up todo list chore(langchain_v1): speed up todo list middleware init Mar 27, 2026
@github-actions github-actions bot added infra PRs made that include chores, devops, repo meta changes internal langchain `langchain` package issues & PRs size: XS < 50 LOC labels Mar 27, 2026
@eyurtsev Eugene Yurtsev (eyurtsev) changed the title chore(langchain_v1): speed up todo list middleware init chore(langchain): speed up todo list middleware init Mar 27, 2026
@github-actions github-actions bot added size: S 50-199 LOC and removed size: XS < 50 LOC labels Mar 27, 2026
@eyurtsev Eugene Yurtsev (eyurtsev) marked this pull request as ready for review March 27, 2026 19:12
Copilot AI review requested due to automatic review settings March 27, 2026 19:12
@eyurtsev Eugene Yurtsev (eyurtsev) merged commit 954a230 into master Mar 27, 2026
51 checks passed
@eyurtsev Eugene Yurtsev (eyurtsev) deleted the eugene/optimzie_todo_list branch March 27, 2026 19:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to speed up TodoListMiddleware initialization by avoiding per-instance nested tool function definition and switching to a StructuredTool built from a module-level implementation.

Changes:

  • Replace the nested @tool function defined in TodoListMiddleware.__init__ with a module-level _write_todos implementation.
  • Build the write_todos tool via StructuredTool.from_function(...) with an explicit Pydantic args schema (WriteTodosInput).
  • Adjust imports to support the new tool construction and runtime access.
Comments suppressed due to low confidence (1)

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

  • This module now contains both a write_todos function and a _write_todos function, while the tool itself is named write_todos. That overlap makes it harder to tell which implementation is the actual tool entrypoint (and increases the chance of accidental misuse during future refactors). Consider renaming _write_todos to a clearer tool-specific name (e.g., write_todos_tool_impl) or reusing the existing write_todos function as the tool implementation (if compatible).
def write_todos(

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +141 to +152
def _write_todos(
runtime: ToolRuntime[ContextT, PlanningState[ResponseT]], todos: list[Todo]
) -> Command[Any]:
"""Create and manage a structured task list for your current work session."""
return Command(
update={
"todos": todos,
"messages": [
ToolMessage(f"Updated todo list to {todos}", tool_call_id=runtime.tool_call_id)
],
}
)
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StructuredTool.from_function(..., args_schema=WriteTodosInput, infer_schema=False) will validate/provide only the fields from WriteTodosInput (currently just todos). However _write_todos requires a runtime positional argument as well, which is not part of the schema; this is likely to raise a TypeError at invocation time unless StructuredTool has an explicit injection mechanism for ToolRuntime in this codebase. To make this robust, align the tool function signature with the args schema (e.g., accept only todos) and use the established injection mechanism for the tool call id (such as an injected tool_call_id argument), or otherwise ensure runtime is provided by the tool runner in a documented/supported way.

Copilot uses AI. Check for mistakes.
Comment on lines +206 to +207
args_schema=WriteTodosInput,
infer_schema=False,
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StructuredTool.from_function(..., args_schema=WriteTodosInput, infer_schema=False) will validate/provide only the fields from WriteTodosInput (currently just todos). However _write_todos requires a runtime positional argument as well, which is not part of the schema; this is likely to raise a TypeError at invocation time unless StructuredTool has an explicit injection mechanism for ToolRuntime in this codebase. To make this robust, align the tool function signature with the args schema (e.g., accept only todos) and use the established injection mechanism for the tool call id (such as an injected tool_call_id argument), or otherwise ensure runtime is provided by the tool runner in a documented/supported way.

Suggested change
args_schema=WriteTodosInput,
infer_schema=False,

Copilot uses AI. Check for mistakes.


# Dynamically create the write_todos tool with the custom description
def _write_todos(
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module now contains both a write_todos function and a _write_todos function, while the tool itself is named write_todos. That overlap makes it harder to tell which implementation is the actual tool entrypoint (and increases the chance of accidental misuse during future refactors). Consider renaming _write_todos to a clearer tool-specific name (e.g., write_todos_tool_impl) or reusing the existing write_todos function as the tool implementation (if compatible).

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +10
from collections.abc import Awaitable, Callable
from typing import Annotated, Any, Literal, cast

from langchain_core.messages import AIMessage, SystemMessage, ToolMessage
from langchain_core.tools import tool
from langchain_core.tools import InjectedToolCallId, StructuredTool, tool
from langgraph.runtime import Runtime
from langgraph.types import Command
from pydantic import BaseModel, Field
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change removes from __future__ import annotations and the TYPE_CHECKING-guarded imports, which means langgraph.runtime.Runtime (and other types) are now imported/evaluated at module import time. That can increase cold-start/import overhead and may counteract the stated goal of speeding up initialization. If runtime evaluation isn’t needed, consider restoring postponed annotations (or moving heavier type-only imports behind TYPE_CHECKING) so these imports don’t execute on every import.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

infra PRs made that include chores, devops, repo meta changes internal langchain `langchain` package issues & PRs size: S 50-199 LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants