Tool runs in `_TracerCore._create_tool_run` were discarding the
structured `inputs` dict that `BaseTool.run` passes to `on_tool_start`,
replacing it with `{"input": str(filtered_tool_input)}`. Consequently,
every multi-arg tool (e.g. ones in `deepagents` like `execute`,
`edit_file`, `write_file`, `grep`, ...) appeared in LangSmith with a
stringified, escaped dump of its arguments — multi-line bash commands
rendered with `\n` and were effectively unreadable. Chain runs already
preserved dicts via `_get_chain_inputs`; tool runs are now symmetric.
## Changes
- Preserve `inputs` when it is already a `dict` in the `original` /
`original+chat` branch of `_TracerCore._create_tool_run`, falling back
to `{"input": input_str}` only when no structured payload was provided
- Add regression tests in the sync and async base-tracer suites that
pass a structured `inputs` to `on_tool_start` and assert the dict
survives onto the resulting `Run`
## Breaking change
Custom `BaseTracer` subclasses that parsed `Run.inputs["input"]` as a
stringified dict for tool runs will need to read the structured fields
directly. The shape now matches what `on_tool_start(inputs=...)` has
always received — introduced alongside `_schema_format` in the
`astream_events` work — and what `streaming_events` consumers already
see.
When a langsmith `@traceable` function invokes a LangChain Runnable or
LangGraph subgraph, the callback manager's `_configure` function injects
the `@traceable` RunTree into the `LangChainTracer`'s `run_map` so that
child runs can resolve their parent for trace nesting. However, since
the RunTree was created outside the tracer's callback lifecycle,
`_end_trace` never removes it. The entry persists in `run_map`
indefinitely, retaining the full RunTree and its entire child tree.
In applications with nested subgraph invocations (e.g. an outer
investigation graph delegating to skill agent subgraphs, each compiled
as their own `StateGraph`), this causes RunTree objects to accumulate
linearly with every call.
**Fix:** Track which `run_map` entries were injected externally via a
shared `_external_run_ids` refcount dict on `_TracerCore`. When
`_start_trace` adds a child under an external parent, it increments the
count. When `_end_trace` finishes a child, it decrements — and evicts
the external parent from `run_map` once the last child completes.
The refcount (rather than a simple set) is necessary because a single
external parent may have multiple sibling children in the callback chain
(e.g. a `prompt | llm` `RunnableSequence`). Only truly external runs are
tracked — the `_configure` guard `if run_id_str not in handler.run_map`
prevents tracer-managed runs from being misclassified.
Fixes missing `run.metadata.usage_metadata` population in
`LangChainTracer` for real LLM/chat traces following #34414
- Fix extraction to read usage from serialized tracer message shape:
`outputs.generations[*][*].message.kwargs.usage_metadata`
- Remove non-serialized direct message shape handling
(`message.usage_metadata`) from extractor to match real tracer output
path
- Clarify tracer docstrings around chat callback naming
(`on_chat_model_start` + shared `on_llm_end`) to reduce ambiguity
## Why
#34414 introduced usage duplication into `run.metadata.usage_metadata`,
but the extractor read `message.usage_metadata`.
In real tracer flow, messages are serialized with `dumpd(...)` during
run completion, so usage metadata lives under
`message.kwargs.usage_metadata`. Because of this mismatch, duplication
did not trigger in real traces.
Fix several docstring inaccuracies in langchain-core and update outdated
LangSmith URLs across three README files.
**Docstring fixes (libs/core):**
- `tap_output_iter`: docstring says "async iterator" but method accepts
sync `Iterator`
- `agenerate_from_stream`: docstring says "Iterator" but method accepts
`AsyncIterator`
- `BaseLLM.OutputType`: docstring says "input type" but property returns
output type
- Grammar: "or deprecated" → "or be deprecated", "relies" → "rely",
"whose the" → "whose"
**URL fixes (libs/core, libs/langchain, libs/langchain_v1):**
- Updated `smith.langchain.com` → `www.langchain.com/langsmith` (root
README already uses the correct URL)
Verified with `make lint` and `make format` in libs/core — no new issues
introduced. Changes are docs-only with no code logic impact.
*This PR was created with assistance from an AI coding tool.*
## Description
This PR replaces a bare `except:` clause with `except Exception:` in
`libs/core/langchain_core/tracers/core.py`.
The previous implementation caught `BaseException`, which includes
`SystemExit` and `KeyboardInterrupt`. This meant that if a user tried to
interrupt the program (Ctrl+C) during a traceback formatting error, the
signal would be suppressed, potentially making the process un-killable.
This change ensures that standard runtime errors are still caught and
logged, but system control signals are allowed to propagate correctly.
## Verification
- Verified via code inspection.
- This is a standard safety fix for exception handling patterns in
Python to avoid suppressing system exit signals.
it looks scary but i promise it is not
improving documentation consistency across core. primarily update
docstrings and comments for better formatting, readability, and
accuracy, as well as add minor clarifications and formatting
improvements to user-facing documentation.
# Add `tool_call_id` to `on_tool_error` event data
## Summary
This PR addresses issue #33597 by adding `tool_call_id` to the
`on_tool_error` callback event data. This enables users to link tool
errors to specific tool calls in stateless agent implementations, which
is essential for building OpenAI-compatible APIs and tracking tool
execution flows.
## Problem
When streaming events using `astream_events` with `version="v2"`, the
`on_tool_error` event only included the error and input data, but lacked
the `tool_call_id`. This made it difficult to:
- Link errors to specific tool calls in stateless agent scenarios
- Implement OpenAI-compatible APIs that require tool call tracking
- Track tool execution flows when using `run_id` is not sufficient
## Solution
The fix adds `tool_call_id` propagation through the callback chain:
1. **Pass `tool_call_id` to callbacks**: Updated `BaseTool.run()` and
`BaseTool.arun()` to pass `tool_call_id` to both `on_tool_start` and
`on_tool_error` callbacks
2. **Store in event stream handler**: Modified
`_AstreamEventsCallbackHandler` to store `tool_call_id` in run info
during `on_tool_start`
3. **Include in error events**: Updated `on_tool_error` handler to
extract and include `tool_call_id` in the event data
## Changes
- **`libs/core/langchain_core/tools/base.py`**:
- Pass `tool_call_id` to `on_tool_start` in both sync and async methods
- Pass `tool_call_id` to `on_tool_error` when errors occur
- **`libs/core/langchain_core/tracers/event_stream.py`**:
- Store `tool_call_id` in run info during `on_tool_start`
- Extract `tool_call_id` from kwargs or run info in `on_tool_error`
- Include `tool_call_id` in the `on_tool_error` event data
## Testing
The fix was verified by:
1. Direct tool invocation: Confirmed `tool_call_id` appears in
`on_tool_error` event data when calling tools directly
2. Agent integration: Tested with `create_agent` to ensure
`tool_call_id` is present in error events during agent execution
```python
# Example verification
async for event in agent.astream_events(
{"messages": "Please demonstrate a tool error"},
version="v2",
):
if event["event"] == "on_tool_error":
assert "tool_call_id" in event["data"] # ✓ Now passes
print(event["data"]["tool_call_id"])
```
## Backward Compatibility
- ✅ Fully backward compatible: `tool_call_id` is optional (can be
`None`)
- ✅ No breaking changes: All changes are additive
- ✅ Existing code continues to work without modification
## Related Issues
Fixes#33597
---------
Co-authored-by: Mason Daugherty <github@mdrxy.com>
* FIxed where possible
* Used `cast` when not possible to fix
---------
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
* Fixed a few TC
* Added a few Pydantic classes to
`flake8-type-checking.runtime-evaluated-base-classes` (not as much as I
would have imagined)
* Added a few `noqa: TC`
* Activated TC rules
Adds automatic tool call counting to tracing by means of a new
`store_tool_call_count_in_run()`, which calls on newly added
`count_tool_calls_in_run()`.
Runs on successful LLM completion. Does not run on errored runs.
Adds `usage_metadata` (token counts, etc.) to the run metadata in
`LangChainTracer`.
When an LLM run ends, usage metadata is extracted from all generations
and aggregated using the existing `add_usage` helper, then stored in
`run.extra["metadata"]["usage_metadata"]`.
The original data in outputs remains unchanged.
Also, see #34415
---------
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
ref https://github.com/langchain-ai/langchainjs/pull/9665
Fixes trace persistence for iterator/generator inputs (like
`RunnableGenerator`) where the full input isn't available at chain
start. Instead of POSTing a run with incomplete inputs on start and
PATCHing later, this defers the POST until chain end when inputs are
fully realized.
---------
Co-authored-by: Mason Daugherty <github@mdrxy.com>
* Fix detection of support of context in `asyncio.create_task`
* Fix: in Python 3.14 `asyncio.get_event_loop()` raises an exception if
there's no running loop
* Bump pydantic to version 2.12
* Skips tests with pydantic v1 models as they are not supported with
Python 3.14
* Run core tests with Python 3.14 in CI.
---------
Co-authored-by: Mason Daugherty <mason@langchain.dev>
Co-authored-by: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com>
Largely:
- Remove explicit `"Default is x"` since new refs show default inferred
from sig
- Inline code (useful for eventual parsing)
- Fix code block rendering (indentations)
Removed:
- `libs/core/langchain_core/chat_history.py`: `add_user_message` and
`add_ai_message` in favor of `add_messages` and `aadd_messages`
- `libs/core/langchain_core/language_models/base.py`: `predict`,
`predict_messages`, and async versions in favor of `invoke`. removed
`_all_required_field_names` since it was a wrapper on
`get_pydantic_field_names`
- `libs/core/langchain_core/language_models/chat_models.py`:
`callback_manager` param in favor of `callbacks`. `__call__` and
`call_as_llm` method in favor of `invoke`
- `libs/core/langchain_core/language_models/llms.py`: `callback_manager`
param in favor of `callbacks`. `__call__`, `predict`, `apredict`, and
`apredict_messages` methods in favor of `invoke`
- `libs/core/langchain_core/prompts/chat.py`: `from_role_strings` and
`from_strings` in favor of `from_messages`
- `libs/core/langchain_core/prompts/pipeline.py`: removed
`PipelinePromptTemplate`
- `libs/core/langchain_core/prompts/prompt.py`: `input_variables` param
on `from_file` as it wasn't used
- `libs/core/langchain_core/tools/base.py`: `callback_manager` param in
favor of `callbacks`
- `libs/core/langchain_core/tracers/context.py`: `tracing_enabled` in
favor of `tracing_enabled_v2`
- `libs/core/langchain_core/tracers/langchain_v1.py`: entire module
- `libs/core/langchain_core/utils/loading.py`: entire module,
`try_load_from_hub`
- `libs/core/langchain_core/vectorstores/in_memory.py`: `upsert` in
favor of `add_documents`
- `libs/standard-tests/langchain_tests/integration_tests/chat_models.py`
and `libs/standard-tests/langchain_tests/unit_tests/chat_models.py`:
`tool_choice_value` as models should accept `tool_choice="any"`
- `langchain` will consequently no longer expose these items if it was
previously
---------
Co-authored-by: Mohammad Mohtashim <45242107+keenborder786@users.noreply.github.com>
Co-authored-by: Caspar Broekhuizen <caspar@langchain.dev>
Co-authored-by: ccurme <chester.curme@gmail.com>
Co-authored-by: Christophe Bornet <cbornet@hotmail.com>
Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
Co-authored-by: Sadra Barikbin <sadraqazvin1@yahoo.com>
Co-authored-by: Vadym Barda <vadim.barda@gmail.com>