CodeVetter · Repo Unpack · Behavior traces
langchain-ai/langchain
Key flows traced end-to-end: (1) model initialization via init_chat_model; (2) agent construction via create_agent — middleware classification, schema merge, node/edge wiring, compile; (3) the agent loop — before_agent/before_model hooks, model call through the wrap_model_call stack, tool dispatch, loop-back or exit through after_model/after_agent; (4) single chat-model invocation through cache/rate-limiter/streaming; (5) MCP tool adoption; (6) CI test and release flows.
- Import/startup: `import langchain` sets only __version__; `from langchain.agents import create_agent` pulls factory.py which imports langgraph (StateGraph, ToolNode, Command, Send), langsmith.traceable, and init_chat_model; `import langchain.mcp` emits a LangChainBetaWarning.
- init_chat_model('openai:gpt-5.5'): parse provider prefix -> `_get_chat_model_creator` (lru_cached) -> `_import_module('langchain_openai')` -> getattr ChatOpenAI -> functools.partial(_call, cls) -> cls(**kwargs); ollama falls back to langchain_community.chat_models for backwards compat.
- create_agent construction order: str model -> init_chat_model; system_prompt -> SystemMessage; middleware partitioned by which hooks each subclass overrides (before_agent/before_model/after_model/after_agent/wrap_model_call/wrap_tool_call); sync and async wrap handlers composed via `_chain_model_call_handlers`/`_chain_async_model_call_handlers`.
- Graph wiring: nodes added — 'model' (RunnableCallable(model_node, amodel_node)), 'tools' (ToolNode, only if tools exist), and '<middleware_name>.<hook>' RunnableCallables; START->entry_node; conditional edges route tools->loop_entry|exit and model->tools|exit|loop_entry; middleware chains wired via `_add_middleware_edge` with can_jump_to.
- Compile step: graph.compile(checkpointer, store, interrupt_before, interrupt_after, debug, name, cache, transformers=[ToolCallTransformer, SubagentTransformer, middleware+user transformers]) then `.with_config({recursion_limit: 9999, metadata: {ls_integration: langchain_create_agent}})`.
- Runtime loop order on agent.invoke: START -> first before_agent middleware -> before_model chain -> 'model' -> after_model chain (reverse order) -> conditional edge: tool_calls present -> 'tools' -> back to loop_entry_node (first before_model or 'model'); no tool_calls -> exit_node -> after_agent chain -> END.
- Inside the model node (sync path mirrored by amodel_node): ModelRequest assembled -> awrap_model_call_handler(request, _execute_model_async) -> _get_bound_model binds tools and resolves response format -> model.ainvoke(messages) -> _handle_model_output -> ModelResponse -> _build_commands (rejecting Command.goto from wrap_model_call with NotImplementedError).
- Structured-output error recovery: StructuredOutputValidationError or MultipleStructuredOutputsError goes through `_handle_structured_output_error`; if retryable, an error ToolMessage (STRUCTURED_OUTPUT_ERROR_TEMPLATE 'Please fix your mistakes.') is appended and the loop continues.
- Single chat-model call: BaseChatModel.invoke -> generate -> callback_manager.on_chat_model_start -> _generate_with_cache -> cache.lookup(prompt, llm_string) short-circuits on hit -> rate_limiter.acquire -> protocol-v2 streaming / implicit _stream (on_llm_new_token per chunk) / _generate -> run_manager.on_llm_end(LLMResult).
- Tool invocation inside ToolNode delegates to BaseTool.run: callback_manager.on_tool_start -> _run -> on ToolException, `_handle_tool_error` renders handled content per handle_tool_error -> on_tool_end(output).
- MCP adoption flow: MCPAdapter(target) -> str targets pass `_validate_url_target` -> FastMCPClient(target) -> `_arm_for_interrupts` (clones pre-built clients that lack an elicitation handler) -> `async with adapter` connects -> list_tools -> as_langchain_tool -> tools passed to create_agent.
- CI unit-test flow: _test.yml -> uv_setup action -> `uv sync --group test --dev` -> `make test` (docker compose postgres+redis up, pytest -n auto --disable-socket --allow-unix-socket) -> get_min_versions.py -> `uv pip install $MIN_VERSIONS` -> `make tests` again -> git-clean check.
- Release flow per AGENTS.md: bump `_version.py` + pyproject version + `uv lock`, merge `release(<partner>): <version>` PR, then `gh workflow run` on _release.yml — build -> release-notes -> pre-release-checks -> TestPyPI -> PyPI -> GitHub release/tag via ncipollo/release-action.