Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-langchain-core for 
openSUSE:Factory checked in at 2026-11-04 19:46:15
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-langchain-core (Old)
 and      /work/SRC/openSUSE:Factory/.python-langchain-core.new.3759290 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-langchain-core"

Wed Nov  4 19:46:15 2026 rev:25 rq:1394852 version:1.7.4

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-langchain-core/python-langchain-core.changes  
    2026-10-31 23:13:40.821154565 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-langchain-core.new.3759290/python-langchain-core.changes
 2026-11-04 19:49:59.800798300 +0100
@@ -1,0 +2,9 @@
+Tue Nov  3 18:52:38 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to 1.7.4:
+  * Avoid mutating message content in the Anthropic translator
+  * Deselect 17 uuid7 run-tree and tracing tests broken by the
+    python-uuid-utils 1.0.0 API change; bare uuid7() production
+    call sites are unaffected
+
+-------------------------------------------------------------------

Old:
----
  langchain_core-1.7.1.tar.gz
  langchain_core-1.7.3.tar.gz

New:
----
  langchain_core-1.7.4.tar.gz

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

Other differences:
------------------
++++++ python-langchain-core.spec ++++++
--- /var/tmp/diff_new_pack.5dSIdV/_old  2026-11-04 19:50:00.741837541 +0100
+++ /var/tmp/diff_new_pack.5dSIdV/_new  2026-11-04 19:50:00.744837666 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           python-langchain-core
-Version:        1.7.3
+Version:        1.7.4
 Release:        0
 Summary:        Building applications with LLMs through composability
 License:        MIT
@@ -98,9 +98,14 @@
 #   does a blocking os.stat on the CA bundle that blockbuster flags in the 
async
 #   tests (openSUSE-specific), so the whole transport module is skipped;
 # - the public-URL/webhook SSRF tests need live DNS resolution (offline build);
+# - the uuid7 run-tree/tracing tests and test_uuid7 fail against
+#   python-uuid-utils 1.0.0, whose uuid7() takes only `nanoseconds=`
+#   while langchain-core and langsmith still pass `timestamp=`/`nanos=`
+#   (upstream CI pins uuid-utils 0.17.0); production call sites use
+#   bare uuid7() and are unaffected.
 # - test_benchmark_model asserts 1000 fake invokes finish in under one second
 #   and already fails on slower OBS i586 workers (same fail on devel 1.5.6).
-%pytest tests/unit_tests --ignore 
tests/unit_tests/indexing/test_in_memory_indexer.py --ignore 
tests/unit_tests/language_models/test_compat_bridge.py --ignore 
tests/unit_tests/stores/test_in_memory.py --ignore 
tests/unit_tests/vectorstores/test_in_memory.py --ignore 
tests/unit_tests/test_ssrf_policy_transport.py --deselect 
tests/unit_tests/runnables/test_graph.py::test_graph_single_runnable --deselect 
tests/unit_tests/runnables/test_graph.py::test_graph_sequence --deselect 
tests/unit_tests/runnables/test_graph.py::test_graph_sequence_map --deselect 
tests/unit_tests/test_ssrf_protection.py::TestValidateSafeUrl::test_valid_public_https_url
 --deselect 
tests/unit_tests/test_ssrf_protection.py::TestValidateSafeUrl::test_valid_public_http_url
 --deselect 
tests/unit_tests/test_ssrf_protection.py::TestValidateSafeUrl::test_https_only_mode
 --deselect 
tests/unit_tests/test_ssrf_protection.py::TestIsSafeUrl::test_safe_url_returns_true
 --deselect tests/unit_tests/test_ssrf_protection.py::TestSSR
 FProtectedUrlType::test_valid_url_accepted --deselect 
tests/unit_tests/test_ssrf_protection.py::TestRealWorldURLs::test_slack_webhook 
--deselect 
tests/unit_tests/test_ssrf_protection.py::TestRealWorldURLs::test_discord_webhook
 --deselect 
tests/unit_tests/test_ssrf_protection.py::TestRealWorldURLs::test_webhook_site 
--deselect 
tests/unit_tests/test_ssrf_protection.py::TestRealWorldURLs::test_ngrok_url 
--deselect 
tests/unit_tests/language_models/chat_models/test_benchmark.py::test_benchmark_model
+%pytest tests/unit_tests --ignore 
tests/unit_tests/indexing/test_in_memory_indexer.py --ignore 
tests/unit_tests/language_models/test_compat_bridge.py --ignore 
tests/unit_tests/stores/test_in_memory.py --ignore 
tests/unit_tests/vectorstores/test_in_memory.py --ignore 
tests/unit_tests/test_ssrf_policy_transport.py --deselect 
tests/unit_tests/runnables/test_graph.py::test_graph_single_runnable --deselect 
tests/unit_tests/runnables/test_graph.py::test_graph_sequence --deselect 
tests/unit_tests/runnables/test_graph.py::test_graph_sequence_map --deselect 
tests/unit_tests/test_ssrf_protection.py::TestValidateSafeUrl::test_valid_public_https_url
 --deselect 
tests/unit_tests/test_ssrf_protection.py::TestValidateSafeUrl::test_valid_public_http_url
 --deselect 
tests/unit_tests/test_ssrf_protection.py::TestValidateSafeUrl::test_https_only_mode
 --deselect 
tests/unit_tests/test_ssrf_protection.py::TestIsSafeUrl::test_safe_url_returns_true
 --deselect tests/unit_tests/test_ssrf_protection.py::TestSSR
 FProtectedUrlType::test_valid_url_accepted --deselect 
tests/unit_tests/test_ssrf_protection.py::TestRealWorldURLs::test_slack_webhook 
--deselect 
tests/unit_tests/test_ssrf_protection.py::TestRealWorldURLs::test_discord_webhook
 --deselect 
tests/unit_tests/test_ssrf_protection.py::TestRealWorldURLs::test_webhook_site 
--deselect 
tests/unit_tests/test_ssrf_protection.py::TestRealWorldURLs::test_ngrok_url 
--deselect 
tests/unit_tests/language_models/chat_models/test_benchmark.py::test_benchmark_model
 --deselect tests/unit_tests/utils/test_uuid_utils.py::test_uuid7 --deselect 
tests/unit_tests/runnables/test_tracing_interops.py::test_config_traceable_handoff
 --deselect 
tests/unit_tests/runnables/test_tracing_interops.py::test_config_traceable_async_handoff
 --deselect 
"tests/unit_tests/runnables/test_tracing_interops.py::test_tree_is_constructed[ls]"
 --deselect 
"tests/unit_tests/runnables/test_tracing_interops.py::test_tree_is_constructed[lc]"
 --deselect tests/unit_tests/runnables/test_traci
 ng_interops.py::test_traceable_parent_run_map_cleanup --deselect 
tests/unit_tests/runnables/test_tracing_interops.py::test_traceable_parent_run_map_cleanup_with_sibling_children
 --deselect 
tests/unit_tests/runnables/test_tracing_interops.py::test_traceable_parent_run_map_no_runttree_accumulation
 --deselect 
tests/unit_tests/runnables/test_tracing_interops.py::TestLangsmithInheritableTracingDefaultsInConfigure::test_inheritable_metadata_concurrent_invocations_remain_isolated
 --deselect 
"tests/unit_tests/tracers/test_address.py::test_ambient_destination[parent-destination0]"
 --deselect 
"tests/unit_tests/tracers/test_address.py::test_ambient_destination[parent-destination1]"
 --deselect 
tests/unit_tests/tracers/test_address.py::test_parent_destination_overrides_tracer
 --deselect 
"tests/unit_tests/tracers/test_address.py::test_tracer_kwargs_parent_destination[destination0]"
 --deselect 
"tests/unit_tests/tracers/test_address.py::test_tracer_kwargs_parent_destination[destination1]"
 --deselec
 t 
tests/unit_tests/tracers/test_address.py::test_addressed_parent_replica_inherited_outside_context
 --deselect 
tests/unit_tests/tracers/test_base_tracer.py::test_traceable_to_tracing 
--deselect 
tests/unit_tests/tracers/test_langchain.py::test_tracer_with_run_tree_parent
 
 %files %python_files
 %doc README.md

++++++ langchain_core-1.7.1.tar.gz -> langchain_core-1.7.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_core-1.7.1/PKG-INFO 
new/langchain_core-1.7.4/PKG-INFO
--- old/langchain_core-1.7.1/PKG-INFO   2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_core-1.7.4/PKG-INFO   2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.5
 Name: langchain-core
