Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-langchain-anthropic for 
openSUSE:Factory checked in at 2026-07-29 19:00:03
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-langchain-anthropic (Old)
 and      /work/SRC/openSUSE:Factory/.python-langchain-anthropic.new.2004 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-langchain-anthropic"

Wed Jul 29 19:00:03 2026 rev:4 rq:1368300 version:1.5.3

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-langchain-anthropic/python-langchain-anthropic.changes
    2026-07-26 11:32:38.183316377 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-langchain-anthropic.new.2004/python-langchain-anthropic.changes
  2026-07-29 19:01:28.370246400 +0200
@@ -1,0 +2,9 @@
+Wed Jul 29 06:30:23 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to version 1.5.3:
+  * Preserve an empty thinking field in signature_delta streaming
+  * Strip unsupported fields from system message content blocks
+- Raise the langchain-core requirement to >= 1.5.2 and the anthropic
+  build requirement to >= 0.120.0, matching upstream
+
+-------------------------------------------------------------------

Old:
----
  langchain_anthropic-1.5.2.tar.gz

New:
----
  langchain_anthropic-1.5.3.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-langchain-anthropic.spec ++++++
--- /var/tmp/diff_new_pack.1D2Q58/_old  2026-07-29 19:01:29.242276477 +0200
+++ /var/tmp/diff_new_pack.1D2Q58/_new  2026-07-29 19:01:29.242276477 +0200
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-langchain-anthropic
-Version:        1.5.2
+Version:        1.5.3
 Release:        0
 Summary:        Integration package connecting Claude (Anthropic) APIs and 
LangChain
 License:        MIT
@@ -29,15 +29,15 @@
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 Requires:       python-anthropic >= 0.120.0
-Requires:       python-langchain-core >= 1.5.1
+Requires:       python-langchain-core >= 1.5.2
 Requires:       python-pydantic >= 2.7.4
 BuildArch:      noarch
 # SECTION test requirements
-BuildRequires:  %{python_module anthropic >= 0.96.0}
+BuildRequires:  %{python_module anthropic >= 0.120.0}
 BuildRequires:  %{python_module blockbuster}
 BuildRequires:  %{python_module defusedxml}
 BuildRequires:  %{python_module freezegun}
-BuildRequires:  %{python_module langchain-core >= 1.5.0}
+BuildRequires:  %{python_module langchain-core >= 1.5.2}
 BuildRequires:  %{python_module pydantic >= 2.7.4}
 BuildRequires:  %{python_module pytest-asyncio}
 BuildRequires:  %{python_module pytest-mock}

++++++ langchain_anthropic-1.5.2.tar.gz -> langchain_anthropic-1.5.3.tar.gz 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_anthropic-1.5.2/PKG-INFO 
new/langchain_anthropic-1.5.3/PKG-INFO
--- old/langchain_anthropic-1.5.2/PKG-INFO      2020-02-02 01:00:00.000000000 
+0100
+++ new/langchain_anthropic-1.5.3/PKG-INFO      2020-02-02 01:00:00.000000000 
+0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: langchain-anthropic
-Version: 1.5.2
+Version: 1.5.3
 Summary: Integration package connecting Claude (Anthropic) APIs and LangChain
 Project-URL: Homepage, 
https://docs.langchain.com/oss/python/integrations/providers/anthropic
 Project-URL: Documentation, 
https://reference.langchain.com/python/integrations/langchain_anthropic/
@@ -24,7 +24,7 @@
 Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
 Requires-Python: <4.0.0,>=3.10.0
 Requires-Dist: anthropic<1.0.0,>=0.120.0
-Requires-Dist: langchain-core<2.0.0,>=1.5.1
+Requires-Dist: langchain-core<2.0.0,>=1.5.2
 Requires-Dist: pydantic<3.0.0,>=2.7.4
 Description-Content-Type: text/markdown
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_anthropic-1.5.2/langchain_anthropic/_version.py 
new/langchain_anthropic-1.5.3/langchain_anthropic/_version.py
--- old/langchain_anthropic-1.5.2/langchain_anthropic/_version.py       
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_anthropic-1.5.3/langchain_anthropic/_version.py       
2020-02-02 01:00:00.000000000 +0100
@@ -1,3 +1,3 @@
 """Version information for `langchain-anthropic`."""
 
-__version__ = "1.5.2"
+__version__ = "1.5.3"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_anthropic-1.5.2/langchain_anthropic/chat_models.py 
new/langchain_anthropic-1.5.3/langchain_anthropic/chat_models.py
--- old/langchain_anthropic-1.5.2/langchain_anthropic/chat_models.py    
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_anthropic-1.5.3/langchain_anthropic/chat_models.py    
2020-02-02 01:00:00.000000000 +0100
@@ -475,6 +475,29 @@
     return formatted_block
 
 
