Files
langchain/libs
Sydney Runkle 3ab8cc7ada feat(langchain): support for parallel (or interrupted) tool calls and structured output (#32980)
This enables parallel tool calling w/ a combo of
1. Standard and structured response tool calls
2. Deferred (requiring human approval / edits) tool calls and structured
response tool calls

Hard to unit test w/ HITL right now end to end, so here's a repro of
things working w/ an integration test:

```py
from pydantic import BaseModel, Field
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage
from langgraph.types import Command
from langgraph.checkpoint.memory import InMemorySaver
from langchain.agents.middleware_agent import create_agent
from langchain.agents.middleware.human_in_the_loop import HumanInTheLoopMiddleware
from langchain_openai import ChatOpenAI


class WeatherBaseModel(BaseModel):
    temperature: float = Field(description="Temperature in fahrenheit")
    condition: str = Field(description="Weather condition")


@tool
def add_numbers(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b


model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
checkpointer = InMemorySaver()

agent = create_agent(
    model=model,
    tools=[add_numbers],
    response_format=WeatherBaseModel,
    middleware=[HumanInTheLoopMiddleware(tool_configs={"add_numbers": True})],
)
agent = agent.compile(checkpointer=checkpointer)

# First invocation should be interrupted due to human-in-the-loop middleware
response = agent.invoke(
    {
        "messages": [
            HumanMessage(
                "Add 1 and 2, then return the weather forecast with temperature 72 and condition sunny."
            )
        ]
    },
    config={"configurable": {"thread_id": "1"}},
)
interrupt_description = response["__interrupt__"][0].value[0]["description"]
print(interrupt_description)
"""
Tool execution requires approval

Tool: add_numbers
Args: {'a': 1, 'b': 2}
"""

# Resume the agent with approval
response = agent.invoke(
    Command(resume=[{"type": "approve"}]), config={"configurable": {"thread_id": "1"}}
)

for msg in response["messages"]:
    msg.pretty_print()

"""
================================ Human Message =================================

Add 1 and 2, then return the weather forecast with temperature 72 and condition sunny.
================================== Ai Message ==================================
Tool Calls:
  WeatherBaseModel (call_u6nXsEYRJbqNx4AEHgiQMpE2)
 Call ID: call_u6nXsEYRJbqNx4AEHgiQMpE2
  Args:
    temperature: 72
    condition: sunny
  add_numbers (call_nuQEZF7PwfYDlVpnSt8eaInI)
 Call ID: call_nuQEZF7PwfYDlVpnSt8eaInI
  Args:
    a: 1
    b: 2
================================= Tool Message =================================
Name: WeatherBaseModel

Returning structured response: temperature=72.0 condition='sunny'
================================= Tool Message =================================
Name: add_numbers

3
"""

print(repr(response["response"]))
"""
WeatherBaseModel(temperature=72.0, condition='sunny')
"""

```
2025-09-17 10:23:26 -04:00
..