-Version: 1.7.1
+Version: 1.7.4
 Summary: Building applications with LLMs through composability
 Project-URL: Homepage, https://docs.langchain.com/
 Project-URL: Documentation, 
https://reference.langchain.com/python/langchain_core/
@@ -24,6 +24,7 @@
 Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Requires-Python: <4.0.0,>=3.10.0
+Requires-Dist: certifi>=2026.10.19
 Requires-Dist: httpx>=0.23.0
 Requires-Dist: jsonpatch>=1.33.0
 Requires-Dist: langchain-protocol>=0.0.20
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_core-1.7.1/langchain_core/messages/__init__.py 
new/langchain_core-1.7.4/langchain_core/messages/__init__.py
--- old/langchain_core-1.7.1/langchain_core/messages/__init__.py        
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_core-1.7.4/langchain_core/messages/__init__.py        
2020-02-02 01:00:00.000000000 +0100
@@ -48,6 +48,11 @@
     from langchain_core.messages.function import FunctionMessage, 
FunctionMessageChunk
     from langchain_core.messages.human import HumanMessage, HumanMessageChunk
     from langchain_core.messages.modifier import RemoveMessage
+    from langchain_core.messages.notification import (
+        NotificationMetadata,
+        add_notification_metadata,
+        get_notification_metadata,
+    )
     from langchain_core.messages.system import SystemMessage, 