+def _format_text_block(block: dict) -> dict:
+    """Narrow a text content block to fields supported by Anthropic's API.
+
+    Drops LangChain-internal fields (e.g. the ``id`` minted by
+    ``create_text_block``) that Anthropic rejects as extra inputs.
+    """
+    formatted_block = {
+        k: v
+        for k, v in block.items()
+        if k in ("type", "text", "cache_control", "citations")
+    }
+    # Clean up citations to remove null file_id fields
+    if formatted_block.get("citations"):
+        cleaned_citations = []
+        for citation in formatted_block["citations"]:
+            cleaned_citation = {
+                k: v for k, v in citation.items() if not (k == "file_id" and v 
is None)
+            }
+            cleaned_citations.append(cleaned_citation)
+        formatted_block["citations"] = cleaned_citations
+    return formatted_block
+
+
 def _format_messages(
     messages: Sequence[BaseMessage],
 ) -> tuple[str | list[dict] | None, list[dict]]:
@@ -490,7 +513,11 @@
             if isinstance(message.content, list):
                 system = [
                     (
-                        block
+                        (
+                            _format_text_block(block)
+                            if block.get("type") == "text"
+                            else block
+                        )
                         if isinstance(block, dict)
                         else {"type": "text", "text": block}
                     )
@@ -598,32 +625,16 @@
                         # accepted.
                         # 
https://github.com/anthropics/anthropic-sdk-python/issues/461
                         if text.strip():
-                            formatted_block = {
-                                k: v
-                                for k, v in block.items()
-                                if k in ("type", "text", "cache_control", 
"citations")
-                            }
-                            # Clean up citations to remove null file_id fields
-                            if formatted_block.get("citations"):
-                                cleaned_citations = []
-                                for citation in formatted_block["citations"]:
-                                    cleaned_citation = {
-                                        k: v
-                                        for k, v in citation.items()
-                                        if not (k == "file_id" and v is None)
-                                    }
-                                    cleaned_citations.append(cleaned_citation)
-                                formatted_block["citations"] = 
cleaned_citations
-                            content.append(formatted_block)
+                            content.append(_format_text_block(block))
                     elif block["type"] == "thinking":
-                        content.append(
-                            {
-                                k: v
-                                for k, v in block.items()
-                                if k
-                                in ("type", "thinking", "cache_control", 
"signature")
-                            },
-                        )
+                        formatted_thinking = {
+                            k: v
+                            for k, v in block.items()
+                            if k in ("type", "thinking", "cache_control", 
"signature")
+                        }
+                        if "signature" in formatted_thinking:
+                            formatted_thinking.setdefault("thinking", "")
+                        content.append(formatted_thinking)
                     elif block["type"] == "redacted_thinking":
                         content.append(
                             {
@@ -1790,6 +1801,8 @@
                 content_block = event.delta.model_dump()
                 content_block["index"] = event.index
                 content_block["type"] = "thinking"
+                if event.delta.type == "signature_delta":
+                    content_block.setdefault("thinking", "")
                 message_chunk = AIMessageChunk(content=[content_block])
 
             # Tool input JSON (streaming tool arguments)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_anthropic-1.5.2/pyproject.toml 
new/langchain_anthropic-1.5.3/pyproject.toml
--- old/langchain_anthropic-1.5.2/pyproject.toml        2020-02-02 
01:00:00.000000000 +0100
+++ new/langchain_anthropic-1.5.3/pyproject.toml        2020-02-02 
01:00:00.000000000 +0100
@@ -20,11 +20,11 @@
     "Topic :: Scientific/Engineering :: Artificial Intelligence",
 ]
 
-version = "1.5.2"
+version = "1.5.3"
 requires-python = ">=3.10.0,<4.0.0"
 dependencies = [
     "anthropic>=0.120.0,<1.0.0",
-    "langchain-core>=1.5.1,<2.0.0",
+    "langchain-core>=1.5.2,<2.0.0",
     "pydantic>=2.7.4,<3.0.0",
 ]
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_anthropic-1.5.2/tests/unit_tests/test_chat_models.py 
new/langchain_anthropic-1.5.3/tests/unit_tests/test_chat_models.py
--- old/langchain_anthropic-1.5.2/tests/unit_tests/test_chat_models.py  
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_anthropic-1.5.3/tests/unit_tests/test_chat_models.py  
2020-02-02 01:00:00.000000000 +0100
@@ -21,6 +21,7 @@
     SystemMessage,
     ToolMessage,
 )
+from langchain_core.messages.content import create_text_block
 from langchain_core.runnables import RunnableBinding
 from langchain_core.tools import BaseTool, tool
 from langchain_core.tracers.base import BaseTracer
@@ -1480,6 +1481,41 @@
     assert expected_messages == actual_messages
 
 
+def test__format_messages_system_v1_content_blocks_drop_id() -> None:
+    """System text blocks from `create_text_block` must not leak the `id` 
field.
+
+    See https://github.com/langchain-ai/langchain/issues/39100
+    """
+    messages = [
+        SystemMessage(content_blocks=[create_text_block("You are helpful.")]),
+        HumanMessage("hi"),
+    ]
+    actual_system, actual_messages = _format_messages(messages)
+    assert actual_system == [{"type": "text", "text": "You are helpful."}]
+    assert actual_messages == [{"role": "user", "content": "hi"}]
+
+
+def test__format_messages_system_text_block_preserves_supported_fields() -> 
None:
+    """Sanitizing system text blocks keeps Anthropic-supported fields."""
+    messages = [
+        SystemMessage(
+            [
+                {
+                    "type": "text",
+                    "text": "foo",
+                    "id": "lc_abc123",
+                    "cache_control": {"type": "ephemeral"},
+                },
+            ],
+        ),
+        HumanMessage("hi"),
+    ]
+    actual_system, _ = _format_messages(messages)
+    assert actual_system == [
+        {"type": "text", "text": "foo", "cache_control": {"type": 
"ephemeral"}},
+    ]
+
+
 def test_anthropic_api_key_is_secret_string() -> None:
     """Test that the API key is stored as a SecretStr."""
     chat_model = ChatAnthropic(  # type: ignore[call-arg, call-arg]
@@ -2408,6 +2444,161 @@
     assert block_start_event is not None
 
 
+def test_signature_delta_preserves_empty_thinking_field() -> None:
+    """A `signature_delta` chunk must include `thinking` even when empty.
+
+    When an empty adaptive-thinking block streams only a signature_delta
+    (no thinking_delta), the aggregated block must still carry the required
+    `thinking` key so the message round-trips to the API without a 400 error.
+    """
+    from anthropic.types import (
+        RawContentBlockDeltaEvent,
+        RawContentBlockStartEvent,
+        SignatureDelta,
+        ThinkingBlock,
+    )
+
+    llm = ChatAnthropic(model=MODEL_NAME)  # type: ignore[call-arg]
+
+    # Step 1: process the empty content_block_start (no chunk emitted).
+    chunk, block_start_event = llm._make_message_chunk_from_anthropic_event(
+        RawContentBlockStartEvent(
+            content_block=ThinkingBlock(thinking="", signature="", 
type="thinking"),
+            index=0,
+            type="content_block_start",
+        ),
+        stream_usage=True,
+        coerce_content_to_string=False,
+        block_start_event=None,
+    )
+    assert chunk is None
+    assert block_start_event is not None
+
+    # Step 2: process the signature_delta with the tracked start event.
+    chunk, _ = llm._make_message_chunk_from_anthropic_event(
+        RawContentBlockDeltaEvent(
+            delta=SignatureDelta(signature="sig_abc", type="signature_delta"),
+            index=0,
+            type="content_block_delta",
+        ),
+        stream_usage=True,
+        coerce_content_to_string=False,
+        block_start_event=block_start_event,
+    )
+    assert chunk is not None
+    assert isinstance(chunk.content, list)
+    assert len(chunk.content) == 1
+    block = chunk.content[0]
+    assert isinstance(block, dict)
+    assert block.get("type") == "thinking"
+    assert block.get("thinking") == ""
+    assert block.get("signature") == "sig_abc"
+
+
+def test_thinking_delta_preserves_thinking_field() -> None:
+    """A `thinking_delta` with text is unaffected by the empty-thinking fix.
+
+    The trailing empty string from a signature_delta must not clobber
+    thinking text already accumulated from earlier thinking_delta chunks.
+    """
+    from anthropic.types import (
+        RawContentBlockDeltaEvent,
+        RawContentBlockStartEvent,
+        RawContentBlockStopEvent,
+        RawMessageStartEvent,
+        SignatureDelta,
+        ThinkingBlock,
+        ThinkingDelta,
+    )
+
+    msg = Message(
+        id="msg_thinking",
+        content=[],
+        model=MODEL_NAME,
+        role="assistant",
+        stop_reason=None,
+        stop_sequence=None,
+        usage=Usage(input_tokens=10, output_tokens=0),
+        type="message",
+    )
+    events: list = [
+        RawMessageStartEvent(message=msg, type="message_start"),
+        RawContentBlockStartEvent(
+            content_block=ThinkingBlock(thinking="", signature="", 
type="thinking"),
+            index=0,
+            type="content_block_start",
+        ),
+        RawContentBlockDeltaEvent(
+            delta=ThinkingDelta(thinking="Let me ", type="thinking_delta"),
+            index=0,
+            type="content_block_delta",
+        ),
+        RawContentBlockDeltaEvent(
+            delta=ThinkingDelta(thinking="think.", type="thinking_delta"),
+            index=0,
+            type="content_block_delta",
+        ),
+        RawContentBlockDeltaEvent(
+            delta=SignatureDelta(signature="sig_xyz", type="signature_delta"),
+            index=0,
+            type="content_block_delta",
+        ),
+        RawContentBlockStopEvent(index=0, type="content_block_stop"),
+    ]
+
+    llm = ChatAnthropic(model=MODEL_NAME)  # type: ignore[call-arg]
+    aggregate = _aggregate_anthropic_events(llm, events, 
coerce_content_to_string=False)
+    assert aggregate is not None
+    assert isinstance(aggregate.content, list)
+    thinking_blocks = [
+        b
+        for b in aggregate.content
+        if isinstance(b, dict) and b.get("type") == "thinking"
+    ]
+    assert len(thinking_blocks) == 1
+    assert thinking_blocks[0]["thinking"] == "Let me think."
+    assert thinking_blocks[0]["signature"] == "sig_xyz"
+
+
+def test_format_messages_backfills_missing_thinking_field() -> None:
+    """Replaying a thinking block missing `thinking` must not 400.
+
+    Thinking blocks persisted before the streaming fix can carry a signature
+    but no `thinking` key. `_format_messages` must restore the canonical empty
+    string so the block round-trips to the API instead of failing with
+    ``400 thinking.thinking: Field required``.
+    """
+    llm = ChatAnthropic(model=MODEL_NAME)  # type: ignore[call-arg]
+
+    # A corrupted block as produced by the pre-fix streaming path: signature
+    # present, required `thinking` key absent.
+    corrupted = AIMessage(
+        content=[{"type": "thinking", "signature": "sig_abc", "index": 0}]
+    )
+    payload = llm._get_request_payload(
+        [HumanMessage("hi"), corrupted, HumanMessage("continue")]
+    )
+    block = payload["messages"][1]["content"][0]
+    assert block["type"] == "thinking"
+    assert block["thinking"] == ""
+    assert block["signature"] == "sig_abc"
+
+
+def test_format_messages_preserves_nonempty_thinking_field() -> None:
+    """The backfill must not clobber real thinking text on replay."""
+    llm = ChatAnthropic(model=MODEL_NAME)  # type: ignore[call-arg]
+
+    msg = AIMessage(
+        content=[
+            {"type": "thinking", "thinking": "Let me think.", "signature": 
"sig_xyz"}
+        ]
+    )
+    payload = llm._get_request_payload([HumanMessage("hi"), msg])
+    block = payload["messages"][1]["content"][0]
+    assert block["thinking"] == "Let me think."
+    assert block["signature"] == "sig_xyz"
+
+
 def test_strict_tool_use() -> None:
     model = ChatAnthropic(
         model=MODEL_NAME,  # type: ignore[call-arg]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_anthropic-1.5.2/uv.lock 
new/langchain_anthropic-1.5.3/uv.lock
--- old/langchain_anthropic-1.5.2/uv.lock       2020-02-02 01:00:00.000000000 
+0100
+++ new/langchain_anthropic-1.5.3/uv.lock       2020-02-02 01:00:00.000000000 
+0100
@@ -617,7 +617,7 @@
 
 [[package]]
 name = "langchain-anthropic"
-version = "1.5.2"
+version = "1.5.3"
 source = { editable = "." }
 dependencies = [
     { name = "anthropic" },
@@ -690,7 +690,7 @@
 
 [[package]]
 name = "langchain-core"
-version = "1.5.1"
+version = "1.5.2"
 source = { editable = "../../core" }
 dependencies = [
     { name = "jsonpatch" },
@@ -721,7 +721,7 @@
 dev = [
     { name = "grandalf", specifier = ">=0.8.0,<1.0.0" },
     { name = "jupyter", specifier = ">=1.0.0,<2.0.0" },
-    { name = "setuptools", specifier = ">=67.6.1,<83.0.0" },
+    { name = "setuptools", specifier = ">=67.6.1,<84.0.0" },
 ]
 lint = [{ name = "ruff", specifier = ">=0.15.0,<0.16.0" }]
 test = [

Reply via email to