Commit Graph

1209 Commits

Author SHA1 Message Date
Weichen Zhao
d899681040 feat(core): add XML format option for get_buffer_string (#34802)
## Summary

Add XML format option for `get_buffer_string()` to provide unambiguous
message serialization. This fixes role prefix ambiguity when message
content contains strings like "Human:" or "AI:".

  Fixes #34786

  ## Changes

- Add `format="xml"` parameter with proper XML escaping using
`quoteattr()` for attributes
- Add explicit validation for format parameter (raises `ValueError` for
invalid values)
  - Add comprehensive tests for XML format edge cases

<img width="1952" height="706" alt="image"
src="https://github.com/user-attachments/assets/1cd6f887-9365-43cf-a532-72d7addd8bad"
/>
<img width="2786" height="776" alt="image"
src="https://github.com/user-attachments/assets/a07b0db0-519c-46d7-b34b-b404237d812b"
/>

---------

Co-authored-by: Mason Daugherty <github@mdrxy.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
2026-01-22 13:33:08 -05:00
Mason Daugherty
3899154daf docs(core): enhance docstring for RunnableConfig for clarity on total=False (#34756) 2026-01-14 16:38:33 -05:00
Mason Daugherty
2ff1d23bba docs(core): clean up callbacks param descriptions (#34738)
many were unnecessarily verbose
2026-01-13 10:25:50 -05:00
Mason Daugherty
3289ee20ed fix(core): correctly guard against non-text-block types (#34729)
# 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
2026-01-13 10:11:00 -05:00
David Fernandez
5b401fa414 refactor(core): generalize comma_list utility to support any Iterable (#34714)
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>
2026-01-12 20:26:59 -05:00
skyvanguard
34e867e92b fix(core): add explicit tags parameter to sync LLMManagerMixin methods (#34722)
## 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>
2026-01-12 20:19:05 -05:00
Mason Daugherty
0b99ca4fcd docs(core): enhance docstrings for ToolCall and ToolCallChunk (#34719) 2026-01-12 15:50:28 -05:00
Shreyansh Singh Gautam
2ef23882d2 fix(core): add tool_call_id to on_tool_error event data (#33731)
# 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>
2026-01-10 02:35:13 -05:00
Bhavesh Sharma
e261924030 fix(core): improve error message for missing title in JSON schema functions (#34683)
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>
2026-01-09 23:10:09 -05:00
Krud-x
d22cfaf7c6 fix(core): make yield_keys prefix keyword-only to match BaseStore (#34659)
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>
2026-01-09 23:07:47 -05:00
Christophe Bornet
8cb7dbd37b chore(core): improve types for RunnableLambda (#34539)
Co-authored-by: Mason Daugherty <mason@langchain.dev>
2026-01-09 21:42:27 -05:00
Christophe Bornet
8e3c6b109f style(core): fix some noqa escapes (#34675)
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
2026-01-09 17:36:08 -05:00
Sydney Runkle
fbe9babb34 fix: remove relative imports (#34680)
standardizing on absolute imports rather than relative across the
codebase
2026-01-09 13:00:51 -05:00
Sydney Runkle
c080296bed release: langchain-core 1.2.7 (#34678) 2026-01-09 16:02:38 +00:00
Sydney Runkle
ed2aa9f747 fix: don't trace injected args only found in signature (#34670)
for the case when they're not included in the `args_schema`

this was predicted by @eyurtsev's comment here:
https://github.com/langchain-ai/langchain/pull/33729/files#r2475538173

pairing w/ this PR in mcp adapters:
https://github.com/langchain-ai/langchain-mcp-adapters/pull/407
2026-01-09 09:58:34 -05:00
Aman Gupta
2847814c70 feat(core): add more file extensions to ignore in HTML link extraction (#34552)
# feat(core): add more file extensions to ignore in HTML link extraction

## Description
This PR enhances the HTML link extraction utility in  
`libs/core/langchain_core/utils/html.py` by expanding the
`SUFFIXES_TO_IGNORE` list to include additional common binary file
extensions:

- `.webp`
- `.pdf`
- `.docx`
- `.xlsx`
- `.pptx`
- `.pptm`

These file types are non-HTML, non-crawlable resources. Ignoring them
prevents `find_all_links` and `extract_sub_links` from mistakenly
treating such binary assets as navigable links. This improves link
filtering, reduces unnecessary crawling, and aligns behavior with
typical web scraping expectations.

## Summary of Changes
- **Updated** `libs/core/langchain_core/utils/html.py`: Added `.webp`,
`.pdf`, `.docx`, `.xlsx`, `.pptx`, `.pptm` to `SUFFIXES_TO_IGNORE`.

## Related Issues
N/A

## Verification
- `ruff check libs/core/langchain_core/utils/html.py`: **Passed**  
- `mypy libs/core/langchain_core/utils/html.py`: **Passed**  
- `pytest libs/core/tests/unit_tests/utils/test_html.py`: **Passed** (11
tests)

---------

Co-authored-by: Mason Daugherty <mason@langchain.dev>
2026-01-08 14:40:22 -05:00
Aman Gupta
50c5bb5607 refactor(core): improve docstrings for HTML link extraction utilities (#34550)
# refactor(core): improve docstrings for HTML link extraction utilities

## Description
This PR updates and clarifies the docstrings for `find_all_links` and
`extract_sub_links` in
`libs/core/langchain_core/utils/html.py`.

The previous return-value descriptions were vague (e.g., "all links",
"sub links"). They have now been revised to clearly describe the
behavior and output of each function:

- **find_all_links** → “A list of all links found in the HTML.”
- **extract_sub_links** → “A list of absolute paths to sub links.”

These improvements make the utilities more understandable and
developer-friendly without altering functionality.

## Verification
- `ruff check libs/core/langchain_core/utils/html.py`: **Passed**  
- `pytest libs/core/tests/unit_tests/utils/test_html.py`: **Passed**

## Checklists
- PR title follows the required format: `TYPE(SCOPE): DESCRIPTION`  
- Changes are limited to the `langchain-core` package  
- `make format`, `make lint`, and `make test` pass
2026-01-08 10:21:17 -05:00
Mohammad Mohtashim
e6a9694f5d fix(core): fix strict schema generation for functions with optional args (#34599) 2026-01-07 15:13:18 -05:00
Chris Papademetrious
0c7b7e045d feat(core): support custom message separator in get_buffer_string() (#34569) 2026-01-07 11:46:17 -05:00
Mason Daugherty
557eddfd51 refactor(core): add warning for fallback GPT-2 tokenizer usage (#34621) 2026-01-06 19:11:10 -05:00
Mason Daugherty
8aeff95341 fix(core,langchain): use get_buffer_string for message summarization (#34607)
Fixes #34517

Supersedes #34557, #34570

Fixes token inflation in `SummarizationMiddleware` that caused context
window overflow during summarization.

**Root cause:** When formatting messages for the summary prompt,
`str(messages)` was implicitly called, which includes all Pydantic
metadata fields (`usage_metadata`, `response_metadata`,
`additional_kwargs`, etc.). This caused the stringified representation
to use ~2.5x more tokens than `count_tokens_approximately` estimates.

**Problem:**
- Summarization triggers at 85% of context window based on
`count_tokens_approximately`
- But `str(messages)` in the prompt uses 2.5x more tokens
- Results in `ContextLengthExceeded`

**Fix:** Use `get_buffer_string()` to format messages, which produces
compact output:

```
Human: What's the weather?
AI: Let me check...[tool_calls]
Tool: 72°F and sunny
```

Instead of verbose Pydantic repr:

```python
[HumanMessage(content='What's the weather?', additional_kwargs={}, response_metadata={}), ...]
```
2026-01-06 19:05:03 -05:00
ゆり
be2c7f1aa8 test(core): add tests for formatting utils and merge functions (#34511)
## Summary
Add comprehensive test coverage for previously untested utilities in
`langchain-core`.

## Changes

### New file: `test_formatting.py` (18 tests)

Tests for `StrictFormatter` class:
- `test_vformat_with_keyword_args` - basic functionality
- `test_vformat_with_multiple_keyword_args` - multiple placeholders
- `test_vformat_with_empty_string` - edge case
- `test_vformat_with_no_placeholders` - literal strings
- `test_vformat_raises_on_positional_args` - error handling
- `test_vformat_raises_on_multiple_positional_args` - error handling
- `test_vformat_with_special_characters` - newlines, tabs
- `test_vformat_with_unicode` - emoji, CJK characters
- `test_vformat_with_format_spec` - format specifications
- `test_vformat_with_nested_braces` - escaped braces

Tests for `validate_input_variables`:
- `test_validate_input_variables_success` - valid input
- `test_validate_input_variables_with_extra_variables` - extra vars
allowed
- `test_validate_input_variables_with_missing_variable` - KeyError
- `test_validate_input_variables_empty_format` - edge case
- `test_validate_input_variables_no_placeholders` - edge case

Tests for `formatter` singleton:
- `test_formatter_is_strict_formatter` - type check
- `test_formatter_format_works` - functionality
- `test_formatter_rejects_positional_args` - error handling

### Extended `test_utils.py` (14 new tests)

Tests for `merge_lists`:
- Parametrized tests covering None handling, simple merge, empty lists,
index-based merging
- `test_merge_lists_multiple_others` - merging 3+ lists
- `test_merge_lists_all_none` - all None inputs

Tests for `merge_obj`:
- Parametrized tests for None, strings, dicts, lists, equal values
- `test_merge_obj_type_mismatch` - TypeError on type mismatch
- `test_merge_obj_unmergeable_values` - ValueError on different values
- `test_merge_obj_tuple_raises` - ValueError for tuples

## Test plan
- [x] Tests follow existing patterns in the codebase
- [x] All tests are unit tests (no network calls)
- [x] Tests cover happy paths and error conditions
- [x] Tests verify no mutation of input data

## AI Disclosure
This contribution was developed with AI assistance (Claude Code).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: yurekami <yurekami@users.noreply.github.com>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
2026-01-05 14:20:11 -05:00
aroun-coumar
730a3676f8 fix(core): strip message IDs from cache keys using model_copy (#33915)
**Description:**  

*Closes
#[33883](https://github.com/langchain-ai/langchain/issues/33883)*

Chat model cache keys are generated by serializing messages via
`dumps(messages)`. The optional `BaseMessage.id` field (a UUID used
solely for tracing/threading) is included in this serialization, causing
functionally identical messages to produce different cache keys. This
results in repeated API calls, cache bloat, and degraded performance in
production workloads (e.g., agents, RAG chains, long conversations).

This change normalizes messages **only for cache key generation** by
stripping the nonsemantic `id` field using Pydantic V2’s
`model_copy(update={"id": None})`. The normalization is applied in both
synchronous and asynchronous cache paths (`_generate_with_cache` /
`_agenerate_with_cache`) immediately before `dumps()`.

```python
normalized_messages = [
    msg.model_copy(update={"id": None})
    if getattr(msg, "id", None) is not None
    else msg
    for msg in messages
]
prompt = dumps(normalized_messages)

---------

Co-authored-by: Mason Daugherty <mason@langchain.dev>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
2026-01-05 10:37:10 -05:00
Mohan Kumar S
13cfdf1676 fix(core): exclude injected args from tool schema (#34582) 2026-01-05 09:59:59 -05:00
Andre Roelofs
c25f3847d0 refactor(core): select chunk_id via ranking and remove extra allocation (#34588) 2026-01-05 09:13:05 -05:00
ccurme
659eab2607 release(core): 1.2.6 (#34586) 2026-01-02 16:20:20 -05:00
Angus Jelinek
458a186540 chore(core): Update LangChainTracer to use Pydantic v2 methods (#34541) 2026-01-02 16:02:13 -05:00
weiii668
5517ef37fb docs(core): add docstrings to internal helper functions (#34525)
Co-authored-by: weiii668 <your-email@example.com>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
2025-12-30 21:58:00 -06:00
Mason Daugherty
2bbe4216e0 docs(core): refresh content.py docstrings (#34546)
minor formatting improvements and increased disambiguation between `id`
and `file_id` for `FileContentBlock` in response to
https://github.com/langchain-ai/langchain-google/pull/1477
2025-12-30 20:44:47 -06:00
Christophe Bornet
03ae39747b refactor(core): fix some missing generic types (#31658)
See
https://mypy.readthedocs.io/en/stable/config_file.html#confval-disallow_any_generics

---------

Co-authored-by: Mason Daugherty <mason@langchain.dev>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
2025-12-27 16:53:08 -06:00
Christophe Bornet
5ef9f6e036 style(core): add ruff RUF012 rule (#34492)
Co-authored-by: Mason Daugherty <mason@langchain.dev>
2025-12-27 02:36:28 -06:00
Connor Hyatt
e3939ade5a fix(core): support (message class, template) tuples in ChatPromptTemplate.from_messages (#33989)
### Description

`ChatPromptTemplate.from_messages` supports multiple tuple formats for
defining message templates. One documented format is `(message class,
template)`, which allows users to specify the message type using the
class directly:

```python
ChatPromptTemplate.from_messages([
    (SystemMessage, "You are a helpful assistant named {name}."),
    (HumanMessage, "{input}"),
])
```

However, this syntax was broken. Passing a tuple like `(HumanMessage,
"{input}")` would raise a Pydantic validation error because the
conversion logic in `_convert_to_message_template` didn't handle
`BaseMessage` subclasses—it only recognized string-based role
identifiers like `"human"` or `"system"`.

This PR adds the missing branch to detect when the first element of a
tuple is a message class (by checking for the `type` class attribute)
and routes it through `_create_template_from_message_type`, which
already knows how to create the appropriate `MessagePromptTemplate` for
each message type.

### Changes

- Updated `_convert_to_message_template` to properly support `(message
class, template)` tuples

### Testing

Added 16 comprehensive unit tests covering:

- Basic usage with `HumanMessage`, `AIMessage`, and `SystemMessage`
classes
- Integration with `invoke()` method
- Mixed syntax (message class tuples alongside string tuples)
- Multiple template variables
- Edge cases: empty templates, static text (no variables)
- Correct extraction of `input_variables`
- Partial variables support
- Combination with `MessagesPlaceholder`
- Mustache template format
- Template operations: `append()`, `extend()`, concatenation, and
slicing
- Special characters and unicode in templates

### Issue

Fixes #33791

### Dependencies

None

---------

Co-authored-by: Mason Daugherty <mason@langchain.dev>
2025-12-27 02:20:33 -06:00
gjeltep
ca7790f895 fix(core): fix callback manager merge mixing handlers (#32028) (#33617)
## Description
Fixed `BaseCallbackManager.merge()` method to correctly preserve the
distinction between `handlers` and `inheritable_handlers` during merge
operations.

Previously, the merge method was using `add_handler()` which incorrectly
added handlers to both lists when `inherit=True`, causing
cross-contamination between regular and inheritable handlers.

The fix directly passes the combined handler lists to the constructor
instead of using `add_handler()`, ensuring proper separation is
maintained.

## Issue
Fixes #32028

## Dependencies
None

## Testing
- Modified existing test `test_merge_preserves_handler_distinction()` to
verify handlers remain properly separated after merge

## Checklist
- [x] **Breaking Changes**: No breaking changes - only fixes incorrect
behavior
- [x] **Type Hints**: All functions have complete type annotations
- [x] **Tests**: Fix is fully tested with existing unit test
- [x] **Security**: No security implications
- [x] **Documentation**: No documentation changes needed - bug fix only
- [x] **Code Quality**: Passes lint and format checks
- [x] **Commit Message**: Follows Conventional Commits format

---------

Co-authored-by: Mason Daugherty <mason@langchain.dev>
2025-12-27 02:01:59 -06:00
Christophe Bornet
a92c032ff6 style(core): fix mypy no-any-return violations (#34204)
* 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>
2025-12-26 21:35:27 -06:00
Mason Daugherty
78b2d51edc docs(core): image url docstring enhancement (#34488) 2025-12-25 23:10:48 -06:00
Harikrishna KP
294dda8df2 test(core): URL-encode bgColor parameter in mermaid.ink API calls (#34466)
## Problem

The `draw_mermaid_png()` function fails with HTTP 400 when using named
background colors like `white`. This is because named colors get
prefixed with `!` (e.g., `!white`) but this special character is not
URL-encoded before being added to the API URL.

As reported in #34444, the URL parameter `bgColor=!white` causes
mermaid.ink to return a 400 Bad Request error.

## Solution

URL-encode the `background_color` parameter using `urllib.parse.quote()`
before constructing the API URL. This ensures special characters like
`!` are properly encoded as `%21`.

## Changes

- Added `import urllib.parse` 
- URL-encode `background_color` value with
`urllib.parse.quote(str(background_color), safe="")`
- Added 2 unit tests:
- `test_mermaid_bgcolor_url_encoding`: Verifies named colors are
properly encoded
- `test_mermaid_bgcolor_hex_not_encoded`: Verifies hex colors work
correctly

## Testing

```bash
pytest tests/unit_tests/runnables/test_graph.py::test_mermaid_bgcolor_url_encoding -v
pytest tests/unit_tests/runnables/test_graph.py::test_mermaid_bgcolor_hex_not_encoded -v
```

Both tests pass.

Fixes #34444

---
*This contribution was made with AI assistance (Claude).*

Co-authored-by: Mr-Neutr0n <mrneutron@users.noreply.github.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
2025-12-25 21:41:46 -06:00
Nhan Nguyen
e99ccbc126 fix(core): URL-encode bgColor in mermaid API calls (#34461)
URL-encode the bgColor parameter to fix 400 errors from mermaid.ink API.

The `!` character in `!white` was not encoded, causing API failures.

Fixes #34444
2025-12-25 21:30:09 -06:00
Rudra Tiwari
75e237643a perf(core): move origin type map to module level in function_calling.py (#34481)
Moves `_ORIGIN_MAP` dict from inside `_py_38_safe_origin()` to module
level constant. This avoids dict allocation on every function call,
reducing garbage collection pressure during frequent tool conversions.

The function is called during typed dict to pydantic model conversion
which happens during tool binding and invocation - a hot path in
LangChain.

**Testing:** `make lint` passes

---------

Co-authored-by: Mason Daugherty <mason@langchain.dev>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
2025-12-25 21:29:31 -06:00
Christophe Bornet
1f403cf612 style(core): add ruff rules TC (#34476)
* 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
2025-12-25 21:23:31 -06:00
Rudra Tiwari
451e8496e7 perf(core): precompile hex color regex pattern at module level (#34480)
Moves hex color validation regex from inside
`_render_mermaid_using_api()` to module-level constant
`_HEX_COLOR_PATTERN`. This avoids recompiling the regex on every
function call, improving performance when rendering multiple Mermaid
graphs.


**Testing:**
- `make lint` passes
- `make test` passes

---------

Co-authored-by: Mason Daugherty <github@mdrxy.com>
2025-12-25 21:22:08 -06:00
Mason Daugherty
75b07b3d4e docs(core): update to indicate betas (#34457) 2025-12-22 17:54:37 -06:00
Mason Daugherty
2e0bed6a21 release(core): 1.2.5 (#34456) 2025-12-22 17:37:44 -06:00
ccurme
5ec0fa69de fix(core): serialization patch (#34455)
- `allowed_objects` kwarg in `load`
- escape lc-ser formatted dicts on `dump`
- fix for jinja2

---------

Co-authored-by: Mason Daugherty <github@mdrxy.com>
2025-12-22 17:33:31 -06:00
Christophe Bornet
d3e9c4d29d fix(core): RunnablePick method return types (#34208)
Following https://github.com/langchain-ai/langchain/pull/31321, the
`Output` type of `RunnablePick` is `Any`.
2025-12-19 23:47:46 -06:00
rari404
1cc4dc7cc9 fix(core): preserve Field(description=...) in @tool decorator (#34354)
## Summary

Fixes #34247

When using `Annotated[type, Field(description="...")]` syntax with the
`@tool` decorator, field descriptions were being lost during schema
generation. The `_get_annotation_description()` function only checked
for string annotations but not for Pydantic `FieldInfo` objects.

## Changes

- Extended `_get_annotation_description()` to also extract descriptions
from `FieldInfo` objects within `Annotated` types
- Added import for `pydantic.fields.FieldInfo`
- Added unit test to verify `Field(description=...)` is preserved

## Why this approach

The fix is minimal and targeted - it extends the existing description
extraction logic rather than restructuring the schema generation. This
maintains backward compatibility while supporting both annotation
styles:

```python
# Both now work correctly:
topic: Annotated[str, "The research topic"]           # existing
topic: Annotated[str, Field(description="...")]       # now fixed
```

## Known limitation

This fix only handles `pydantic.fields.FieldInfo` (Pydantic v2). The v1
compatibility layer (`pydantic.v1.fields.FieldInfo`) is a different
class and will not have descriptions extracted. This is intentional:

- Pydantic v1 is deprecated; users should migrate to v2
- The v1 compat layer exists for legacy model migration, not new tool
definitions
- Duck-typing on `description` attribute could match unintended objects

If v1 `Field` support is needed, it can be addressed in a follow-up PR
with explicit handling.

## Testing

- Added `test_tool_field_description_preserved()` covering required and
optional params
- Verified existing `test_tool_annotated_descriptions` still passes
- Lint and type checks pass

---

> [!NOTE]
> This PR was developed with AI agent assistance (Factory/Droid).

---------

Co-authored-by: Mason Daugherty <github@mdrxy.com>
2025-12-19 23:14:23 -06:00
Nhan Nguyen
398c067f30 fix(core): populate default args from tool's args_schema (#34399)
## Summary
- Fixes issue where Pydantic default values from `args_schema` were not
passed to tool functions when the caller omits optional arguments
- Modified `_parse_input()` in `libs/core/langchain_core/tools/base.py`
to include fields with non-None defaults
- Added unit tests to verify default args behavior for both sync and
async tools

## Problem
When a tool has an `args_schema` with default values:
```python
class SearchArgs(BaseModel):
    query: str = Field(..., description="Search query")
    page: int = Field(default=1, description="Page number")
    size: int = Field(default=10, description="Results per page")

@tool("search", args_schema=SearchArgs)
def search_tool(query: str, page: int, size: int) -> str:
    return f"query={query}, page={page}, size={size}"

# This threw: TypeError: search_tool() missing 2 required positional arguments
search_tool.invoke({"query": "test"})
```

The defaults from `args_schema` were being discarded because
`_parse_input()` filtered validated results to only include keys from
the original input.

## Solution
Changed the filtering logic to:
1. Include all fields that were in the original input (validated)
2. Also include fields with non-None defaults from the Pydantic schema

This applies user-defined defaults (like `Field(default=1)`) while
excluding synthetic fields from `*args`/`**kwargs` which have
`default=None`.

## Test plan
- [x] Added `test_tool_args_schema_default_values` - tests sync tool
with defaults
- [x] Added `test_tool_args_schema_default_values_async` - tests async
tool with defaults
- [x] All existing tests pass (150 passed, 4 skipped)
- [x] Lint passes

Fixes #34384

---------

Co-authored-by: Mason Daugherty <mason@langchain.dev>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
2025-12-19 23:14:13 -06:00
rari404
d84eef667a fix(core): use tool_calls instead of deprecated function_call in get_buffer_string (#34355)
## Summary

Fixes #33970

`get_buffer_string` was only checking for the deprecated `function_call`
field in `additional_kwargs`, which modern LLM providers no longer
return. This fix updates the function to check for the modern
`tool_calls` field first, falling back to `function_call` for legacy
compatibility.

## Changes

- Check `AIMessage.tool_calls` first (modern standard)
- Fall back to `additional_kwargs["function_call"]` (legacy support)
- Added 3 unit tests covering tool_calls, empty content, and precedence
behavior

## Testing

```python
# Before fix: tool_calls info was lost
msg = AIMessage(content="Hi", tool_calls=[{"name": "search", ...}])
get_buffer_string([msg])  # "AI: Hi" (no tool info)

# After fix: tool_calls are included
get_buffer_string([msg])  # "AI: Hi[{\"name\": \"search\", ...}]"
```

- All existing `get_buffer_string` tests pass
- Legacy `function_call` behavior preserved

---

> [!NOTE]
> This PR was developed with AI agent assistance (Factory/Droid).

---------

Co-authored-by: Mason Daugherty <github@mdrxy.com>
2025-12-19 22:37:56 -06:00
Mason Daugherty
85c401f648 feat(core): add PEP 702 __deprecated__ attribute support to @deprecated (#34257)
Adds [PEP 702](https://peps.python.org/pep-0702/) `__deprecated__`
attribute support to the `@deprecated` decorator, enabling IDE and type
checker integration for deprecation warnings.

---

PEP 702 introduced the `__deprecated__` attribute convention, which type
checkers (Pyright, mypy) and IDEs (VS Code with Pylance, PyCharm) can
use to surface deprecations directly in the editor. This PR sets
`__deprecated__` on all objects decorated with `@deprecated`.

With this change, developers using supported IDEs will see:

- **Strikethrough text** on deprecated symbols
- **Hover messages** showing the deprecation reason and suggested
alternative
- **Diagnostic warnings** during type checking (e.g., `pyright`, `mypy`)

### References

- [PEP 702 – Marking deprecations using the type
system](https://peps.python.org/pep-0702/)
- [`typing.deprecated`
specification](https://typing.python.org/en/latest/spec/directives.html#deprecated)
2025-12-19 21:07:37 -06:00
Mason Daugherty
04ec6cacaf fix(core): ensure tool_call_count is never null (#34431)
add truthiness check to guard against `None`
2025-12-19 21:04:01 -06:00
Mason Daugherty
ed9bd6e3ad feat(core): automatically count and store meta for tool call count (#33756)
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.
2025-12-19 20:41:57 -06:00