SystemMessageChunk
     from langchain_core.messages.tool import (
         ToolCall,
@@ -95,6 +100,7 @@
     "MessageLikeRepresentation",
     "NonStandardAnnotation",
     "NonStandardContentBlock",
+    "NotificationMetadata",
     "OutputTokenDetails",
     "PlainTextContentBlock",
     "ReasoningContentBlock",
@@ -112,6 +118,7 @@
     "UsageMetadata",
     "VideoContentBlock",
     "_message_from_dict",
+    "add_notification_metadata",
     "convert_to_messages",
     "convert_to_openai_data_block",
     "convert_to_openai_image_block",
@@ -119,6 +126,7 @@
     "ensure_id",
     "filter_messages",
     "get_buffer_string",
+    "get_notification_metadata",
     "is_data_content_block",
     "merge_content",
     "merge_message_runs",
@@ -149,6 +157,9 @@
     "FunctionMessageChunk": "function",
     "HumanMessage": "human",
     "HumanMessageChunk": "human",
+    "NotificationMetadata": "notification",
+    "add_notification_metadata": "notification",
+    "get_notification_metadata": "notification",
     "NonStandardAnnotation": "content",
     "NonStandardContentBlock": "content",
     "OutputTokenDetails": "ai",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_core-1.7.1/langchain_core/messages/base.py 
new/langchain_core-1.7.4/langchain_core/messages/base.py
--- old/langchain_core-1.7.1/langchain_core/messages/base.py    2020-02-02 
01:00:00.000000000 +0100
+++ new/langchain_core-1.7.4/langchain_core/messages/base.py    2020-02-02 
01:00:00.000000000 +0100
@@ -379,6 +379,9 @@
     """
     merged: str | list[str | dict[Any, Any]]
     merged = "" if first_content is None else first_content
+    if isinstance(merged, list):
+        # The branches below append to it, which would change the first 
message.
+        merged = merged.copy()
 
     for content in contents:
         # If current is a string
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_core-1.7.1/langchain_core/messages/block_translators/anthropic.py 
new/langchain_core-1.7.4/langchain_core/messages/block_translators/anthropic.py
--- 
old/langchain_core-1.7.1/langchain_core/messages/block_translators/anthropic.py 
    2020-02-02 01:00:00.000000000 +0100
+++ 
new/langchain_core-1.7.4/langchain_core/messages/block_translators/anthropic.py 
    2020-02-02 01:00:00.000000000 +0100
@@ -484,12 +484,19 @@
                 yield server_tool_result
 
             else:
-                new_block: types.NonStandardContentBlock = {
-                    "type": "non_standard",
-                    "value": block,
-                }
-                if "index" in new_block["value"]:
-                    new_block["index"] = new_block["value"].pop("index")
+                if "index" in block:
+                    # Copy before lifting `index` off the block: `block` is the
+                    # caller's own dict from `message.content`, and `.pop` 
would
+                    # otherwise mutate it in place.
+                    value = block.copy()
+                    index = value.pop("index")
+                    new_block: types.NonStandardContentBlock = {
+                        "type": "non_standard",
+                        "value": value,
+                        "index": index,
+                    }
+                else:
+                    new_block = {"type": "non_standard", "value": block}
                 yield new_block
 
     return list(_iter_blocks())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_core-1.7.1/langchain_core/messages/notification.py 
new/langchain_core-1.7.4/langchain_core/messages/notification.py
--- old/langchain_core-1.7.1/langchain_core/messages/notification.py    
1970-01-01 01:00:00.000000000 +0100
+++ new/langchain_core-1.7.4/langchain_core/messages/notification.py    
2020-02-02 01:00:00.000000000 +0100
@@ -0,0 +1,92 @@
+"""Metadata for messages the agent harness adds to a conversation."""
+
+from typing import Any
+
+from typing_extensions import NotRequired, TypedDict
+
+from langchain_core.messages.base import BaseMessage
+from langchain_core.messages.human import HumanMessage
+
+
+class NotificationMetadata(TypedDict):
+    """Which feature added a message, and that feature's fields.
+
+    !!! version-added "Added in `langchain-core` 1.7.2"
+    """
+
+    source: str
+    """The feature that added the message, e.g. `'async_subagent'`."""
+
+    data: NotRequired[dict[str, Any]]
+    """The feature's fields, documented by that feature."""
+
+
+def add_notification_metadata(
+    message: HumanMessage, *, source: str, data: dict[str, Any] | None = None
+) -> HumanMessage:
+    """Return a copy of `message` marked as added by the agent harness.
+
+    Use it for a human message the user didn't type, such as a background task
+    update or a conversation summary. It sets `additional_kwargs["lc_source"]`
+    and, when `data` is given, `additional_kwargs["lc_data"]`. Any earlier
+    `lc_source` and `lc_data` are removed first. LangChain's chat model
+    integrations don't send these keys to the model.
+
+    It doesn't change the content. Write the content the model reads yourself.
+
+    Anyone who can send input to a thread can set these keys. Don't use them to
+    decide whether to trust a message.
+
+    `merge_message_runs` and chunk addition join a notification with adjacent
+    human messages; the merged message keeps the metadata and concatenates
+    `lc_source`.
+
+    !!! version-added "Added in `langchain-core` 1.7.2"
+
+    Args:
+        message: The message to mark.
+        source: The feature that added the message.
+        data: The feature's fields. Must be JSON-serializable.
+
+    Returns:
+        A shallow copy of `message`, with the same `id`.
+
+    Example:
+        ```python
+        from langchain_core.messages import HumanMessage, 
add_notification_metadata
+
+        message = add_notification_metadata(
+            HumanMessage("Background task t_42 finished."),
+            source="async_subagent",
+            data={"task_id": "t_42", "status": "success"},
+        )
+        ```
+    """
+    kwargs = {k: v for k, v in message.additional_kwargs.items() if k != 
"lc_data"}
+    kwargs["lc_source"] = source
+    if data is not None:
+        kwargs["lc_data"] = dict(data)
+    return message.model_copy(update={"additional_kwargs": kwargs})
+
+
+def get_notification_metadata(message: BaseMessage) -> NotificationMetadata | 
None:
+    """Return the notification metadata of `message`.
+
+    !!! version-added "Added in `langchain-core` 1.7.2"
+
+    Args:
+        message: The message to read.
+
+    Returns:
+        The metadata `add_notification_metadata` set, or `None` for a message
+            without a string `lc_source`. `data` is a copy of `lc_data`, left 
out
+            unless `lc_data` is a dict.
+    """
+    source = message.additional_kwargs.get("lc_source")
+    if not isinstance(source, str):
+        return None
+    metadata = NotificationMetadata(source=source)
+    data = message.additional_kwargs.get("lc_data")
+    if isinstance(data, dict):
+        metadata["data"] = dict(data)
+    return metadata
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_core-1.7.1/langchain_core/version.py 
new/langchain_core-1.7.4/langchain_core/version.py
--- old/langchain_core-1.7.1/langchain_core/version.py  2020-02-02 
01:00:00.000000000 +0100
+++ new/langchain_core-1.7.4/langchain_core/version.py  2020-02-02 
01:00:00.000000000 +0100
@@ -1,3 +1,3 @@
 """Version information for `langchain-core`."""
 
-VERSION = "1.7.1"
+VERSION = "1.7.4"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_core-1.7.1/pyproject.toml 
new/langchain_core-1.7.4/pyproject.toml
--- old/langchain_core-1.7.1/pyproject.toml     2020-02-02 01:00:00.000000000 
+0100
+++ new/langchain_core-1.7.4/pyproject.toml     2020-02-02 01:00:00.000000000 
+0100
@@ -21,11 +21,13 @@
     "Topic :: Software Development :: Libraries :: Python Modules",
 ]
 
-version = "1.7.1"
+version = "1.7.4"
 requires-python = ">=3.10.0,<4.0.0"
 dependencies = [
     "langsmith>=0.14.9,<1.0.0",
     "httpx>=0.23.0",
+    # Remove once our minimum httpx version requires certifi>=2026.10.19.
+    "certifi>=2026.10.19",
     "tenacity!=8.4.0,>=8.1.0",
     "jsonpatch>=1.33.0",
     "PyYAML>=5.3.0",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_core-1.7.1/tests/unit_tests/messages/block_translators/test_anthropic.py
 
new/langchain_core-1.7.4/tests/unit_tests/messages/block_translators/test_anthropic.py
--- 
old/langchain_core-1.7.1/tests/unit_tests/messages/block_translators/test_anthropic.py
      2020-02-02 01:00:00.000000000 +0100
+++ 
new/langchain_core-1.7.4/tests/unit_tests/messages/block_translators/test_anthropic.py
      2020-02-02 01:00:00.000000000 +0100
@@ -192,6 +192,36 @@
     assert message.content != expected_content  # check no mutation
 
 
+def test_convert_to_v1_from_anthropic_index_no_mutation() -> None:
+    """`index` is lifted onto the `non_standard` wrapper without mutating 
`content`.
+
+    Regression test: the `else` branch built `non_standard["value"]` from the
+    original block dict (not a copy) and then `.pop("index")`'d off of it, 
deleting
+    `index` from `message.content` as a side effect of reading 
`.content_blocks`.
+    """
+    original_block = {"type": "something_else", "foo": "bar", "index": 3}
+    message = AIMessage(
+        content=[dict(original_block)],
+        response_metadata={"model_provider": "anthropic"},
+    )
+
+    first = message.content_blocks
+    second = message.content_blocks
+
+    assert message.content == [original_block]
+    assert (
+        first
+        == second
+        == [
+            {
+                "type": "non_standard",
+                "value": {"type": "something_else", "foo": "bar"},
+                "index": 3,
+            }
+        ]
+    )
+
+
 def test_convert_to_v1_from_anthropic_chunk() -> None:
     chunks = [
         AIMessageChunk(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_core-1.7.1/tests/unit_tests/messages/block_translators/test_bedrock.py
 
new/langchain_core-1.7.4/tests/unit_tests/messages/block_translators/test_bedrock.py
--- 
old/langchain_core-1.7.1/tests/unit_tests/messages/block_translators/test_bedrock.py
        2020-02-02 01:00:00.000000000 +0100
+++ 
new/langchain_core-1.7.4/tests/unit_tests/messages/block_translators/test_bedrock.py
        2020-02-02 01:00:00.000000000 +0100
@@ -129,6 +129,42 @@
     assert message.content_blocks == expected_content
 
 
+def test_convert_to_v1_from_bedrock_index_no_mutation() -> None:
+    """`bedrock` inherits `_convert_to_v1_from_anthropic`, so it inherits this 
fix too.
+
+    Regression test: the `else` branch built `non_standard["value"]` from the
+    original block dict (not a copy) and then `.pop("index")`'d off of it, 
deleting
+    `index` from `message.content` as a side effect of reading 
`.content_blocks`.
+    """
+    original_block = {"type": "something_else", "foo": "bar", "index": 3}
+    message = AIMessage(
+        content=[dict(original_block)],
+        # model_name must mention "claude" -- translate_content() only routes
+        # through _convert_to_v1_from_bedrock() for Anthropic-backed models,
+        # else it falls back to best-effort parsing.
+        response_metadata={
+            "model_provider": "bedrock",
+            "model_name": "us.anthropic.claude-sonnet-4-20250514-v1:0",
+        },
+    )
+
+    first = message.content_blocks
+    second = message.content_blocks
+
+    assert message.content == [original_block]
+    assert (
+        first
+        == second
+        == [
+            {
+                "type": "non_standard",
+                "value": {"type": "something_else", "foo": "bar"},
+                "index": 3,
+            }
+        ]
+    )
+
+
 def test_convert_to_v1_from_bedrock_chunk() -> None:
     chunks = [
         AIMessageChunk(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_core-1.7.1/tests/unit_tests/messages/test_imports.py 
new/langchain_core-1.7.4/tests/unit_tests/messages/test_imports.py
--- old/langchain_core-1.7.1/tests/unit_tests/messages/test_imports.py  
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_core-1.7.4/tests/unit_tests/messages/test_imports.py  
2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,9 @@
 from langchain_core.messages import __all__
 
 EXPECTED_ALL = [
+    "NotificationMetadata",
+    "add_notification_metadata",
+    "get_notification_metadata",
     "MessageLikeRepresentation",
     "_message_from_dict",
     "AIMessage",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_core-1.7.1/tests/unit_tests/messages/test_notification.py 
new/langchain_core-1.7.4/tests/unit_tests/messages/test_notification.py
--- old/langchain_core-1.7.1/tests/unit_tests/messages/test_notification.py     
1970-01-01 01:00:00.000000000 +0100
+++ new/langchain_core-1.7.4/tests/unit_tests/messages/test_notification.py     
2020-02-02 01:00:00.000000000 +0100
@@ -0,0 +1,86 @@
+from langchain_core.messages import (
+    HumanMessage,
+    add_notification_metadata,
+    get_notification_metadata,
+    messages_from_dict,
+    messages_to_dict,
+)
+
+
+def test_metadata_is_added_to_a_copy() -> None:
+    original = HumanMessage("done", id="msg_1", additional_kwargs={"other": 1})
+
+    message = add_notification_metadata(
+        original, source="async_subagent", data={"task_id": "t_42"}
+    )
+
+    assert message.id == "msg_1"
+    assert message.content == "done"
+    assert message.additional_kwargs == {
+        "other": 1,
+        "lc_source": "async_subagent",
+        "lc_data": {"task_id": "t_42"},
+    }
+    assert original.additional_kwargs == {"other": 1}
+
+
+def test_metadata_is_read_back() -> None:
+    message = add_notification_metadata(
+        HumanMessage("done"), source="async_subagent", data={"task_id": "t_42"}
+    )
+
+    assert get_notification_metadata(message) == {
+        "source": "async_subagent",
+        "data": {"task_id": "t_42"},
+    }
+
+
+def test_marking_again_replaces_source_and_data() -> None:
+    message = add_notification_metadata(
+        HumanMessage("x"), source="first", data={"a": 1}
+    )
+
+    remarked = add_notification_metadata(message, source="second")
+
+    assert get_notification_metadata(remarked) == {"source": "second"}
+
+
+def test_data_is_copied() -> None:
+    data = {"status": "success"}
+    message = add_notification_metadata(HumanMessage("x"), source="s", 
data=data)
+    data["status"] = "error"
+
+    assert get_notification_metadata(message) == {
+        "source": "s",
+        "data": {"status": "success"},
+    }
+
+
+def test_messages_without_a_string_source_have_no_metadata() -> None:
+    assert get_notification_metadata(HumanMessage("hi")) is None
+    assert (
+        get_notification_metadata(HumanMessage("x", 
additional_kwargs={"lc_source": 1}))
+        is None
+    )
+
+
+def test_non_dict_data_is_left_out() -> None:
+    kwargs = {"lc_source": "s", "lc_data": "not a dict"}
+
+    assert get_notification_metadata(HumanMessage("x", 
additional_kwargs=kwargs)) == {
+        "source": "s"
+    }
+
+
+def test_metadata_survives_serialization() -> None:
+    message = add_notification_metadata(
+        HumanMessage("done", id="msg_1"), source="async_subagent", data={"a": 
1}
+    )
+
+    [restored] = messages_from_dict(messages_to_dict([message]))
+
+    assert restored == message
+    assert get_notification_metadata(restored) == {
+        "source": "async_subagent",
+        "data": {"a": 1},
+    }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_core-1.7.1/tests/unit_tests/runnables/test_runnable.py 
new/langchain_core-1.7.4/tests/unit_tests/runnables/test_runnable.py
--- old/langchain_core-1.7.1/tests/unit_tests/runnables/test_runnable.py        
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_core-1.7.4/tests/unit_tests/runnables/test_runnable.py        
2020-02-02 01:00:00.000000000 +0100
@@ -30,12 +30,14 @@
 from langchain_core.callbacks import BaseCallbackHandler
 from langchain_core.callbacks.manager import (
     AsyncCallbackManagerForRetrieverRun,
+    CallbackManagerForLLMRun,
     CallbackManagerForRetrieverRun,
     atrace_as_chain_group,
     trace_as_chain_group,
 )
 from langchain_core.documents import Document
 from langchain_core.language_models import (
+    BaseChatModel,
     FakeListChatModel,
     FakeListLLM,
     FakeStreamingListLLM,
@@ -50,7 +52,8 @@
     CommaSeparatedListOutputParser,
     StrOutputParser,
 )
-from langchain_core.outputs.chat_generation import ChatGeneration
+from langchain_core.outputs.chat_generation import ChatGeneration, 
ChatGenerationChunk
+from langchain_core.outputs.chat_result import ChatResult
 from langchain_core.outputs.llm_result import LLMResult
 from langchain_core.prompt_values import ChatPromptValue, StringPromptValue
 from langchain_core.prompts import (
@@ -6064,3 +6067,67 @@
     schema = sequence.get_output_jsonschema()
     assert set(schema["properties"]) == {"a"}
     assert "a" in schema["required"]
+
+
+class _ReasonsThenAnswers(BaseChatModel):
+    @property
+    def _llm_type(self) -> str:
+        return "reasons-then-answers"
+
+    @override
+    def _generate(
+        self,
+        messages: list[BaseMessage],
+        stop: list[str] | None = None,
+        run_manager: CallbackManagerForLLMRun | None = None,
+        **kwargs: Any,
+    ) -> ChatResult:
+        raise NotImplementedError
+
+    @override
+    def _stream(
+        self,
+        messages: list[BaseMessage],
+        stop: list[str] | None = None,
+        run_manager: CallbackManagerForLLMRun | None = None,
+        **kwargs: Any,
+    ) -> Iterator[ChatGenerationChunk]:
+        yield ChatGenerationChunk(
+            message=AIMessageChunk(content=[{"type": "thinking", "thinking": 
"hmm"}])
+        )
+        for text in ["Hel", "lo", " world"]:
+            yield ChatGenerationChunk(message=AIMessageChunk(content=text))
+
+
+class _ModelOutput(BaseCallbackHandler):
+    def __init__(self) -> None:
+        self.content: Any = None
+
+    @override
+    def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
+        generation = response.generations[0][0]
+        assert isinstance(generation, ChatGeneration)
+        self.content = generation.message.content
+
+
[email protected]("use_async", [False, True])
+async def test_streaming_a_reasoning_block_then_text_adds_the_text_once(
+    *, use_async: bool
+) -> None:
+    received: list[Any] = []
+
+    def record(message: BaseMessage) -> BaseMessage:
+        received.append(message.content)
+        return message
+
+    chain = _ReasonsThenAnswers() | RunnableLambda(record)
+    model_output = _ModelOutput()
+    config: RunnableConfig = {"callbacks": [model_output]}
+    if use_async:
+        _ = [chunk async for chunk in chain.astream("hi", config)]
+    else:
+        _ = list(chain.stream("hi", config))
+
+    content = [{"type": "thinking", "thinking": "hmm"}, "Hello world"]
+    assert received == [content]
+    assert model_output.content == content
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_core-1.7.1/tests/unit_tests/test_messages.py 
new/langchain_core-1.7.4/tests/unit_tests/test_messages.py
--- old/langchain_core-1.7.1/tests/unit_tests/test_messages.py  2020-02-02 
01:00:00.000000000 +0100
+++ new/langchain_core-1.7.4/tests/unit_tests/test_messages.py  2020-02-02 
01:00:00.000000000 +0100
@@ -1110,6 +1110,21 @@
     assert actual == expected
 
 
[email protected](
+    "first",
+    [
+        [{"type": "text", "text": "a"}],
+        ["x"],
+    ],
+)
+def test_merge_content_leaves_the_first_list_unchanged(
+    first: list[str | dict[str, Any]],
+) -> None:
+    original = list(first)
+    merge_content(first, "b")
+    assert first == original
+
+
 def test_tool_message_content() -> None:
     ToolMessage("foo", tool_call_id="1")
     ToolMessage(["foo"], tool_call_id="1")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_core-1.7.1/uv.lock 
new/langchain_core-1.7.4/uv.lock
--- old/langchain_core-1.7.1/uv.lock    2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_core-1.7.4/uv.lock    2020-02-02 01:00:00.000000000 +0100
@@ -1130,9 +1130,10 @@
 
 [[package]]
 name = "langchain-core"
-version = "1.7.1"
+version = "1.7.4"
 source = { editable = "." }
 dependencies = [
+    { name = "certifi" },
     { name = "httpx" },
     { name = "jsonpatch" },
     { name = "langchain-protocol" },
@@ -1181,6 +1182,7 @@
 
 [package.metadata]
 requires-dist = [
+    { name = "certifi", specifier = ">=2026.10.19" },
     { name = "httpx", specifier = ">=0.23.0" },
     { name = "jsonpatch", specifier = ">=1.33.0" },
     { name = "langchain-protocol", specifier = ">=0.0.20" },
@@ -1265,7 +1267,7 @@
     { name = "numpy", marker = "python_full_version >= '3.13'", specifier = 
">=2.1.0" },
     { name = "pytest", specifier = ">=9.0.3" },
     { name = "pytest-asyncio", specifier = ">=1.3.0" },
-    { name = "pytest-benchmark" },
+    { name = "pytest-benchmark", specifier = ">=5.3.1" },
     { name = "pytest-codspeed" },
     { name = "pytest-recording" },
     { name = "pytest-socket", specifier = ">=0.7.0" },
@@ -2399,15 +2401,15 @@
 
 [[package]]
 name = "pytest-benchmark"
-version = "5.3.0"
+version = "5.3.1"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
     { name = "py-cpuinfo2" },
     { name = "pytest" },
 ]
-sdist = { url = 
"https://files.pythonhosted.org/packages/63/8f/83a15e40dbc34a580ee56eb56983cae5394c6e94d50cf28fe268e457be25/pytest_benchmark-5.3.0.tar.gz";,
 hash = 
"sha256:358444d4e89be901ee2b6404fb043ac3d7684002ad7f3563cc153fca6339c965", size 
= 375410, upload-time = "2026-08-23T17:45:08.891Z" }
+sdist = { url = 
"https://files.pythonhosted.org/packages/13/d1/1bc61155de46f25473a06476ad1b3a94f96d40d49cfa98f82c1184117808/pytest_benchmark-5.3.1.tar.gz";,
 hash = 
"sha256:c1ee70a58f9bfb1ac955cc634247a340b0df7bcb949071cfca396f16c6fc3516", size 
= 385467, upload-time = "2026-11-01T03:32:10.048Z" }
 wheels = [
-    { url = 
"https://files.pythonhosted.org/packages/eb/42/7e80f7cfa191e0a766d1de99b4661847415ad5db34f8209d81fd42175b59/pytest_benchmark-5.3.0-py3-none-any.whl";,
 hash = 
"sha256:920ab1dfcffa718d49aa15ba144c7e357bda59216a0dc308016cc1c7236f719d", size 
= 48401, upload-time = "2026-08-23T17:45:07.094Z" },
+    { url = 
"https://files.pythonhosted.org/packages/78/75/919ac85ad3f762e45aa369d2f68d40390e316474b6bb8434aeb6e3c8bf8b/pytest_benchmark-5.3.1-py3-none-any.whl";,
 hash = 
"sha256:d7732a9952b1c4a4a32277adc693739126eab37afc3a711847f53805ccadc020", size 
= 49142, upload-time = "2026-11-01T03:32:08.359Z" },
 ]
 
 [[package]]

Reply via email to