fix(core): fix nested subgraph rendering for 3+ levels with draw_mermaid (fixes #35492)
Root cause: in the "empty subgraphs" section of draw_mermaid, the guard
``if ":" not in prefix`` silently skipped any prefix that contained a colon
(e.g. "parent:child"). For 3+ level nesting where no subgraph has internal
edges, every prefix is multi-level, so no subgraph boxes were ever emitted —
nodes appeared flat with escaped colons ("parent\3achild\3agrandchild").
Fix: replace the flat loop with a recursive helper that expands every prefix
into its ancestor levels ("parent" and "parent:child" from "parent:child"),
then recurses depth-first to produce properly nested subgraph/end blocks.
2-level nesting (existing behaviour) is unchanged.
fix(core): filter _DirectlyInjectedToolArg params from tool schema to prevent Pydantic validation errors
Root cause: `_filter_schema_args` in `structured.py` did not filter
`_DirectlyInjectedToolArg` parameters (e.g., `ToolRuntime`) from the
auto-generated Pydantic schema. The schema used `extra="forbid"`, so
passing a `ToolRuntime` dataclass instance to `model_validate` produced
a `ValidationError` (`type=dataclass_type`) because Pydantic could not
validate the dataclass.
Fix: two-part change so the inferred schema excludes directly-injected
params and `_parse_input` strips those values before validation
(re-injecting them afterwards via `_injected_args_keys`):
1. `_filter_schema_args`: calls `_is_directly_injected_arg_type` to
exclude `_DirectlyInjectedToolArg` annotated params.
2. `_parse_input`: builds `tool_input_for_validation` that omits any
value that is a `_DirectlyInjectedToolArg` instance before calling
`model_validate`, avoiding the `extra="forbid"` rejection.
Fixes langchain-ai/langgraph#6431
fix(core): create independent dicts in BaseLLM metadata_list construction
Replace [{}] * len(prompts) with [{} for _ in range(len(prompts))] to
prevent shared mutable dict references.
The old pattern creates N references to the same dict object, so if any
downstream code mutates one entry's metadata dict, all entries are
silently corrupted.
This fix applies to both sync (generate) and async (agenerate) paths.
Fixes: langchain-ai/langchain#35900
fix(core): use list comprehension to avoid shared dict refs in metadata_list
`[{}] * len(prompts)` creates N references to the same dict object.
If any downstream code mutates one entry, all entries are silently
corrupted. Replace with `[{} for _ in range(len(prompts))]` to create
independent dicts.
Fixes both the sync (`generate`) and async (`agenerate`) paths.
Note: the related `[callback_manager] * len(prompts)` pattern in the
single-callback else branch is a separate pre-existing issue tracked
independently.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>