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

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

Copy link
Copy Markdown
Collaborator

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

@open-swe open-swe Bot left a comment

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.

Open SWE Review found 1 potential issue.

View Open SWE trace

Comment thread libs/langchain_v1/langchain/agents/factory.py Outdated

@open-swe open-swe Bot left a comment

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.

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

@open-swe open-swe Bot left a comment

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.

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

@open-swe open-swe Bot left a comment

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.

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.

@mdrxy Mason Daugherty (mdrxy) changed the title feat(agents): AgentRuntime fields + _build_runtime hook for middleware feat(agents): AgentRuntime fields + _build_runtime hook for middleware Jun 11, 2026
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