-
+ return (
+
+
-
- {`pip install -qU ${selectedOption.packageName}`}
-
-
- {apiKeyText ? apiKeyText + "\n\n" + selectedOption.text : selectedOption.text}
-
-
-);
+
+ {`pip install -qU ${selectedOption.packageName}`}
+
+
+ {apiKeyText ? apiKeyText + "\n\n" + selectedOption.text : selectedOption.text}
+
+
+ );
}
diff --git a/libs/core/langchain_core/language_models/chat_models.py b/libs/core/langchain_core/language_models/chat_models.py
index d37ca232052..c5e7b43cebf 100644
--- a/libs/core/langchain_core/language_models/chat_models.py
+++ b/libs/core/langchain_core/language_models/chat_models.py
@@ -11,22 +11,9 @@ from abc import ABC, abstractmethod
from collections.abc import AsyncIterator, Iterator, Sequence
from functools import cached_property
from operator import itemgetter
-from typing import (
- TYPE_CHECKING,
- Any,
- Callable,
- Literal,
- Optional,
- Union,
- cast,
-)
+from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Union, cast
-from pydantic import (
- BaseModel,
- ConfigDict,
- Field,
- model_validator,
-)
+from pydantic import BaseModel, ConfigDict, Field, model_validator
from typing_extensions import override
from langchain_core._api import deprecated
@@ -63,6 +50,7 @@ from langchain_core.outputs import (
ChatGeneration,
ChatGenerationChunk,
ChatResult,
+ Generation,
LLMResult,
RunInfo,
)
@@ -653,6 +641,34 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
def _combine_llm_outputs(self, llm_outputs: list[Optional[dict]]) -> dict: # noqa: ARG002
return {}
+ def _convert_cached_generations(self, cache_val: list) -> list[ChatGeneration]:
+ """Convert cached Generation objects to ChatGeneration objects.
+
+ Handle case where cache contains Generation objects instead of
+ ChatGeneration objects. This can happen due to serialization/deserialization
+ issues or legacy cache data (see #22389).
+
+ Args:
+ cache_val: List of cached generation objects.
+
+ Returns:
+ List of ChatGeneration objects.
+ """
+ converted_generations = []
+ for gen in cache_val:
+ if isinstance(gen, Generation) and not isinstance(gen, ChatGeneration):
+ # Convert Generation to ChatGeneration by creating AIMessage
+ # from the text content
+ chat_gen = ChatGeneration(
+ message=AIMessage(content=gen.text),
+ generation_info=gen.generation_info,
+ )
+ converted_generations.append(chat_gen)
+ else:
+ # Already a ChatGeneration or other expected type
+ converted_generations.append(gen)
+ return converted_generations
+
def _get_invocation_params(
self,
stop: Optional[list[str]] = None,
@@ -1010,7 +1026,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
prompt = dumps(messages)
cache_val = llm_cache.lookup(prompt, llm_string)
if isinstance(cache_val, list):
- return ChatResult(generations=cache_val)
+ converted_generations = self._convert_cached_generations(cache_val)
+ return ChatResult(generations=converted_generations)
elif self.cache is None:
pass
else:
@@ -1082,7 +1099,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
prompt = dumps(messages)
cache_val = await llm_cache.alookup(prompt, llm_string)
if isinstance(cache_val, list):
- return ChatResult(generations=cache_val)
+ converted_generations = self._convert_cached_generations(cache_val)
+ return ChatResult(generations=converted_generations)
elif self.cache is None:
pass
else:
diff --git a/libs/core/langchain_core/runnables/base.py b/libs/core/langchain_core/runnables/base.py
index 5061cd3c5d0..fd66cf6dad5 100644
--- a/libs/core/langchain_core/runnables/base.py
+++ b/libs/core/langchain_core/runnables/base.py
@@ -97,10 +97,7 @@ if TYPE_CHECKING:
from langchain_core.runnables.retry import ExponentialJitterParams
from langchain_core.runnables.schema import StreamEvent
from langchain_core.tools import BaseTool
- from langchain_core.tracers.log_stream import (
- RunLog,
- RunLogPatch,
- )
+ from langchain_core.tracers.log_stream import RunLog, RunLogPatch
from langchain_core.tracers.root_listeners import AsyncListener
from langchain_core.tracers.schemas import Run
@@ -2570,7 +2567,7 @@ class RunnableSerializable(Serializable, Runnable[Input, Output]):
from langchain_openai import ChatOpenAI
model = ChatAnthropic(
- model_name="claude-3-sonnet-20240229"
+ model_name="claude-3-7-sonnet-20250219"
).configurable_alternatives(
ConfigurableField(id="llm"),
default_key="anthropic",
diff --git a/libs/core/langchain_core/runnables/fallbacks.py b/libs/core/langchain_core/runnables/fallbacks.py
index a17552f23d0..dd9a9fdf390 100644
--- a/libs/core/langchain_core/runnables/fallbacks.py
+++ b/libs/core/langchain_core/runnables/fallbacks.py
@@ -5,12 +5,7 @@ import inspect
import typing
from collections.abc import AsyncIterator, Iterator, Sequence
from functools import wraps
-from typing import (
- TYPE_CHECKING,
- Any,
- Optional,
- Union,
-)
+from typing import TYPE_CHECKING, Any, Optional, Union
from pydantic import BaseModel, ConfigDict
from typing_extensions import override
@@ -599,7 +594,7 @@ class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
from langchain_anthropic import ChatAnthropic
gpt_4o = ChatOpenAI(model="gpt-4o")
- claude_3_sonnet = ChatAnthropic(model="claude-3-sonnet-20240229")
+ claude_3_sonnet = ChatAnthropic(model="claude-3-7-sonnet-20250219")
llm = gpt_4o.with_fallbacks([claude_3_sonnet])
llm.model_name
diff --git a/libs/core/tests/unit_tests/language_models/chat_models/test_cache.py b/libs/core/tests/unit_tests/language_models/chat_models/test_cache.py
index 1cceb0a146b..f4f033d3d7f 100644
--- a/libs/core/tests/unit_tests/language_models/chat_models/test_cache.py
+++ b/libs/core/tests/unit_tests/language_models/chat_models/test_cache.py
@@ -13,7 +13,8 @@ from langchain_core.language_models.fake_chat_models import (
GenericFakeChatModel,
)
from langchain_core.messages import AIMessage
-from langchain_core.outputs import ChatGeneration
+from langchain_core.outputs import ChatGeneration, Generation
+from langchain_core.outputs.chat_result import ChatResult
class InMemoryCache(BaseCache):
@@ -305,6 +306,93 @@ def test_llm_representation_for_serializable() -> None:
)
+def test_cache_with_generation_objects() -> None:
+ """Test that cache can handle Generation objects instead of ChatGeneration objects.
+
+ This test reproduces a bug where cache returns Generation objects
+ but ChatResult expects ChatGeneration objects, causing validation errors.
+
+ See #22389 for more info.
+
+ """
+ cache = InMemoryCache()
+
+ # Create a simple fake chat model that we can control
+ from langchain_core.messages import AIMessage
+
+ class SimpleFakeChat:
+ """Simple fake chat model for testing."""
+
+ def __init__(self, cache: BaseCache) -> None:
+ self.cache = cache
+ self.response = "hello"
+
+ def _get_llm_string(self) -> str:
+ return "test_llm_string"
+
+ def generate_response(self, prompt: str) -> ChatResult:
+ """Simulate the cache lookup and generation logic."""
+ from langchain_core.load import dumps
+
+ llm_string = self._get_llm_string()
+ prompt_str = dumps([prompt])
+
+ # Check cache first
+ cache_val = self.cache.lookup(prompt_str, llm_string)
+ if cache_val:
+ # This is where our fix should work
+ converted_generations = []
+ for gen in cache_val:
+ if isinstance(gen, Generation) and not isinstance(
+ gen, ChatGeneration
+ ):
+ # Convert Generation to ChatGeneration by creating an AIMessage
+ chat_gen = ChatGeneration(
+ message=AIMessage(content=gen.text),
+ generation_info=gen.generation_info,
+ )
+ converted_generations.append(chat_gen)
+ else:
+ converted_generations.append(gen)
+ return ChatResult(generations=converted_generations)
+
+ # Generate new response
+ chat_gen = ChatGeneration(
+ message=AIMessage(content=self.response), generation_info={}
+ )
+ result = ChatResult(generations=[chat_gen])
+
+ # Store in cache
+ self.cache.update(prompt_str, llm_string, result.generations)
+ return result
+
+ model = SimpleFakeChat(cache)
+
+ # First call - normal operation
+ result1 = model.generate_response("test prompt")
+ assert result1.generations[0].message.content == "hello"
+
+ # Manually corrupt the cache by replacing ChatGeneration with Generation
+ cache_key = next(iter(cache._cache.keys()))
+ cached_chat_generations = cache._cache[cache_key]
+
+ # Replace with Generation objects (missing message field)
+ corrupted_generations = [
+ Generation(
+ text=gen.text,
+ generation_info=gen.generation_info,
+ type="Generation", # This is the key - wrong type
+ )
+ for gen in cached_chat_generations
+ ]
+ cache._cache[cache_key] = corrupted_generations
+
+ # Second call should handle the Generation objects gracefully
+ result2 = model.generate_response("test prompt")
+ assert result2.generations[0].message.content == "hello"
+ assert isinstance(result2.generations[0], ChatGeneration)
+
+
def test_cleanup_serialized() -> None:
cleanup_serialized = {
"lc": 1,
diff --git a/libs/core/tests/unit_tests/test_tools.py b/libs/core/tests/unit_tests/test_tools.py
index 748523e5ef9..57b4573d70d 100644
--- a/libs/core/tests/unit_tests/test_tools.py
+++ b/libs/core/tests/unit_tests/test_tools.py
@@ -2586,6 +2586,18 @@ def test_title_property_preserved() -> None:
}
+def test_nested_pydantic_fields() -> None:
+ class Address(BaseModel):
+ street: str
+
+ class Person(BaseModel):
+ name: str
+ address: Address = Field(description="Home address")
+
+ result = convert_to_openai_tool(Person)
+ assert len(result["function"]["parameters"]["properties"]) == 2
+
+
async def test_tool_ainvoke_does_not_mutate_inputs() -> None:
"""Verify that the inputs are not mutated when invoking a tool asynchronously."""
diff --git a/libs/langchain/langchain/chat_models/base.py b/libs/langchain/langchain/chat_models/base.py
index dda881bf1f4..2f8b46bcb59 100644
--- a/libs/langchain/langchain/chat_models/base.py
+++ b/libs/langchain/langchain/chat_models/base.py
@@ -3,15 +3,7 @@ from __future__ import annotations
import warnings
from collections.abc import AsyncIterator, Iterator, Sequence
from importlib import util
-from typing import (
- Any,
- Callable,
- Literal,
- Optional,
- Union,
- cast,
- overload,
-)
+from typing import Any, Callable, Literal, Optional, Union, cast, overload
from langchain_core.language_models import (
BaseChatModel,
@@ -188,7 +180,7 @@ def init_chat_model(
o3_mini = init_chat_model("openai:o3-mini", temperature=0)
claude_sonnet = init_chat_model("anthropic:claude-3-5-sonnet-latest", temperature=0)
- gemini_2_flash = init_chat_model("google_vertexai:gemini-2.0-flash", temperature=0)
+ gemini_2_flash = init_chat_model("google_vertexai:gemini-2.5-flash", temperature=0)
o3_mini.invoke("what's your name")
claude_sonnet.invoke("what's your name")
diff --git a/libs/langchain/tests/integration_tests/chat_models/test_base.py b/libs/langchain/tests/integration_tests/chat_models/test_base.py
index 4d87b98dfaf..05d3e009387 100644
--- a/libs/langchain/tests/integration_tests/chat_models/test_base.py
+++ b/libs/langchain/tests/integration_tests/chat_models/test_base.py
@@ -25,7 +25,7 @@ async def test_init_chat_model_chain() -> None:
model_with_config = model_with_tools.with_config(
RunnableConfig(tags=["foo"]),
- configurable={"bar_model": "claude-3-sonnet-20240229"},
+ configurable={"bar_model": "claude-3-7-sonnet-20250219"},
)
prompt = ChatPromptTemplate.from_messages([("system", "foo"), ("human", "{input}")])
chain = prompt | model_with_config
diff --git a/libs/langchain/tests/unit_tests/chat_models/test_base.py b/libs/langchain/tests/unit_tests/chat_models/test_base.py
index 18279c95e67..8cd5e0631b8 100644
--- a/libs/langchain/tests/unit_tests/chat_models/test_base.py
+++ b/libs/langchain/tests/unit_tests/chat_models/test_base.py
@@ -196,17 +196,17 @@ def test_configurable_with_default() -> None:
model_with_config = model_with_tools.with_config(
RunnableConfig(tags=["foo"]),
- configurable={"bar_model": "claude-3-sonnet-20240229"},
+ configurable={"bar_model": "claude-3-7-sonnet-20250219"},
)
- assert model_with_config.model == "claude-3-sonnet-20240229" # type: ignore[attr-defined]
+ assert model_with_config.model == "claude-3-7-sonnet-20250219" # type: ignore[attr-defined]
assert model_with_config.model_dump() == { # type: ignore[attr-defined]
"name": None,
"bound": {
"name": None,
"disable_streaming": False,
- "model": "claude-3-sonnet-20240229",
+ "model": "claude-3-7-sonnet-20250219",
"mcp_servers": None,
"max_tokens": 1024,
"temperature": None,
diff --git a/libs/langchain_v1/langchain/chat_models/base.py b/libs/langchain_v1/langchain/chat_models/base.py
index 80dea9b9d34..6a093e0cdff 100644
--- a/libs/langchain_v1/langchain/chat_models/base.py
+++ b/libs/langchain_v1/langchain/chat_models/base.py
@@ -13,10 +13,7 @@ from typing import (
overload,
)
-from langchain_core.language_models import (
- BaseChatModel,
- LanguageModelInput,
-)
+from langchain_core.language_models import BaseChatModel, LanguageModelInput
from langchain_core.messages import AnyMessage, BaseMessage
from langchain_core.runnables import Runnable, RunnableConfig, ensure_config
from typing_extensions import TypeAlias, override
@@ -177,7 +174,7 @@ def init_chat_model(
o3_mini = init_chat_model("openai:o3-mini", temperature=0)
claude_sonnet = init_chat_model("anthropic:claude-3-5-sonnet-latest", temperature=0)
- gemini_2_flash = init_chat_model("google_vertexai:gemini-2.0-flash", temperature=0)
+ gemini_2_flash = init_chat_model("google_vertexai:gemini-2.5-flash", temperature=0)
o3_mini.invoke("what's your name")
claude_sonnet.invoke("what's your name")
diff --git a/libs/langchain_v1/tests/integration_tests/chat_models/test_base.py b/libs/langchain_v1/tests/integration_tests/chat_models/test_base.py
index 4d87b98dfaf..05d3e009387 100644
--- a/libs/langchain_v1/tests/integration_tests/chat_models/test_base.py
+++ b/libs/langchain_v1/tests/integration_tests/chat_models/test_base.py
@@ -25,7 +25,7 @@ async def test_init_chat_model_chain() -> None:
model_with_config = model_with_tools.with_config(
RunnableConfig(tags=["foo"]),
- configurable={"bar_model": "claude-3-sonnet-20240229"},
+ configurable={"bar_model": "claude-3-7-sonnet-20250219"},
)
prompt = ChatPromptTemplate.from_messages([("system", "foo"), ("human", "{input}")])
chain = prompt | model_with_config
diff --git a/libs/langchain_v1/tests/unit_tests/chat_models/test_chat_models.py b/libs/langchain_v1/tests/unit_tests/chat_models/test_chat_models.py
index b3d6659d9d7..147d7813f89 100644
--- a/libs/langchain_v1/tests/unit_tests/chat_models/test_chat_models.py
+++ b/libs/langchain_v1/tests/unit_tests/chat_models/test_chat_models.py
@@ -196,17 +196,17 @@ def test_configurable_with_default() -> None:
model_with_config = model_with_tools.with_config(
RunnableConfig(tags=["foo"]),
- configurable={"bar_model": "claude-3-sonnet-20240229"},
+ configurable={"bar_model": "claude-3-7-sonnet-20250219"},
)
- assert model_with_config.model == "claude-3-sonnet-20240229" # type: ignore[attr-defined]
+ assert model_with_config.model == "claude-3-7-sonnet-20250219" # type: ignore[attr-defined]
assert model_with_config.model_dump() == { # type: ignore[attr-defined]
"name": None,
"bound": {
"name": None,
"disable_streaming": False,
- "model": "claude-3-sonnet-20240229",
+ "model": "claude-3-7-sonnet-20250219",
"mcp_servers": None,
"max_tokens": 1024,
"temperature": None,
diff --git a/libs/partners/anthropic/langchain_anthropic/chat_models.py b/libs/partners/anthropic/langchain_anthropic/chat_models.py
index 41b51322086..e32c1279261 100644
--- a/libs/partners/anthropic/langchain_anthropic/chat_models.py
+++ b/libs/partners/anthropic/langchain_anthropic/chat_models.py
@@ -7,14 +7,7 @@ import warnings
from collections.abc import AsyncIterator, Iterator, Mapping, Sequence
from functools import cached_property
from operator import itemgetter
-from typing import (
- Any,
- Callable,
- Literal,
- Optional,
- Union,
- cast,
-)
+from typing import Any, Callable, Literal, Optional, Union, cast
import anthropic
from langchain_core._api import beta, deprecated
@@ -42,33 +35,16 @@ from langchain_core.messages import (
)
from langchain_core.messages.ai import InputTokenDetails, UsageMetadata
from langchain_core.messages.tool import tool_call_chunk as create_tool_call_chunk
-from langchain_core.output_parsers import (
- JsonOutputKeyToolsParser,
- PydanticToolsParser,
-)
+from langchain_core.output_parsers import JsonOutputKeyToolsParser, PydanticToolsParser
from langchain_core.output_parsers.base import OutputParserLike
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
-from langchain_core.runnables import (
- Runnable,
- RunnableMap,
- RunnablePassthrough,
-)
+from langchain_core.runnables import Runnable, RunnableMap, RunnablePassthrough
from langchain_core.tools import BaseTool
-from langchain_core.utils import (
- from_env,
- get_pydantic_field_names,
- secret_from_env,
-)
+from langchain_core.utils import from_env, get_pydantic_field_names, secret_from_env
from langchain_core.utils.function_calling import convert_to_openai_tool
from langchain_core.utils.pydantic import is_basemodel_subclass
from langchain_core.utils.utils import _build_model_kwargs
-from pydantic import (
- BaseModel,
- ConfigDict,
- Field,
- SecretStr,
- model_validator,
-)
+from pydantic import BaseModel, ConfigDict, Field, SecretStr, model_validator
from typing_extensions import NotRequired, TypedDict
from langchain_anthropic._client_utils import (
@@ -517,7 +493,7 @@ class ChatAnthropic(BaseChatModel):
Key init args — completion params:
model: str
- Name of Anthropic model to use. e.g. ``'claude-3-sonnet-20240229'``.
+ Name of Anthropic model to use. e.g. ``'claude-3-7-sonnet-20250219'``.
temperature: float
Sampling temperature. Ranges from ``0.0`` to ``1.0``.
max_tokens: int
@@ -543,7 +519,7 @@ class ChatAnthropic(BaseChatModel):
from langchain_anthropic import ChatAnthropic
llm = ChatAnthropic(
- model="claude-3-sonnet-20240229",
+ model="claude-3-7-sonnet-20250219",
temperature=0,
max_tokens=1024,
timeout=None,
@@ -583,7 +559,7 @@ class ChatAnthropic(BaseChatModel):
.. code-block:: python
- AIMessage(content="J'aime la programmation.", response_metadata={'id': 'msg_01Trik66aiQ9Z1higrD5XFx3', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 25, 'output_tokens': 11}}, id='run-5886ac5f-3c2e-49f5-8a44-b1e92808c929-0', usage_metadata={'input_tokens': 25, 'output_tokens': 11, 'total_tokens': 36})
+ AIMessage(content="J'aime la programmation.", response_metadata={'id': 'msg_01Trik66aiQ9Z1higrD5XFx3', 'model': 'claude-3-7-sonnet-20250219', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 25, 'output_tokens': 11}}, id='run-5886ac5f-3c2e-49f5-8a44-b1e92808c929-0', usage_metadata={'input_tokens': 25, 'output_tokens': 11, 'total_tokens': 36})
Stream:
.. code-block:: python
@@ -627,7 +603,7 @@ class ChatAnthropic(BaseChatModel):
.. code-block:: python
- AIMessage(content="J'aime la programmation.", response_metadata={'id': 'msg_01Trik66aiQ9Z1higrD5XFx3', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 25, 'output_tokens': 11}}, id='run-5886ac5f-3c2e-49f5-8a44-b1e92808c929-0', usage_metadata={'input_tokens': 25, 'output_tokens': 11, 'total_tokens': 36})
+ AIMessage(content="J'aime la programmation.", response_metadata={'id': 'msg_01Trik66aiQ9Z1higrD5XFx3', 'model': 'claude-3-7-sonnet-20250219', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 25, 'output_tokens': 11}}, id='run-5886ac5f-3c2e-49f5-8a44-b1e92808c929-0', usage_metadata={'input_tokens': 25, 'output_tokens': 11, 'total_tokens': 36})
Tool calling:
.. code-block:: python
@@ -1156,7 +1132,7 @@ class ChatAnthropic(BaseChatModel):
.. code-block:: python
{'id': 'msg_013xU6FHEGEq76aP4RgFerVT',
- 'model': 'claude-3-sonnet-20240229',
+ 'model': 'claude-3-7-sonnet-20250219',
'stop_reason': 'end_turn',
'stop_sequence': None,
'usage': {'input_tokens': 25, 'output_tokens': 11}}
diff --git a/libs/partners/anthropic/langchain_anthropic/llms.py b/libs/partners/anthropic/langchain_anthropic/llms.py
index 0ad6d247fed..85ac112d017 100644
--- a/libs/partners/anthropic/langchain_anthropic/llms.py
+++ b/libs/partners/anthropic/langchain_anthropic/llms.py
@@ -3,11 +3,7 @@ from __future__ import annotations
import re
import warnings
from collections.abc import AsyncIterator, Iterator, Mapping
-from typing import (
- Any,
- Callable,
- Optional,
-)
+from typing import Any, Callable, Optional
import anthropic
from langchain_core._api.deprecation import deprecated
@@ -19,14 +15,8 @@ from langchain_core.language_models import BaseLanguageModel, LangSmithParams
from langchain_core.language_models.llms import LLM
from langchain_core.outputs import GenerationChunk
from langchain_core.prompt_values import PromptValue
-from langchain_core.utils import (
- get_pydantic_field_names,
-)
-from langchain_core.utils.utils import (
- _build_model_kwargs,
- from_env,
- secret_from_env,
-)
+from langchain_core.utils import get_pydantic_field_names
+from langchain_core.utils.utils import _build_model_kwargs, from_env, secret_from_env
from pydantic import ConfigDict, Field, SecretStr, model_validator
from typing_extensions import Self
@@ -34,10 +24,10 @@ from typing_extensions import Self
class _AnthropicCommon(BaseLanguageModel):
client: Any = None #: :meta private:
async_client: Any = None #: :meta private:
- model: str = Field(default="claude-2", alias="model_name")
+ model: str = Field(default="claude-3-5-sonnet-latest", alias="model_name")
"""Model name to use."""
- max_tokens_to_sample: int = Field(default=1024, alias="max_tokens")
+ max_tokens: int = Field(default=1024, alias="max_tokens_to_sample")
"""Denotes the number of tokens to predict per generation."""
temperature: Optional[float] = None
@@ -104,15 +94,16 @@ class _AnthropicCommon(BaseLanguageModel):
timeout=self.default_request_timeout,
max_retries=self.max_retries,
)
- self.HUMAN_PROMPT = anthropic.HUMAN_PROMPT
- self.AI_PROMPT = anthropic.AI_PROMPT
+ # Keep for backward compatibility but not used in Messages API
+ self.HUMAN_PROMPT = getattr(anthropic, "HUMAN_PROMPT", None)
+ self.AI_PROMPT = getattr(anthropic, "AI_PROMPT", None)
return self
@property
def _default_params(self) -> Mapping[str, Any]:
"""Get the default parameters for calling Anthropic API."""
d = {
- "max_tokens_to_sample": self.max_tokens_to_sample,
+ "max_tokens": self.max_tokens,
"model": self.model,
}
if self.temperature is not None:
@@ -129,16 +120,8 @@ class _AnthropicCommon(BaseLanguageModel):
return {**self._default_params}
def _get_anthropic_stop(self, stop: Optional[list[str]] = None) -> list[str]:
- if not self.HUMAN_PROMPT or not self.AI_PROMPT:
- msg = "Please ensure the anthropic package is loaded"
- raise NameError(msg)
-
if stop is None:
stop = []
-
- # Never want model to invent new turns of Human / Assistant dialog.
- stop.extend([self.HUMAN_PROMPT])
-
return stop
@@ -192,7 +175,7 @@ class AnthropicLLM(LLM, _AnthropicCommon):
"""Get the identifying parameters."""
return {
"model": self.model,
- "max_tokens": self.max_tokens_to_sample,
+ "max_tokens": self.max_tokens,
"temperature": self.temperature,
"top_k": self.top_k,
"top_p": self.top_p,
@@ -211,27 +194,51 @@ class AnthropicLLM(LLM, _AnthropicCommon):
params = super()._get_ls_params(stop=stop, **kwargs)
identifying_params = self._identifying_params
if max_tokens := kwargs.get(
- "max_tokens_to_sample",
+ "max_tokens",
identifying_params.get("max_tokens"),
):
params["ls_max_tokens"] = max_tokens
return params
- def _wrap_prompt(self, prompt: str) -> str:
- if not self.HUMAN_PROMPT or not self.AI_PROMPT:
- msg = "Please ensure the anthropic package is loaded"
- raise NameError(msg)
+ def _format_messages(self, prompt: str) -> list[dict[str, str]]:
+ """Convert prompt to Messages API format."""
+ messages = []
- if prompt.startswith(self.HUMAN_PROMPT):
- return prompt # Already wrapped.
+ # Handle legacy prompts that might have HUMAN_PROMPT/AI_PROMPT markers
+ if self.HUMAN_PROMPT and self.HUMAN_PROMPT in prompt:
+ # Split on human/assistant turns
+ parts = prompt.split(self.HUMAN_PROMPT)
- # Guard against common errors in specifying wrong number of newlines.
- corrected_prompt, n_subs = re.subn(r"^\n*Human:", self.HUMAN_PROMPT, prompt)
- if n_subs == 1:
- return corrected_prompt
+ for _, part in enumerate(parts):
+ if not part.strip():
+ continue
- # As a last resort, wrap the prompt ourselves to emulate instruct-style.
- return f"{self.HUMAN_PROMPT} {prompt}{self.AI_PROMPT} Sure, here you go:\n"
+ if self.AI_PROMPT and self.AI_PROMPT in part:
+ # Split human and assistant parts
+ human_part, assistant_part = part.split(self.AI_PROMPT, 1)
+ if human_part.strip():
+ messages.append({"role": "user", "content": human_part.strip()})
+ if assistant_part.strip():
+ messages.append(
+ {"role": "assistant", "content": assistant_part.strip()}
+ )
+ else:
+ # Just human content
+ if part.strip():
+ messages.append({"role": "user", "content": part.strip()})
+ else:
+ # Handle modern format or plain text
+ # Clean prompt for Messages API
+ content = re.sub(r"^\n*Human:\s*", "", prompt)
+ content = re.sub(r"\n*Assistant:\s*.*$", "", content)
+ if content.strip():
+ messages.append({"role": "user", "content": content.strip()})
+
+ # Ensure we have at least one message
+ if not messages:
+ messages = [{"role": "user", "content": prompt.strip() or "Hello"}]
+
+ return messages
def _call(
self,
@@ -272,15 +279,19 @@ class AnthropicLLM(LLM, _AnthropicCommon):
stop = self._get_anthropic_stop(stop)
params = {**self._default_params, **kwargs}
- response = self.client.completions.create(
- prompt=self._wrap_prompt(prompt),
- stop_sequences=stop,
+
+ # Remove parameters not supported by Messages API
+ params = {k: v for k, v in params.items() if k != "max_tokens_to_sample"}
+
+ response = self.client.messages.create(
+ messages=self._format_messages(prompt),
+ stop_sequences=stop if stop else None,
**params,
)
- return response.completion
+ return response.content[0].text
def convert_prompt(self, prompt: PromptValue) -> str:
- return self._wrap_prompt(prompt.to_string())
+ return prompt.to_string()
async def _acall(
self,
@@ -304,12 +315,15 @@ class AnthropicLLM(LLM, _AnthropicCommon):
stop = self._get_anthropic_stop(stop)
params = {**self._default_params, **kwargs}
- response = await self.async_client.completions.create(
- prompt=self._wrap_prompt(prompt),
- stop_sequences=stop,
+ # Remove parameters not supported by Messages API
+ params = {k: v for k, v in params.items() if k != "max_tokens_to_sample"}
+
+ response = await self.async_client.messages.create(
+ messages=self._format_messages(prompt),
+ stop_sequences=stop if stop else None,
**params,
)
- return response.completion
+ return response.content[0].text
def _stream(
self,
@@ -343,17 +357,20 @@ class AnthropicLLM(LLM, _AnthropicCommon):
stop = self._get_anthropic_stop(stop)
params = {**self._default_params, **kwargs}
- for token in self.client.completions.create(
- prompt=self._wrap_prompt(prompt),
- stop_sequences=stop,
- stream=True,
- **params,
- ):
- chunk = GenerationChunk(text=token.completion)
+ # Remove parameters not supported by Messages API
+ params = {k: v for k, v in params.items() if k != "max_tokens_to_sample"}
- if run_manager:
- run_manager.on_llm_new_token(chunk.text, chunk=chunk)
- yield chunk
+ with self.client.messages.stream(
+ messages=self._format_messages(prompt),
+ stop_sequences=stop if stop else None,
+ **params,
+ ) as stream:
+ for event in stream:
+ if event.type == "content_block_delta" and hasattr(event.delta, "text"):
+ chunk = GenerationChunk(text=event.delta.text)
+ if run_manager:
+ run_manager.on_llm_new_token(chunk.text, chunk=chunk)
+ yield chunk
async def _astream(
self,
@@ -386,17 +403,20 @@ class AnthropicLLM(LLM, _AnthropicCommon):
stop = self._get_anthropic_stop(stop)
params = {**self._default_params, **kwargs}
- async for token in await self.async_client.completions.create(
- prompt=self._wrap_prompt(prompt),
- stop_sequences=stop,
- stream=True,
- **params,
- ):
- chunk = GenerationChunk(text=token.completion)
+ # Remove parameters not supported by Messages API
+ params = {k: v for k, v in params.items() if k != "max_tokens_to_sample"}
- if run_manager:
- await run_manager.on_llm_new_token(chunk.text, chunk=chunk)
- yield chunk
+ async with self.async_client.messages.stream(
+ messages=self._format_messages(prompt),
+ stop_sequences=stop if stop else None,
+ **params,
+ ) as stream:
+ async for event in stream:
+ if event.type == "content_block_delta" and hasattr(event.delta, "text"):
+ chunk = GenerationChunk(text=event.delta.text)
+ if run_manager:
+ await run_manager.on_llm_new_token(chunk.text, chunk=chunk)
+ yield chunk
def get_num_tokens(self, text: str) -> int:
"""Calculate number of tokens."""
diff --git a/libs/partners/anthropic/pyproject.toml b/libs/partners/anthropic/pyproject.toml
index b4db8a8a810..61a256e3a38 100644
--- a/libs/partners/anthropic/pyproject.toml
+++ b/libs/partners/anthropic/pyproject.toml
@@ -7,12 +7,12 @@ authors = []
license = { text = "MIT" }
requires-python = ">=3.9"
dependencies = [
- "anthropic<1,>=0.57.0",
- "langchain-core<1.0.0,>=0.3.68",
+ "anthropic<1,>=0.60.0",
+ "langchain-core<1.0.0,>=0.3.72",
"pydantic<3.0.0,>=2.7.4",
]
name = "langchain-anthropic"
-version = "0.3.17"
+version = "0.3.18"
description = "An integration package connecting AnthropicMessages and LangChain"
readme = "README.md"
diff --git a/libs/partners/anthropic/tests/integration_tests/test_chat_models.py b/libs/partners/anthropic/tests/integration_tests/test_chat_models.py
index 1bfad6214e0..78e956e7d6e 100644
--- a/libs/partners/anthropic/tests/integration_tests/test_chat_models.py
+++ b/libs/partners/anthropic/tests/integration_tests/test_chat_models.py
@@ -31,8 +31,8 @@ from pydantic import BaseModel, Field
from langchain_anthropic import ChatAnthropic, ChatAnthropicMessages
from tests.unit_tests._utils import FakeCallbackHandler
-MODEL_NAME = "claude-3-5-haiku-latest"
-IMAGE_MODEL_NAME = "claude-3-5-sonnet-latest"
+MODEL_NAME = "claude-3-5-haiku-20241022"
+IMAGE_MODEL_NAME = "claude-3-5-sonnet-20241022"
def test_stream() -> None:
@@ -178,7 +178,7 @@ async def test_abatch_tags() -> None:
async def test_async_tool_use() -> None:
llm = ChatAnthropic(
- model=MODEL_NAME,
+ model=MODEL_NAME, # type: ignore[call-arg]
)
llm_with_tools = llm.bind_tools(
@@ -274,7 +274,7 @@ def test_system_invoke() -> None:
def test_anthropic_call() -> None:
"""Test valid call to anthropic."""
- chat = ChatAnthropic(model=MODEL_NAME)
+ chat = ChatAnthropic(model=MODEL_NAME) # type: ignore[call-arg]
message = HumanMessage(content="Hello")
response = chat.invoke([message])
assert isinstance(response, AIMessage)
@@ -283,7 +283,7 @@ def test_anthropic_call() -> None:
def test_anthropic_generate() -> None:
"""Test generate method of anthropic."""
- chat = ChatAnthropic(model=MODEL_NAME)
+ chat = ChatAnthropic(model=MODEL_NAME) # type: ignore[call-arg]
chat_messages: list[list[BaseMessage]] = [
[HumanMessage(content="How many toes do dogs have?")],
]
@@ -299,7 +299,7 @@ def test_anthropic_generate() -> None:
def test_anthropic_streaming() -> None:
"""Test streaming tokens from anthropic."""
- chat = ChatAnthropic(model=MODEL_NAME)
+ chat = ChatAnthropic(model=MODEL_NAME) # type: ignore[call-arg]
message = HumanMessage(content="Hello")
response = chat.stream([message])
for token in response:
@@ -312,7 +312,7 @@ def test_anthropic_streaming_callback() -> None:
callback_handler = FakeCallbackHandler()
callback_manager = CallbackManager([callback_handler])
chat = ChatAnthropic(
- model=MODEL_NAME,
+ model=MODEL_NAME, # type: ignore[call-arg]
callback_manager=callback_manager,
verbose=True,
)
@@ -328,7 +328,7 @@ async def test_anthropic_async_streaming_callback() -> None:
callback_handler = FakeCallbackHandler()
callback_manager = CallbackManager([callback_handler])
chat = ChatAnthropic(
- model=MODEL_NAME,
+ model=MODEL_NAME, # type: ignore[call-arg]
callback_manager=callback_manager,
verbose=True,
)
@@ -343,7 +343,7 @@ async def test_anthropic_async_streaming_callback() -> None:
def test_anthropic_multimodal() -> None:
"""Test that multimodal inputs are handled correctly."""
- chat = ChatAnthropic(model=IMAGE_MODEL_NAME)
+ chat = ChatAnthropic(model=IMAGE_MODEL_NAME) # type: ignore[call-arg]
messages: list[BaseMessage] = [
HumanMessage(
content=[
@@ -399,7 +399,7 @@ async def test_astreaming() -> None:
def test_tool_use() -> None:
llm = ChatAnthropic(
- model="claude-3-7-sonnet-20250219",
+ model="claude-3-7-sonnet-20250219", # type: ignore[call-arg]
temperature=0,
)
tool_definition = {
@@ -424,7 +424,7 @@ def test_tool_use() -> None:
# Test streaming
llm = ChatAnthropic(
- model="claude-3-7-sonnet-20250219",
+ model="claude-3-7-sonnet-20250219", # type: ignore[call-arg]
temperature=0,
# Add extra headers to also test token-efficient tools
model_kwargs={
@@ -492,7 +492,7 @@ def test_tool_use() -> None:
def test_builtin_tools() -> None:
- llm = ChatAnthropic(model="claude-3-7-sonnet-20250219")
+ llm = ChatAnthropic(model="claude-3-7-sonnet-20250219") # type: ignore[call-arg]
tool = {"type": "text_editor_20250124", "name": "str_replace_editor"}
llm_with_tools = llm.bind_tools([tool])
response = llm_with_tools.invoke(
@@ -510,7 +510,7 @@ class GenerateUsername(BaseModel):
def test_disable_parallel_tool_calling() -> None:
- llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")
+ llm = ChatAnthropic(model="claude-3-5-sonnet-20241022") # type: ignore[call-arg]
llm_with_tools = llm.bind_tools([GenerateUsername], parallel_tool_calls=False)
result = llm_with_tools.invoke(
"Use the GenerateUsername tool to generate user names for:\n\n"
@@ -529,7 +529,7 @@ def test_anthropic_with_empty_text_block() -> None:
"""Type the given letter."""
return "OK"
- model = ChatAnthropic(model="claude-3-opus-20240229", temperature=0).bind_tools(
+ model = ChatAnthropic(model="claude-3-opus-20240229", temperature=0).bind_tools( # type: ignore[call-arg]
[type_letter],
)
@@ -568,7 +568,7 @@ def test_anthropic_with_empty_text_block() -> None:
def test_with_structured_output() -> None:
llm = ChatAnthropic(
- model="claude-3-opus-20240229",
+ model="claude-3-opus-20240229", # type: ignore[call-arg]
)
structured_llm = llm.with_structured_output(
@@ -587,7 +587,7 @@ def test_with_structured_output() -> None:
def test_get_num_tokens_from_messages() -> None:
- llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")
+ llm = ChatAnthropic(model="claude-3-5-sonnet-20241022") # type: ignore[call-arg]
# Test simple case
messages = [
@@ -650,7 +650,7 @@ class GetWeather(BaseModel):
@pytest.mark.parametrize("tool_choice", ["GetWeather", "auto", "any"])
def test_anthropic_bind_tools_tool_choice(tool_choice: str) -> None:
chat_model = ChatAnthropic(
- model=MODEL_NAME,
+ model=MODEL_NAME, # type: ignore[call-arg]
)
chat_model_with_tools = chat_model.bind_tools([GetWeather], tool_choice=tool_choice)
response = chat_model_with_tools.invoke("what's the weather in ny and la")
@@ -661,7 +661,7 @@ def test_pdf_document_input() -> None:
url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
data = b64encode(requests.get(url, timeout=10).content).decode()
- result = ChatAnthropic(model=IMAGE_MODEL_NAME).invoke(
+ result = ChatAnthropic(model=IMAGE_MODEL_NAME).invoke( # type: ignore[call-arg]
[
HumanMessage(
[
@@ -684,7 +684,7 @@ def test_pdf_document_input() -> None:
def test_citations() -> None:
- llm = ChatAnthropic(model="claude-3-5-haiku-latest")
+ llm = ChatAnthropic(model="claude-3-5-haiku-latest") # type: ignore[call-arg]
messages = [
{
"role": "user",
@@ -729,8 +729,8 @@ def test_citations() -> None:
@pytest.mark.vcr
def test_thinking() -> None:
llm = ChatAnthropic(
- model="claude-3-7-sonnet-latest",
- max_tokens=5_000,
+ model="claude-3-7-sonnet-latest", # type: ignore[call-arg]
+ max_tokens=5_000, # type: ignore[call-arg]
thinking={"type": "enabled", "budget_tokens": 2_000},
)
@@ -766,8 +766,8 @@ def test_thinking() -> None:
@pytest.mark.vcr
def test_redacted_thinking() -> None:
llm = ChatAnthropic(
- model="claude-3-7-sonnet-latest",
- max_tokens=5_000,
+ model="claude-3-7-sonnet-latest", # type: ignore[call-arg]
+ max_tokens=5_000, # type: ignore[call-arg]
thinking={"type": "enabled", "budget_tokens": 2_000},
)
query = "ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB" # noqa: E501
@@ -805,8 +805,8 @@ def test_redacted_thinking() -> None:
def test_structured_output_thinking_enabled() -> None:
llm = ChatAnthropic(
- model="claude-3-7-sonnet-latest",
- max_tokens=5_000,
+ model="claude-3-7-sonnet-latest", # type: ignore[call-arg]
+ max_tokens=5_000, # type: ignore[call-arg]
thinking={"type": "enabled", "budget_tokens": 2_000},
)
with pytest.warns(match="structured output"):
@@ -828,8 +828,8 @@ def test_structured_output_thinking_force_tool_use() -> None:
# when `thinking` is enabled. When this test fails, it means that the feature
# is supported and the workarounds in `with_structured_output` should be removed.
llm = ChatAnthropic(
- model="claude-3-7-sonnet-latest",
- max_tokens=5_000,
+ model="claude-3-7-sonnet-latest", # type: ignore[call-arg]
+ max_tokens=5_000, # type: ignore[call-arg]
thinking={"type": "enabled", "budget_tokens": 2_000},
).bind_tools(
[GenerateUsername],
@@ -896,13 +896,13 @@ def test_image_tool_calling() -> None:
],
),
]
- llm = ChatAnthropic(model="claude-3-5-sonnet-latest")
+ llm = ChatAnthropic(model="claude-3-5-sonnet-latest") # type: ignore[call-arg]
llm.bind_tools([color_picker]).invoke(messages)
@pytest.mark.vcr
def test_web_search() -> None:
- llm = ChatAnthropic(model="claude-3-5-sonnet-latest")
+ llm = ChatAnthropic(model="claude-3-5-sonnet-latest") # type: ignore[call-arg]
tool = {"type": "web_search_20250305", "name": "web_search", "max_uses": 1}
llm_with_tools = llm.bind_tools([tool])
@@ -944,9 +944,9 @@ def test_web_search() -> None:
@pytest.mark.vcr
def test_code_execution() -> None:
llm = ChatAnthropic(
- model="claude-sonnet-4-20250514",
+ model="claude-sonnet-4-20250514", # type: ignore[call-arg]
betas=["code-execution-2025-05-22"],
- max_tokens=10_000,
+ max_tokens=10_000, # type: ignore[call-arg]
)
tool = {"type": "code_execution_20250522", "name": "code_execution"}
@@ -1002,10 +1002,10 @@ def test_remote_mcp() -> None:
]
llm = ChatAnthropic(
- model="claude-sonnet-4-20250514",
+ model="claude-sonnet-4-20250514", # type: ignore[call-arg]
betas=["mcp-client-2025-04-04"],
mcp_servers=mcp_servers,
- max_tokens=10_000,
+ max_tokens=10_000, # type: ignore[call-arg]
)
input_message = {
@@ -1052,7 +1052,7 @@ def test_files_api_image(block_format: str) -> None:
if not image_file_id:
pytest.skip()
llm = ChatAnthropic(
- model="claude-sonnet-4-20250514",
+ model="claude-sonnet-4-20250514", # type: ignore[call-arg]
betas=["files-api-2025-04-14"],
)
if block_format == "anthropic":
@@ -1086,7 +1086,7 @@ def test_files_api_pdf(block_format: str) -> None:
if not pdf_file_id:
pytest.skip()
llm = ChatAnthropic(
- model="claude-sonnet-4-20250514",
+ model="claude-sonnet-4-20250514", # type: ignore[call-arg]
betas=["files-api-2025-04-14"],
)
if block_format == "anthropic":
@@ -1111,7 +1111,7 @@ def test_files_api_pdf(block_format: str) -> None:
def test_search_result_tool_message() -> None:
"""Test that we can pass a search result tool message to the model."""
llm = ChatAnthropic(
- model="claude-3-5-haiku-latest",
+ model="claude-3-5-haiku-latest", # type: ignore[call-arg]
betas=["search-results-2025-06-09"],
)
@@ -1164,7 +1164,7 @@ def test_search_result_tool_message() -> None:
def test_search_result_top_level() -> None:
llm = ChatAnthropic(
- model="claude-3-5-haiku-latest",
+ model="claude-3-5-haiku-latest", # type: ignore[call-arg]
betas=["search-results-2025-06-09"],
)
input_message = HumanMessage(
@@ -1209,6 +1209,6 @@ def test_search_result_top_level() -> None:
def test_async_shared_client() -> None:
- llm = ChatAnthropic(model="claude-3-5-haiku-latest")
+ llm = ChatAnthropic(model="claude-3-5-haiku-latest") # type: ignore[call-arg]
_ = asyncio.run(llm.ainvoke("Hello"))
_ = asyncio.run(llm.ainvoke("Hello"))
diff --git a/libs/partners/anthropic/tests/integration_tests/test_llms.py b/libs/partners/anthropic/tests/integration_tests/test_llms.py
index 9bf2900ba35..3da6663d4c7 100644
--- a/libs/partners/anthropic/tests/integration_tests/test_llms.py
+++ b/libs/partners/anthropic/tests/integration_tests/test_llms.py
@@ -24,14 +24,14 @@ def test_anthropic_model_param() -> None:
def test_anthropic_call() -> None:
"""Test valid call to anthropic."""
- llm = Anthropic(model="claude-2.1") # type: ignore[call-arg]
+ llm = Anthropic(model="claude-3-7-sonnet-20250219") # type: ignore[call-arg]
output = llm.invoke("Say foo:")
assert isinstance(output, str)
def test_anthropic_streaming() -> None:
"""Test streaming tokens from anthropic."""
- llm = Anthropic(model="claude-2.1") # type: ignore[call-arg]
+ llm = Anthropic(model="claude-3-7-sonnet-20250219") # type: ignore[call-arg]
generator = llm.stream("I'm Pickle Rick")
assert isinstance(generator, Generator)
@@ -45,6 +45,7 @@ def test_anthropic_streaming_callback() -> None:
callback_handler = FakeCallbackHandler()
callback_manager = CallbackManager([callback_handler])
llm = Anthropic(
+ model="claude-3-7-sonnet-20250219", # type: ignore[call-arg]
streaming=True,
callback_manager=callback_manager,
verbose=True,
@@ -55,7 +56,7 @@ def test_anthropic_streaming_callback() -> None:
async def test_anthropic_async_generate() -> None:
"""Test async generate."""
- llm = Anthropic()
+ llm = Anthropic(model="claude-3-7-sonnet-20250219") # type: ignore[call-arg]
output = await llm.agenerate(["How many toes do dogs have?"])
assert isinstance(output, LLMResult)
@@ -65,6 +66,7 @@ async def test_anthropic_async_streaming_callback() -> None:
callback_handler = FakeCallbackHandler()
callback_manager = CallbackManager([callback_handler])
llm = Anthropic(
+ model="claude-3-7-sonnet-20250219", # type: ignore[call-arg]
streaming=True,
callback_manager=callback_manager,
verbose=True,
diff --git a/libs/partners/anthropic/uv.lock b/libs/partners/anthropic/uv.lock
index 445e1f60047..110c4f691ef 100644
--- a/libs/partners/anthropic/uv.lock
+++ b/libs/partners/anthropic/uv.lock
@@ -23,7 +23,7 @@ wheels = [
[[package]]
name = "anthropic"
-version = "0.57.0"
+version = "0.60.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
@@ -34,9 +34,9 @@ dependencies = [
{ name = "sniffio" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/7f/58/21e725231c2d36025384a4764e528009a93728039d81a295d28c1e396448/anthropic-0.57.0.tar.gz", hash = "sha256:2073ed4c8b75bb25dff53da0a3336232cd54f475392ea47150d0ccfa634bdc42", size = 423683, upload-time = "2025-07-03T16:25:40.926Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/4e/03/3334921dc54ed822b3dd993ae72d823a7402588521bbba3e024b3333a1fd/anthropic-0.60.0.tar.gz", hash = "sha256:a22ba187c6f4fd5afecb2fc913b960feccf72bc0d25c1b7ce0345e87caede577", size = 425983, upload-time = "2025-07-28T19:53:47.685Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/dd/1f/00f8b30c6d3ad02dc37423c3cb0344289a566c38fb0322c8532751e43ecf/anthropic-0.57.0-py3-none-any.whl", hash = "sha256:18672e4d1f7734df58371ff19b796e630faf0732ce64167b55d4a455de75bbf6", size = 292827, upload-time = "2025-07-03T16:25:39.385Z" },
+ { url = "https://files.pythonhosted.org/packages/da/bb/d84f287fb1c217b30c328af987cf8bbe3897edf0518dcc5fa39412f794ec/anthropic-0.60.0-py3-none-any.whl", hash = "sha256:65ad1f088a960217aaf82ba91ff743d6c89e9d811c6d64275b9a7c59ee9ac3c6", size = 293116, upload-time = "2025-07-28T19:53:45.944Z" },
]
[[package]]
@@ -424,7 +424,7 @@ wheels = [
[[package]]
name = "langchain-anthropic"
-version = "0.3.17"
+version = "0.3.18"
source = { editable = "." }
dependencies = [
{ name = "anthropic" },
@@ -469,7 +469,7 @@ typing = [
[package.metadata]
requires-dist = [
- { name = "anthropic", specifier = ">=0.57.0,<1" },
+ { name = "anthropic", specifier = ">=0.60.0,<1" },
{ name = "langchain-core", editable = "../../core" },
{ name = "pydantic", specifier = ">=2.7.4,<3.0.0" },
]
@@ -505,7 +505,7 @@ typing = [
[[package]]
name = "langchain-core"
-version = "0.3.68"
+version = "0.3.72"
source = { editable = "../../core" }
dependencies = [
{ name = "jsonpatch" },
@@ -521,7 +521,7 @@ dependencies = [
requires-dist = [
{ name = "jsonpatch", specifier = ">=1.33,<2.0" },
{ name = "langsmith", specifier = ">=0.3.45" },
- { name = "packaging", specifier = ">=23.2,<25" },
+ { name = "packaging", specifier = ">=23.2" },
{ name = "pydantic", specifier = ">=2.7.4" },
{ name = "pyyaml", specifier = ">=5.3" },
{ name = "tenacity", specifier = ">=8.1.0,!=8.4.0,<10.0.0" },
diff --git a/libs/partners/openai/tests/integration_tests/chat_models/test_base.py b/libs/partners/openai/tests/integration_tests/chat_models/test_base.py
index 4b02676e2a8..48fa87bbb19 100644
--- a/libs/partners/openai/tests/integration_tests/chat_models/test_base.py
+++ b/libs/partners/openai/tests/integration_tests/chat_models/test_base.py
@@ -221,6 +221,7 @@ def test_openai_invoke() -> None:
llm = ChatOpenAI(
model="o4-mini",
service_tier="flex", # Also test service_tier
+ max_retries=3, # Add retries for 503 capacity errors
)
result = llm.invoke("Hello", config=dict(tags=["foo"]))
diff --git a/libs/partners/openai/tests/unit_tests/chat_models/test_responses_stream.py b/libs/partners/openai/tests/unit_tests/chat_models/test_responses_stream.py
index 370adcd1f1a..eca5ee1c255 100644
--- a/libs/partners/openai/tests/unit_tests/chat_models/test_responses_stream.py
+++ b/libs/partners/openai/tests/unit_tests/chat_models/test_responses_stream.py
@@ -237,6 +237,7 @@ responses_stream = [
item_id="msg_123",
output_index=1,
sequence_number=16,
+ logprobs=[],
type="response.output_text.delta",
),
ResponseTextDeltaEvent(
@@ -245,6 +246,7 @@ responses_stream = [
item_id="msg_123",
output_index=1,
sequence_number=17,
+ logprobs=[],
type="response.output_text.delta",
),
ResponseTextDoneEvent(
@@ -253,6 +255,7 @@ responses_stream = [
output_index=1,
sequence_number=18,
text="text block one",
+ logprobs=[],
type="response.output_text.done",
),
ResponseContentPartDoneEvent(
@@ -279,6 +282,7 @@ responses_stream = [
item_id="msg_123",
output_index=1,
sequence_number=21,
+ logprobs=[],
type="response.output_text.delta",
),
ResponseTextDeltaEvent(
@@ -287,6 +291,7 @@ responses_stream = [
item_id="msg_123",
output_index=1,
sequence_number=22,
+ logprobs=[],
type="response.output_text.delta",
),
ResponseTextDoneEvent(
@@ -295,6 +300,7 @@ responses_stream = [
output_index=1,
sequence_number=23,
text="another text block",
+ logprobs=[],
type="response.output_text.done",
),
ResponseContentPartDoneEvent(
@@ -443,6 +449,7 @@ responses_stream = [
item_id="msg_234",
output_index=3,
sequence_number=38,
+ logprobs=[],
type="response.output_text.delta",
),
ResponseTextDoneEvent(
@@ -451,6 +458,7 @@ responses_stream = [
output_index=3,
sequence_number=39,
text="more",
+ logprobs=[],
type="response.output_text.done",
),
ResponseContentPartDoneEvent(
@@ -475,6 +483,7 @@ responses_stream = [
item_id="msg_234",
output_index=3,
sequence_number=42,
+ logprobs=[],
type="response.output_text.delta",
),
ResponseTextDoneEvent(
@@ -483,6 +492,7 @@ responses_stream = [
output_index=3,
sequence_number=43,
text="text",
+ logprobs=[],
type="response.output_text.done",
),
ResponseContentPartDoneEvent(
diff --git a/libs/partners/openai/uv.lock b/libs/partners/openai/uv.lock
index 32497ee381a..0934e7aa241 100644
--- a/libs/partners/openai/uv.lock
+++ b/libs/partners/openai/uv.lock
@@ -995,7 +995,7 @@ wheels = [
[[package]]
name = "openai"
-version = "1.95.1"
+version = "1.98.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
@@ -1007,9 +1007,9 @@ dependencies = [
{ name = "tqdm" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/a1/a3/70cd57c7d71086c532ce90de5fdef4165dc6ae9dbf346da6737ff9ebafaa/openai-1.95.1.tar.gz", hash = "sha256:f089b605282e2a2b6776090b4b46563ac1da77f56402a222597d591e2dcc1086", size = 488271, upload-time = "2025-07-11T20:47:24.437Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/9d/52eadb15c92802711d6b6cf00df3a6d0d18b588f4c5ba5ff210c6419fc03/openai-1.98.0.tar.gz", hash = "sha256:3ee0fcc50ae95267fd22bd1ad095ba5402098f3df2162592e68109999f685427", size = 496695, upload-time = "2025-07-30T12:48:03.701Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/02/1d/0432ea635097f4dbb34641a3650803d8a4aa29d06bafc66583bf1adcceb4/openai-1.95.1-py3-none-any.whl", hash = "sha256:8bbdfeceef231b1ddfabbc232b179d79f8b849aab5a7da131178f8d10e0f162f", size = 755613, upload-time = "2025-07-11T20:47:22.629Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/fe/f64631075b3d63a613c0d8ab761d5941631a470f6fa87eaaee1aa2b4ec0c/openai-1.98.0-py3-none-any.whl", hash = "sha256:b99b794ef92196829120e2df37647722104772d2a74d08305df9ced5f26eae34", size = 767713, upload-time = "2025-07-30T12:48:01.264Z" },
]
[[package]]
diff --git a/scripts/release_branch.py b/scripts/release_branch.py
index 91d4f3b007b..a7d0c2e06c5 100644
--- a/scripts/release_branch.py
+++ b/scripts/release_branch.py
@@ -3,11 +3,13 @@ python scripts/release_branch.py anthropic bagatur
"""
import glob
-import tomllib
-import toml
import subprocess
import sys
+# Ignoring errors since this script is run in a controlled environment
+import toml # type: ignore # pyright: ignore[reportMissingModuleSource]
+import tomllib # type: ignore # pyright: ignore[reportMissingImports]
+
def main(*args):
pkg = args[1]
diff --git a/scripts/update_mypy_ruff.py b/scripts/update_mypy_ruff.py
index 3bfd1439691..d5e66856ff7 100644
--- a/scripts/update_mypy_ruff.py
+++ b/scripts/update_mypy_ruff.py
@@ -1,12 +1,13 @@
"""python scripts/update_mypy_ruff.py"""
import glob
-import tomllib
+import re
+import subprocess
from pathlib import Path
-import toml
-import subprocess
-import re
+# Ignoring errors since this script is run in a controlled environment
+import toml # type: ignore # pyright: ignore[reportMissingModuleSource]
+import tomllib # type: ignore # pyright: ignore[reportMissingImports]
ROOT_DIR = Path(__file__).parents[1]
@@ -50,10 +51,9 @@ def main():
to_ignore = {}
for l in logs:
- if re.match("^(.*)\:(\d+)\: error:.*\[(.*)\]", l):
- path, line_no, error_type = re.match(
- "^(.*)\:(\d+)\: error:.*\[(.*)\]", l
- ).groups()
+ match = re.match(r"^(.*):(\d+): error:.*\[(.*)\]", l)
+ if match:
+ path, line_no, error_type = match.groups()
if (path, line_no) in to_ignore:
to_ignore[(path, line_no)].append(error_type)
else: