# Before
```python
if isinstance(block, dict) and "text" in block:
text = block["text"]
break
```
Extracts text from any `dict` with a `'text'` key, including
thinking/reasoning blocks.
# After
```python
if isinstance(block, dict) and "text" in block:
block_type = block.get("type")
if block_type is None or block_type == "text":
text = block["text"]
break
```
Skips blocks with explicit non-text types (e.g., `type: 'thinking'`).
# Justification
Models like Gemini 3 return structured content with multiple block
types:
```python
[
{"type": "thinking", "text": "let me reason..."},
{"type": "text", "text": "The answer is 42"}
]
```
The old logic extracted `'let me reason...'` (the thinking block)
because it matched first. The new logic skips it and correctly extracts
`'The answer is 42'`.
The `ChatGeneration.text` field is used by `on_llm_new_token(token,
chunk=chunk)` callbacks during streaming. Consequently, it would get
tokens incorrectly for reasoning blocks.
Related: #34727
Updates `comma_list` in `libs/core/langchain_core/utils/strings.py` to
accept `Iterable[Any]` instead of `list[Any]`, making the utility more
flexible.
---------
Co-authored-by: Mason Daugherty <github@mdrxy.com>
The code behind this functionality has been moved to the
langchain-community repository, and there are tests there to exercise
this functionality.
Fixes#33392
Co-authored-by: amelvil2 <amelvil2>
## Summary
- Adds explicit `tags: list[str] | None = None` parameter to sync
`LLMManagerMixin` methods
- Aligns sync methods with their async counterparts in
`AsyncCallbackHandler`
## Changes
Added `tags` parameter to:
- `on_llm_new_token`
- `on_llm_end`
- `on_llm_error`
## Why
- Sync handlers receive `tags` via `**kwargs`, but it was undocumented
in the method signature
- Async handlers already have `tags` explicitly documented
- This improves IDE autocompletion and type hints for sync handlers
Closes#34720🤖 Generated with [Claude Code](https://claude.ai/claude-code)
Co-authored-by: skyvanguard <skyvanguard@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Clarify the preference for using exact model IDs from provider
documentation over aliases to ensure reliable behavior in face of
upstream backend changes.
# 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>
Changes Created
I have fixed the issue where a generic and misleading error message was
displayed when a JSON schema was missing the top-level
title
key.
[Fix: Improve error message for missing title in JSON schema
functions](https://github.com/Bhavesh007Sharma/langchain/tree/fix-json-schema-title-error)
File Modified:
libs/core/langchain_core/utils/function_calling.py
I updated the
convert_to_openai_function
validation logic to specifically check for
dict
inputs that look like schemas (
type
or
properties
keys present) but are missing the
title
key.
# Before (Generic Error)
raise ValueError(
f"Unsupported function\n\n{function}\n\nFunctions must be passed in"
" as Dict, pydantic.BaseModel, or Callable. If they're a dict they must"
" either be in OpenAI function format or valid JSON schema with
top-level"
" 'title' and 'description' keys."
)
# After (Specific Error)
if isinstance(function, dict) and ("type" in function or "properties" in
function):
msg = (
"Unsupported function\n\nTo use a JSON schema as a function, "
"it must have a top-level 'title' key to be used as the function name."
)
raise ValueError(msg)
Verification Results
Automated Tests
I created a reproduction script
reproduce_issue.py
to confirm the behavior.
Before Fix: The script would have raised the generic "Unsupported
function" error claiming description was also required.
After Fix: The script now confirms that the new, specific error message
is raised when
title
is missing.
(Note: Verification was performed by inspecting the code logic and
running a lightweight reproduction script locally, as full suite
verification had environment dependency issues.)
---------
Co-authored-by: Mason Daugherty <github@mdrxy.com>
This PR fixes a signature mismatch between BaseStore and its concrete
implementations by making the `prefix` parameter keyword-only in
`yield_keys` and `ayield_keys`.
This aligns the implementations with the BaseStore interface contract,
prevents Liskov Substitution Principle violations, and ensures
consistent
method signatures across store backends.
Fixes#32637
Breaking changes
None. This change only enforces the existing abstract interface and does
not modify runtime behavior
Testing
- Verified that existing test suites pass after the signature fix.
Parts of this contribution were assisted by generative AI for
code navigation and drafting. All final design decisions and changes
were
reviewed and validated manually.
---------
Co-authored-by: Khagesh-Anayasmi <khagesh.desai@anayasmi.in>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
Regression introduced in 8e3c6b109f
The commit changed the return annotation of `with_structured_output`
from `typing.Dict | BaseModel` to `builtins.dict[str, Any] | BaseModel`.
Since `BaseModel` refers to `pydantic.BaseModel (v2)`, but the test
`test_structured_output_pydantic_2_v1` uses `pydantic.v1.BaseModel`,
mypy's `warn_unreachable` setting flags the `isinstance` checks as
unreachable (since a class can't be both a `dict` and a different
`BaseModel` type).
Switching to `builtins.dict[str, Any]` made the type more precise, which
exposed this type incompatibility that was always latent but hidden by
the looser `typing.Dict` annotation.
* Making `FakeToolCallingModel` generic on its `structured_response`
doesn't help anywhere in typing.
* There are more than 120 references of `FakeToolCallingModel` in the
code where you get ` error: Need type annotation for "model"
[var-annotated]` because mypy can't resolve the generic type (we don't
see them atm because they are in files temporarily excluded from mypy
checking). We would need to explicitly type them to
`FakeToolCallingModel[Any]`
Co-authored-by: Mason Daugherty <mason@langchain.dev>
Appears `override()`'s docstring in `langgraph` already shows
`state=new_state` as a valid usage pattern
Works since `dataclasses.replace()` accepts any field, but the
`TypedDicts` weren't updated to match. Caused mypy to flag legitimate
usage as an error.
description by @mdrxy
- Enable `test_responses_spec.py` integration tests that were previously
skipped at module level
- Widen `ToolStrategy.schema` type annotation from `type[SchemaT]` to
`type[SchemaT] | dict[str, Any]` to match actual supported usage (JSON
schema dicts were already handled at runtime)
- Fix type annotations and linting issues in test file (modernize to
`dict`/`list`, add return types, prefix unused `_request` param)
- Improve generic typing in `load_spec` utility with bounded `TypeVar`
Co-authored-by: Mason Daugherty <mason@langchain.dev>