langchain-ai/langchain
Agents are LangGraph state machines: input is a messages list merged into a resolved state schema (middleware schemas + AgentState, base last so caller schema wins conflicts). Each loop iteration builds a ModelRequest carried through the wrap_model_call middleware stack to a bound model; the AIMessage is post-processed for structured output and packed into Command state updates. Tool calls route to ToolNode, whose ToolMessages append via the add_messages reducer. Tool calls go through a wrap_tool_call stack. Streaming is reshaped by TransformerFactory instances registered at compile time. Outside agents, init_chat_model resolves provider strings to lazily imported classes, and BaseChatModel.invoke routes through callbacks, an LLM cache, and a rate limiter before _generate/_stream.
- Agent input boundary is `{"messages": [...]}`: `_resolve_schemas` merges each middleware's `state_schema` (in registration order) with the base schema last, producing resolved_state_schema, input_schema, output_schema for the StateGraph.
- ModelRequest is the data unit flowing through wrap_model_call middleware: it carries model, messages (excluding system message), system_message, tool_choice, tools, response_format, state, runtime, and model_settings, with an `override()` API (direct attribute assignment is deprecated).
- The model node builds a ModelRequest, runs it through the composed `wrap_model_call_handler` (or calls `_execute_model` directly), then `_build_commands` converts the ModelResponse into Command objects updating `messages` and `structured_response` state.
- `_get_bound_model` validates middleware-added client-side tools against `tool_node.tools_by_name` (skipped when wrap_tool_call middleware can handle dynamic tools), normalizes raw schemas to AutoStrategy, and picks ProviderStrategy vs ToolStrategy by model capability.
- Structured output is marshaled inside `_handle_model_output`: ProviderStrategy parses the AIMessage via `ProviderStrategyBinding.parse`, ToolStrategy intercepts tool_calls matching `structured_output_tools` names and validates args into `structured_response` state.
- Tool execution reuses LangGraph's ToolNode — langchain's tools/tool_node.py is a backwards-compat shim re-exporting ToolNode, ToolCallRequest, ToolCallWrapper, InjectedState, InjectedStore, ToolRuntime from langgraph.prebuilt.
- Conversation history accumulates in the `messages` channel via langgraph's `add_messages` reducer imported in middleware types; agent state also carries ephemeral routing via JumpTo ('tools'|'model'|'end').
- Stream output is post-processed by transformers registered at compile: ToolCallTransformer and SubagentTransformer always run first, then per-middleware `transformers` (e.g. InternalCallTransformer for SummarizationMiddleware's internal calls), then user-supplied factories, deduped.
- Tracing flows through LangSmith: middleware wrap hooks are wrapped in `traceable` with `_scrub_inputs` stripping unserializable handler/runtime, per-middleware TracePolicy via `_node_trace_policy`, and the compiled graph gets metadata `ls_integration: langchain_create_agent`.
- init_chat_model data flow: provider key lookup in `_BUILTIN_PROVIDERS` -> `_import_module` (importlib with 'pip install <pkg>' hint on failure) -> getattr class -> functools.partial creator; provider inference from model name uses the registry keys.
- BaseChatModel.invoke pipelines through callback_manager.on_chat_model_start -> `_generate_with_cache` (llm cache lookup via `dumps(messages)` key, then rate_limiter.acquire) -> either protocol v2 streaming, implicit `_stream` chunk accumulation with on_llm_new_token, or `_generate` -> on_llm_end with LLMResult.
- BaseTool.run wraps execution in on_tool_start/on_tool_end callbacks and converts ToolException into tool output content per the tool's `handle_tool_error` flag (bool, str template, or callable) via `_handle_tool_error`.
- MCP tool data flow: MCPAdapter builds a FastMCPClient (str targets restricted to http/https), `list_tools` calls client.list_tools(cache_mode) then `as_langchain_tool` per remote tool; elicitation requests surface as langgraph interrupts.