Skip to content

feat(agents): AgentRuntime fields + _build_runtime hook for middleware#37879

Open
Sydney Runkle (sydney-runkle) wants to merge 10 commits into
masterfrom
sr/agent-runtime
Open

feat(agents): AgentRuntime fields + _build_runtime hook for middleware#37879
Sydney Runkle (sydney-runkle) wants to merge 10 commits into
masterfrom
sr/agent-runtime

Conversation

@sydney-runkle
Copy link
Copy Markdown
Collaborator

@sydney-runkle Sydney Runkle (sydney-runkle) commented Jun 3, 2026

Adds AgentRuntime and a _build_runtime hook so subpackages can enrich the runtime without touching langchain internals.

@dataclass
class AgentRuntime(Runtime[ContextT]):
    agent_name: str
    model_name: str | None
    model: BaseChatModel | None
    system_prompt: str | None
    tools: list[BaseTool | dict]
    tool_choice: Any | None
    response_format: ResponseFormat | None
    model_settings: dict[str, Any]

class AgentMiddleware:
    def _build_runtime(self, runtime: AgentRuntime) -> AgentRuntime:
        """`_build_runtime` calls are accumulated across all middlewares before
        each hook dispatch. Subpackages override to return an enriched subclass."""
        return runtime

ModelRequest shared fields (model, tools, system_prompt, etc.) are now properties delegating to runtime, eliminating duplication.

Non-breaking: existing runtime: Runtime annotations accept AgentRuntime via Liskov substitution.

…dleware enrichment

AgentRuntime gains model_name and tools fields populated by create_agent at
wire time. A private _build_runtime hook on AgentMiddleware lets subpackages
(e.g. deepagents) return an enriched runtime subclass from their middleware
without exposing those fields in LangChain. All four middleware hook nodes
(before_agent, before_model, after_model, after_agent) now receive AgentRuntime
instead of the bare LangGraph Runtime; existing Runtime annotations remain
valid via Liskov substitution.
@github-actions github-actions Bot added feature For PRs that implement a new feature; NOT A FEATURE REQUEST internal langchain `langchain` package issues & PRs size: M 200-499 LOC labels Jun 3, 2026
Copy link
Copy Markdown
Contributor

@open-swe open-swe Bot left a comment

Choose a reason for hiding this comment

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

Open SWE Review found 1 potential issue.

View Open SWE trace

Comment thread libs/langchain_v1/langchain/agents/factory.py Outdated
Copy link
Copy Markdown
Contributor

@open-swe open-swe Bot left a comment

Choose a reason for hiding this comment

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

Open SWE Review found 1 potential issue.

View Open SWE trace

Comment on lines +86 to +90
model: BaseChatModel | None = field(default=None)
"""Resolved model instance, if not a dynamic callable."""

system_prompt: str | None = field(default=None)
"""System prompt for the agent."""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Hook runtimes leave new fields empty

The new AgentRuntime fields are only populated when model_node builds a ModelRequest; the hook-node wrappers still call AgentRuntime.from_runtime(..., model_name=..., tools=...) without passing model, system_prompt, tool_choice, or response_format. As a result, a before_model/after_model middleware that uses the newly documented runtime.model or runtime.system_prompt receives None even for an agent created with a concrete model and system prompt (I verified this with a before_model(self, state, runtime) hook). Please pass the same agent fields through the hook wrappers before exposing them on AgentRuntime, or avoid advertising them there.

(Refers to lines 86-90)


Was this helpful? React with 👍 or 👎 to provide feedback.

…ent, slim ModelRequest

- _build_runtime calls now chain across all middlewares before dispatch
  instead of being called per-middleware independently
- create_agent accepts backend= (object|None); passed to AgentRuntime
- AgentRuntime gains backend: object|None field + from_runtime param
- ModelRequest drops duplicated fields; model/system_prompt/tools/etc
  are now properties that delegate to runtime, eliminating redundancy
- _wrap_hook/_wrap_async_hook no longer take a mw argument
Copy link
Copy Markdown
Contributor

@open-swe open-swe Bot left a comment

Choose a reason for hiding this comment

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

Open SWE Review found 1 potential issue.

View Open SWE trace

Comment on lines +167 to +169
messages: list[AnyMessage]
state: AgentState
runtime: Runtime[ContextT] # type: ignore[valid-type]
model_settings: dict[str, Any] = field(default_factory=dict)
runtime: AgentRuntime[ContextT] # type: ignore[valid-type]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 ModelRequest constructor now rejects existing calls

Removing these dataclass fields from ModelRequest.__init__ breaks existing code that constructs a request directly with the documented invocation fields. This is not just theoretical: the current test suite still has many call sites such as ModelRequest(model=..., system_prompt=..., tools=..., response_format=...), and at this HEAD that raises TypeError: ModelRequest.__init__() got an unexpected keyword argument 'model'. Since ModelRequest is exported from the middleware package and used by middleware tests/users, please preserve the old constructor shape (for example with a custom __init__ that builds an AgentRuntime) while still delegating properties to the runtime internally.

(Refers to lines 167-169)


Was this helpful? React with 👍 or 👎 to provide feedback.

backend belongs only on deepagents.AgentRuntime, injected exclusively
via BackendMiddleware._build_runtime — not on the langchain layer
Copy link
Copy Markdown
Contributor

@open-swe open-swe Bot left a comment

Choose a reason for hiding this comment

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

Open SWE Review found 1 potential issue.

View Open SWE trace

tools=default_tools,
tool_choice=None,
response_format=initial_response_format,
backend=backend,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 Async agents crash on backend reference

The backend parameter was removed from create_agent and AgentRuntime.from_runtime, but the async model node still passes backend=backend. Any agent.ainvoke() now fails before the model runs with NameError: name 'backend' is not defined (I verified this with a minimal create_agent(...).ainvoke(...)). Please remove this argument here as well, matching the sync path above.

(Refers to line 1134)


Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature For PRs that implement a new feature; NOT A FEATURE REQUEST internal langchain `langchain` package issues & PRs size: M 200-499 LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant