From 0d13463e7ff6a8e328c1c30d2eac1cf95dfdfaae Mon Sep 17 00:00:00 2001 From: Mike Lambert Date: Tue, 17 Feb 2026 15:59:54 -0500 Subject: [PATCH] feat(anthropic): add User-Agent header for Anthropic API calls (#35268) --- .../anthropic/langchain_anthropic/chat_models.py | 10 +++++++++- .../anthropic/tests/unit_tests/test_chat_models.py | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/libs/partners/anthropic/langchain_anthropic/chat_models.py b/libs/partners/anthropic/langchain_anthropic/chat_models.py index 35463c88a74..e1abc27e8d9 100644 --- a/libs/partners/anthropic/langchain_anthropic/chat_models.py +++ b/libs/partners/anthropic/langchain_anthropic/chat_models.py @@ -57,6 +57,7 @@ from langchain_core.utils.utils import _build_model_kwargs from pydantic import BaseModel, ConfigDict, Field, SecretStr, model_validator from typing_extensions import NotRequired, Self, TypedDict +from langchain_anthropic import __version__ from langchain_anthropic._client_utils import ( _get_default_async_httpx_client, _get_default_httpx_client, @@ -74,6 +75,8 @@ _message_type_lookups = { _MODEL_PROFILES = cast(ModelProfileRegistry, _PROFILES) +_USER_AGENT: Final[str] = f"langchain-anthropic/{__version__}" + def _get_default_model_profile(model_name: str) -> ModelProfile: """Get the default profile for a model. @@ -1025,11 +1028,16 @@ class ChatAnthropic(BaseChatModel): @cached_property def _client_params(self) -> dict[str, Any]: + # Merge User-Agent with user-provided headers (user headers take precedence) + default_headers = {"User-Agent": _USER_AGENT} + if self.default_headers: + default_headers.update(self.default_headers) + client_params: dict[str, Any] = { "api_key": self.anthropic_api_key.get_secret_value(), "base_url": self.anthropic_api_url, "max_retries": self.max_retries, - "default_headers": (self.default_headers or None), + "default_headers": default_headers, } # value <= 0 indicates the param should be ignored. None is a meaningful value # for Anthropic client and treated differently than not specifying the param at diff --git a/libs/partners/anthropic/tests/unit_tests/test_chat_models.py b/libs/partners/anthropic/tests/unit_tests/test_chat_models.py index eab0f4aed21..1eb74c71008 100644 --- a/libs/partners/anthropic/tests/unit_tests/test_chat_models.py +++ b/libs/partners/anthropic/tests/unit_tests/test_chat_models.py @@ -53,6 +53,15 @@ def test_initialization() -> None: assert model.anthropic_api_url == "https://api.anthropic.com" +def test_user_agent_header_in_client_params() -> None: + """Test that _client_params includes a User-Agent header.""" + llm = ChatAnthropic(model=MODEL_NAME, api_key="test-key") # type: ignore[arg-type] + params = llm._client_params + assert "default_headers" in params + assert "User-Agent" in params["default_headers"] + assert params["default_headers"]["User-Agent"].startswith("langchain-anthropic/") + + @pytest.mark.parametrize("async_api", [True, False]) def test_streaming_attribute_should_stream(async_api: bool) -> None: # noqa: FBT001 llm = ChatAnthropic(model=MODEL_NAME, streaming=True)