Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-langchain-openai for 
openSUSE:Factory checked in at 2026-09-24 22:56:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-langchain-openai (Old)
 and      /work/SRC/openSUSE:Factory/.python-langchain-openai.new.383539 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-langchain-openai"

Thu Sep 24 22:56:36 2026 rev:14 rq:1380234 version:1.6.6

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-langchain-openai/python-langchain-openai.changes
  2026-09-22 21:58:35.431507076 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-langchain-openai.new.383539/python-langchain-openai.changes
      2026-09-24 22:59:22.366028656 +0200
@@ -1,0 +2,6 @@
+Wed Sep 23 18:43:25 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to version 1.6.6 (includes 1.6.4; patch
+  releases, no upstream changelogs)
+
+-------------------------------------------------------------------

Old:
----
  langchain_openai-1.6.3.tar.gz

New:
----
  langchain_openai-1.6.6.tar.gz

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

Other differences:
------------------
++++++ python-langchain-openai.spec ++++++
--- /var/tmp/diff_new_pack.T5L6Jz/_old  2026-09-24 22:59:23.066057929 +0200
+++ /var/tmp/diff_new_pack.T5L6Jz/_new  2026-09-24 22:59:23.067057971 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           python-langchain-openai
-Version:        1.6.3
+Version:        1.6.6
 Release:        0
 Summary:        An integration package connecting OpenAI and LangChain
 License:        MIT

++++++ langchain_openai-1.6.3.tar.gz -> langchain_openai-1.6.6.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_openai-1.6.3/PKG-INFO 
new/langchain_openai-1.6.6/PKG-INFO
--- old/langchain_openai-1.6.3/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_openai-1.6.6/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.5
 Name: langchain-openai
-Version: 1.6.3
+Version: 1.6.6
 Summary: An integration package connecting OpenAI and LangChain
 Project-URL: Homepage, 
https://docs.langchain.com/oss/python/integrations/providers/openai
 Project-URL: Documentation, 
https://reference.langchain.com/python/integrations/langchain_openai/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_openai-1.6.3/langchain_openai/_version.py 
new/langchain_openai-1.6.6/langchain_openai/_version.py
--- old/langchain_openai-1.6.3/langchain_openai/_version.py     2020-02-02 
01:00:00.000000000 +0100
+++ new/langchain_openai-1.6.6/langchain_openai/_version.py     2020-02-02 
01:00:00.000000000 +0100
@@ -1,3 +1,3 @@
 """Version information for `langchain-openai`."""
 
-__version__ = "1.6.3"
+__version__ = "1.6.6"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_openai-1.6.3/langchain_openai/chat_models/_compat.py 
new/langchain_openai-1.6.6/langchain_openai/chat_models/_compat.py
--- old/langchain_openai-1.6.3/langchain_openai/chat_models/_compat.py  
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_openai-1.6.6/langchain_openai/chat_models/_compat.py  
2020-02-02 01:00:00.000000000 +0100
@@ -74,6 +74,17 @@
 from langchain_core.messages import AIMessage, is_data_content_block
 from langchain_core.messages import content as types
 
+
+def _unwrap_non_standard(block: dict) -> dict:
+    """Unwrap a provider-native dictionary from a standard content block."""
+    if block.get("type") == "non_standard" and isinstance(
+        value := block.get("value"),
+        dict,
+    ):
+        return value
+    return block
+
+
 _FUNCTION_CALL_IDS_MAP_KEY = "__openai_function_call_ids__"
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_openai-1.6.3/langchain_openai/chat_models/base.py 
new/langchain_openai-1.6.6/langchain_openai/chat_models/base.py
--- old/langchain_openai-1.6.3/langchain_openai/chat_models/base.py     
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_openai-1.6.6/langchain_openai/chat_models/base.py     
2020-02-02 01:00:00.000000000 +0100
@@ -163,6 +163,7 @@
     _convert_from_v1_to_chat_completions,
     _convert_from_v1_to_responses,
     _convert_to_v03_ai_message,
+    _unwrap_non_standard,
 )
 from langchain_openai.data._profiles import _PROFILES
 
@@ -326,12 +327,79 @@
     return content
 
 
+_ADDITIONAL_TOOLS_BLOCK_TYPE = "additional_tools"
+"""Responses API input item that adds tools partway through a conversation."""
+
+
+def _is_ai_role(role: str | None) -> bool:
+    """Return whether a message's role is the assistant's.
+
+    Assistant content is replayed model output, not a caller's instruction, so 
it is
+    exempt from the placement checks a caller's own blocks are held to.
+    """
+    return str(role).lower().startswith("ai")
+
+
+def _is_system_role(role: str | None) -> bool:
+    """Return whether a message's role carries provider instructions.
+
+    `SystemMessage` reports `"system"` whether or not it is later emitted with
+    OpenAI's `developer` role, so one check covers both spellings.
+    """
+    return role in ("system", "developer")
+
+
+def _raise_if_additional_tools(content: Any, reason: str) -> None:
+    """Reject an `additional_tools` block that cannot work where it was placed.
+
+    The block only reaches the wire as a Responses top-level input item 
carried on
+    a system message. Anywhere else it is this provider's own block type in a
+    position this provider forbids, which the error taxonomy makes loud rather 
than
+    silent: nothing routes a request to the Responses API based on message 
content,
+    so a silent drop would make the broken case the default outcome.
+
+    Args:
+        content: The message's content.
+        reason: Sentence explaining why this placement cannot work, and how to 
fix
+            it. Appended to the error.
+
+    Raises:
+        ValueError: If an `additional_tools` block is present, in either 
spelling.
+    """
+    if not isinstance(content, list):
+        return
+    for raw_block in content:
+        if (
+            isinstance(raw_block, dict)
+            and _unwrap_non_standard(raw_block).get("type")
+            == _ADDITIONAL_TOOLS_BLOCK_TYPE
+        ):
+            msg = f"`additional_tools` {reason}"
+            raise ValueError(msg)
+
+
 def _format_message_content(
     content: Any,
     api: Literal["chat/completions", "responses"] = "chat/completions",
     role: str | None = None,
 ) -> Any:
     """Format message content."""
+    if _is_ai_role(role):
+        # Replayed assistant output; `additional_tools` is also an output 
item, so
+        # an echoed one must survive a round trip rather than abort the 
request.
+        pass
+    elif not _is_system_role(role):
+        _raise_if_additional_tools(
+            content,
+            "must be carried on a `SystemMessage`. OpenAI restricts the input 
item "
+            'to `role: "developer"`, so it cannot be sent on any other 
message.',
+        )
+    elif api == "chat/completions":
+        _raise_if_additional_tools(
+            content,
+            "requires the Responses API and cannot be sent via Chat 
Completions. "
+            "Set `use_responses_api=True`.",
+        )
     if content and isinstance(content, list):
         formatted_content = []
         for block in content:
@@ -3135,6 +3203,39 @@
 
         See `bind_tools` for more.
 
+    ??? info "Mid-conversation tool additions"
+
+        ```python
+        from langchain_core.messages import HumanMessage, SystemMessage
+        from langchain_openai import ChatOpenAI
+
+        model = ChatOpenAI(model="gpt-6-astra", use_responses_api=True)
+        model.invoke(
+            [
+                HumanMessage("What time is it?"),
+                SystemMessage(
+                    [
+                        {
+                            "type": "additional_tools",
+                            "role": "developer",
+                            "tools": [
+                                {
+                                    "type": "function",
+                                    "name": "get_time",
+                                    "description": "Get the current time.",
+                                    "parameters": {
+                                        "type": "object",
+                                        "properties": {},
+                                    },
+                                }
+                            ],
+                        }
+                    ]
+                ),
+            ]
+        )
+        ```
+
     ??? info "Built-in (server-side) tools"
 
         You can access [built-in 
tools](https://platform.openai.com/docs/guides/tools?api-mode=responses)
@@ -4993,6 +5094,21 @@
                         new_blocks.append(block)
                     elif block["type"] in non_message_item_types:
                         input_.append(block)
+                    elif block["type"] == _ADDITIONAL_TOOLS_BLOCK_TYPE:
+                        if isinstance(lc_msg, SystemMessage):
+                            input_.append(block)
+                    elif _is_system_role(msg["role"]):
+                        # System content is a closed set here, so an 
unrecognized
+                        # block is a mistake rather than something to forward.
+                        # User content keeps its long-standing silent drop, 
where
+                        # the set is open and warning would be noise.
+                        warnings.warn(
+                            f"Content block {block['type']!r} was dropped from 
a "
+                            "system message: the Responses API has no input 
item "
+                            "of that type, so it cannot be placed in the 
request.",
+                            UserWarning,
+                            stacklevel=2,
+                        )
                     else:
                         pass
                 msg["content"] = new_blocks
@@ -5338,6 +5454,13 @@
         response = _coerce_chunk_response(chunk.response)
         id = response.id
         response_metadata["id"] = response.id  # Backwards compatibility
+    elif chunk.type == "response.failed":
+        response = _coerce_chunk_response(chunk.response)
+        error_msg = str(response.error or f"Response {response.id} failed.")
+        raise ValueError(error_msg)
+    elif chunk.type == "error":
+        error_msg = f"{chunk.code}: {chunk.message}" if chunk.code else 
chunk.message
+        raise ValueError(error_msg)
     elif chunk.type in ("response.completed", "response.incomplete"):
         response = _coerce_chunk_response(chunk.response)
         msg = cast(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_openai-1.6.3/langchain_openai/data/_profiles.py 
new/langchain_openai-1.6.6/langchain_openai/data/_profiles.py
--- old/langchain_openai-1.6.3/langchain_openai/data/_profiles.py       
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_openai-1.6.6/langchain_openai/data/_profiles.py       
2020-02-02 01:00:00.000000000 +0100
@@ -1204,6 +1204,78 @@
             "max",
         ],
     },
+    "gpt-6-luna": {
+        "name": "GPT-6 Luna",
+        "release_date": "2026-09-22",
+        "last_updated": "2026-09-22",
+        "open_weights": False,
+        "max_input_tokens": 1050000,
+        "max_output_tokens": 128000,
+        "text_inputs": True,
+        "image_inputs": True,
+        "audio_inputs": False,
+        "pdf_inputs": True,
+        "video_inputs": False,
+        "text_outputs": True,
+        "image_outputs": False,
+        "audio_outputs": False,
+        "video_outputs": False,
+        "reasoning_output": True,
+        "tool_calling": True,
+        "structured_output": True,
+        "attachment": True,
+        "temperature": False,
+        "image_url_inputs": True,
+        "pdf_tool_message": True,
+        "image_tool_message": True,
+        "tool_choice": True,
+        "tool_call_streaming": True,
+        "reasoning_effort_levels": [
+            "none",
+            "low",
+            "medium",
+            "high",
+            "xhigh",
+            "max",
+        ],
+        "reasoning_effort_default": "medium",
+    },
+    "gpt-6-sol": {
+        "name": "GPT-6 Sol",
+        "release_date": "2026-09-22",
+        "last_updated": "2026-09-22",
+        "open_weights": False,
+        "max_input_tokens": 1050000,
+        "max_output_tokens": 128000,
+        "text_inputs": True,
+        "image_inputs": True,
+        "audio_inputs": False,
+        "pdf_inputs": True,
+        "video_inputs": False,
+        "text_outputs": True,
+        "image_outputs": False,
+        "audio_outputs": False,
+        "video_outputs": False,
+        "reasoning_output": True,
+        "tool_calling": True,
+        "structured_output": True,
+        "attachment": True,
+        "temperature": False,
+        "image_url_inputs": True,
+        "pdf_tool_message": True,
+        "image_tool_message": True,
+        "tool_choice": True,
+        "tool_call_streaming": True,
+        "reasoning_effort_levels": [
+            "none",
+            "low",
+            "medium",
+            "high",
+            "xhigh",
+            "max",
+        ],
+        "reasoning_effort_default": "medium",
+    },
     "gpt-image-1": {
         "name": "gpt-image-1",
         "status": "deprecated",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_openai-1.6.3/langchain_openai/data/profile_augmentations.toml 
new/langchain_openai-1.6.6/langchain_openai/data/profile_augmentations.toml
--- old/langchain_openai-1.6.3/langchain_openai/data/profile_augmentations.toml 
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_openai-1.6.6/langchain_openai/data/profile_augmentations.toml 
2020-02-02 01:00:00.000000000 +0100
@@ -117,3 +117,11 @@
 
 [overrides."gpt-6-astra"]
 reasoning_effort_levels = ["low", "medium", "high", "xhigh", "max"]
+
+[overrides."gpt-6-sol"]
+reasoning_effort_levels = ["none", "low", "medium", "high", "xhigh", "max"]
+reasoning_effort_default = "medium"
+
+[overrides."gpt-6-luna"]
+reasoning_effort_levels = ["none", "low", "medium", "high", "xhigh", "max"]
+reasoning_effort_default = "medium"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_openai-1.6.3/pyproject.toml 
new/langchain_openai-1.6.6/pyproject.toml
--- old/langchain_openai-1.6.3/pyproject.toml   2020-02-02 01:00:00.000000000 
+0100
+++ new/langchain_openai-1.6.6/pyproject.toml   2020-02-02 01:00:00.000000000 
+0100
@@ -20,7 +20,7 @@
     "Topic :: Scientific/Engineering :: Artificial Intelligence",
 ]
 
-version = "1.6.3"
+version = "1.6.6"
 requires-python = ">=3.10.0,<4.0.0"
 dependencies = [
     "langchain-core>=1.6.4,<2.0.0",
Binary files 
old/langchain_openai-1.6.3/tests/cassettes/test_system_additional_tools.yaml.gz 
and 
new/langchain_openai-1.6.6/tests/cassettes/test_system_additional_tools.yaml.gz 
differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_openai-1.6.3/tests/integration_tests/chat_models/test_responses_api.py
 
new/langchain_openai-1.6.6/tests/integration_tests/chat_models/test_responses_api.py
--- 
old/langchain_openai-1.6.3/tests/integration_tests/chat_models/test_responses_api.py
        2020-02-02 01:00:00.000000000 +0100
+++ 
new/langchain_openai-1.6.6/tests/integration_tests/chat_models/test_responses_api.py
        2020-02-02 01:00:00.000000000 +0100
@@ -21,6 +21,7 @@
     BaseMessageChunk,
     HumanMessage,
     MessageLikeRepresentation,
+    SystemMessage,
     ToolMessage,
 )
 from langchain_core.tools import tool
@@ -1960,3 +1961,34 @@
     # v2 bridge's default `"stop"` synthesis; provider metadata now
     # passes through unchanged.)
     assert v1.response_metadata == v2.response_metadata
+
+
[email protected]
+def test_system_additional_tools() -> None:
+    model = ChatOpenAI(model="gpt-6-astra", use_responses_api=True)
+    response = model.invoke(
+        [
+            HumanMessage("What time is it?"),
+            SystemMessage(
+                [
+                    {
+                        "type": "additional_tools",
+                        "role": "developer",
+                        "tools": [
+                            {
+                                "type": "function",
+                                "name": "get_time",
+                                "description": "Get the current time.",
+                                "parameters": {
+                                    "type": "object",
+                                    "properties": {},
+                                },
+                            }
+                        ],
+                    }
+                ]
+            ),
+        ]
+    )
+    assert isinstance(response, AIMessage)
+    assert response.tool_calls[0]["name"] == "get_time"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_openai-1.6.3/tests/unit_tests/chat_models/test_base.py 
new/langchain_openai-1.6.6/tests/unit_tests/chat_models/test_base.py
--- old/langchain_openai-1.6.3/tests/unit_tests/chat_models/test_base.py        
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_openai-1.6.6/tests/unit_tests/chat_models/test_base.py        
2020-02-02 01:00:00.000000000 +0100
@@ -2,6 +2,7 @@
 
 from __future__ import annotations
 
+import copy
 import json
 import warnings
 from functools import partial
@@ -5507,6 +5508,223 @@
     assert llm.openai_api_key.get_secret_value() == "provider-key"
 
 
+_ADDITIONAL_TOOLS_BLOCK = {
+    "type": "additional_tools",
+    "role": "developer",
+    "tools": [
+        {
+            "type": "function",
+            "name": "get_customer",
+            "description": "Look up a customer by ID.",
+            "parameters": {
+                "type": "object",
+                "properties": {"customer_id": {"type": "string"}},
+                "required": ["customer_id"],
+                "additionalProperties": False,
+            },
+        }
+    ],
+}
+_FOREIGN_TOOL_CHANGE_BLOCK = {
+    "type": "tool_removal",
+    "tool": {"type": "tool_reference", "name": "get_weather"},
+}
+
+
[email protected]("spelling", ["bare", "non_standard"])
+def test_additional_tools_block_becomes_input_item(spelling: str) -> None:
+    """An `additional_tools` block is hoisted to a top-level Responses input 
item."""
+    block: dict = (
+        _ADDITIONAL_TOOLS_BLOCK
+        if spelling == "bare"
+        else {"type": "non_standard", "value": _ADDITIONAL_TOOLS_BLOCK}
+    )
+    llm = ChatOpenAI(model=OPENAI_TEST_MODEL, use_responses_api=True)
+    payload = llm._get_request_payload(
+        [
+            HumanMessage("Earlier question"),
+            AIMessage("Earlier answer", response_metadata={"id": "resp_123"}),
+            SystemMessage([{"type": "text", "text": "Be concise."}, block]),
+            HumanMessage("Next question"),
+        ]
+    )
+
+    # The item precedes the message it was carried on, matching how the 
Responses
+    # API converter hoists every other non-message input item.
+    assert payload["input"][2] == _ADDITIONAL_TOOLS_BLOCK
+    assert payload["input"][3] == {
+        "role": "system",
+        "content": [{"type": "input_text", "text": "Be concise."}],
+        "type": "message",
+    }
+    assert payload["input"][4]["role"] == "user"
+
+
+def test_additional_tools_block_empties_message() -> None:
+    """A system message carrying only the block leaves no message behind."""
+    llm = ChatOpenAI(model=OPENAI_TEST_MODEL, use_responses_api=True)
+    payload = llm._get_request_payload(
+        [
+            HumanMessage("Earlier question"),
+            SystemMessage([_ADDITIONAL_TOOLS_BLOCK]),
+        ]
+    )
+
+    assert payload["input"] == [
+        {"role": "user", "content": "Earlier question", "type": "message"},
+        _ADDITIONAL_TOOLS_BLOCK,
+    ]
+
+
+def test_additional_tools_block_does_not_mutate_input_content() -> None:
+    """Hoisting the item must leave the caller's own message content 
untouched."""
+    content: list[str | dict] = [
+        {"type": "text", "text": "Be concise."},
+        _ADDITIONAL_TOOLS_BLOCK,
+    ]
+    before = copy.deepcopy(content)
+    llm = ChatOpenAI(model=OPENAI_TEST_MODEL, use_responses_api=True)
+    llm._get_request_payload([HumanMessage("Earlier question"), 
SystemMessage(content)])
+    assert content == before
+
+
[email protected]("spelling", ["bare", "non_standard"])
+def test_additional_tools_block_on_chat_completions_raises(spelling: str) -> 
None:
+    """`additional_tools` requires the Responses API."""
+    block: dict = (
+        _ADDITIONAL_TOOLS_BLOCK
+        if spelling == "bare"
+        else {"type": "non_standard", "value": _ADDITIONAL_TOOLS_BLOCK}
+    )
+    llm = ChatOpenAI(model=OPENAI_TEST_MODEL)
+    with pytest.raises(ValueError, match="use_responses_api=True"):
+        llm._get_request_payload(
+            [HumanMessage("Earlier question"), SystemMessage([block])]
+        )
+
+
[email protected]("spelling", ["bare", "non_standard"])
[email protected]("use_responses_api", [True, False])
[email protected]("message_type", ["human", "tool"])
+def test_additional_tools_block_off_system_message_raises(
+    message_type: str,
+    use_responses_api: bool,
+    spelling: str,
+) -> None:
+    """OpenAI restricts the input item to `role: "developer"`.
+
+    Anywhere but a `SystemMessage` it is this provider's own block in a 
position
+    this provider forbids, so it is raised rather than dropped. Guards against
+    client-supplied content blocks reaching the top-level input list.
+    """
+    block: dict = (
+        _ADDITIONAL_TOOLS_BLOCK
+        if spelling == "bare"
+        else {"type": "non_standard", "value": _ADDITIONAL_TOOLS_BLOCK}
+    )
+    message: BaseMessage = (
+        HumanMessage([block])
+        if message_type == "human"
+        else ToolMessage([block], tool_call_id="call_1")
+    )
+    llm = ChatOpenAI(model=OPENAI_TEST_MODEL, 
use_responses_api=use_responses_api)
+    with pytest.raises(ValueError, match="SystemMessage"):
+        llm._get_request_payload([message])
+
+
+def test_additional_tools_block_on_ai_message_not_rejected() -> None:
+    """`additional_tools` is also a Responses *output* item.
+
+    Replaying an assistant turn that echoes one must not raise; handling the 
output
+    form is out of scope, and out of scope should mean untouched, not fatal.
+    """
+    llm = ChatOpenAI(model=OPENAI_TEST_MODEL, use_responses_api=True)
+    llm._get_request_payload(
+        [
+            HumanMessage("Earlier question"),
+            AIMessage(
+                [{"type": "text", "text": "Sure."}, _ADDITIONAL_TOOLS_BLOCK],
+                response_metadata={"id": "resp_123"},
+            ),
+        ]
+    )
+
+
[email protected]("spelling", ["bare", "non_standard"])
+def test_unrecognized_system_block_dropped_with_warning(spelling: str) -> None:
+    """Responses system content is a closed set, so an unknown block is 
reported."""
+    block: dict = (
+        _FOREIGN_TOOL_CHANGE_BLOCK
+        if spelling == "bare"
+        else {"type": "non_standard", "value": _FOREIGN_TOOL_CHANGE_BLOCK}
+    )
+    llm = ChatOpenAI(model=OPENAI_TEST_MODEL, use_responses_api=True)
+    with pytest.warns(UserWarning, match="tool_removal"):
+        payload = llm._get_request_payload(
+            [
+                HumanMessage("Earlier question"),
+                SystemMessage([{"type": "text", "text": "Be concise."}, 
block]),
+            ]
+        )
+
+    assert payload["input"][-1] == {
+        "role": "system",
+        "content": [{"type": "input_text", "text": "Be concise."}],
+        "type": "message",
+    }
+
+
+def test_unrecognized_user_block_dropped_silently() -> None:
+    """User content is an open set, so dropping an unknown block stays 
quiet."""
+    llm = ChatOpenAI(model=OPENAI_TEST_MODEL, use_responses_api=True)
+    with warnings.catch_warnings():
+        warnings.simplefilter("error")
+        payload = llm._get_request_payload(
+            [
+                HumanMessage(
+                    [
+                        {"type": "text", "text": "Hello"},
+                        {"type": "made_up_block", "foo": "bar"},
+                    ]
+                )
+            ]
+        )
+
+    assert payload["input"] == [
+        {
+            "role": "user",
+            "content": [{"type": "input_text", "text": "Hello"}],
+            "type": "message",
+        },
+    ]
+
+
[email protected]("role", ["system", "human"])
+def test_unrecognized_block_forwarded_on_chat_completions(role: str) -> None:
+    """Chat Completions keeps its long-standing passthrough for unknown blocks.
+
+    `additional_tools` is the one system block it rejects, as that's a common 
mistake
+    (needs responses api). Every other unknown block is passed through as the 
caller
+    wrote it.
+    """
+    message = (
+        SystemMessage([_FOREIGN_TOOL_CHANGE_BLOCK])
+        if role == "system"
+        else HumanMessage([_FOREIGN_TOOL_CHANGE_BLOCK])
+    )
+    llm = ChatOpenAI(model=OPENAI_TEST_MODEL)
+    with warnings.catch_warnings():
+        warnings.simplefilter("error")
+        payload = llm._get_request_payload([message])
+
+    assert payload["messages"] == [
+        {
+            "role": role if role == "system" else "user",
+            "content": [_FOREIGN_TOOL_CHANGE_BLOCK],
+        }
+    ]
+
+
 def test_configuration_update_block_becomes_input_item() -> None:
     """A `configuration_update` block is hoisted out of the message content.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_openai-1.6.3/tests/unit_tests/chat_models/test_responses_stream.py
 
new/langchain_openai-1.6.6/tests/unit_tests/chat_models/test_responses_stream.py
--- 
old/langchain_openai-1.6.3/tests/unit_tests/chat_models/test_responses_stream.py
    2020-02-02 01:00:00.000000000 +0100
+++ 
new/langchain_openai-1.6.6/tests/unit_tests/chat_models/test_responses_stream.py
    2020-02-02 01:00:00.000000000 +0100
@@ -13,6 +13,8 @@
     ResponseContentPartAddedEvent,
     ResponseContentPartDoneEvent,
     ResponseCreatedEvent,
+    ResponseErrorEvent,
+    ResponseFailedEvent,
     ResponseFunctionCallArgumentsDeltaEvent,
     ResponseFunctionCallArgumentsDoneEvent,
     ResponseFunctionToolCallItem,
@@ -30,6 +32,7 @@
     ResponseTextDoneEvent,
 )
 from openai.types.responses.response import Response
+from openai.types.responses.response_error import ResponseError
 from openai.types.responses.response_output_text import ResponseOutputText
 from openai.types.responses.response_reasoning_item import Summary
 from openai.types.responses.response_reasoning_summary_part_added_event import 
(
@@ -50,7 +53,10 @@
 from langchain_openai.chat_models.base import (
     _convert_responses_chunk_to_generation_chunk,
 )
-from tests.unit_tests.chat_models.test_base import MockSyncContextManager
+from tests.unit_tests.chat_models.test_base import (
+    MockAsyncContextManager,
+    MockSyncContextManager,
+)
 
 MODEL = "gpt-5.4"
 
@@ -1231,3 +1237,68 @@
             full = chunk if full is None else full + chunk
     assert isinstance(full, AIMessageChunk)
     assert full.id == "resp_123"
+
+
+def _failed_event(error: ResponseError | None) -> ResponseFailedEvent:
+    created = responses_stream[0]
+    assert isinstance(created, ResponseCreatedEvent)
+    response = created.response.model_copy(update={"status": "failed", 
"error": error})
+    return ResponseFailedEvent(
+        response=response, sequence_number=1, type="response.failed"
+    )
+
+
+_FAILURE_CASES = [
+    (
+        _failed_event(ResponseError(code="server_error", message="Model 
failed.")),
+        "server_error",
+    ),
+    (_failed_event(None), "Response resp_123 failed."),
+    (
+        ResponseErrorEvent(
+            type="error",
+            code="rate_limit_exceeded",
+            message="Rate limit reached.",
+            param=None,
+            sequence_number=1,
+        ),
+        "rate_limit_exceeded: Rate limit reached.",
+    ),
+]
+
+
[email protected](("failure_event", "match"), _FAILURE_CASES)
+def test_responses_stream_raises_on_failure(failure_event: Any, match: str) -> 
None:
+    llm = ChatOpenAI(model=MODEL, use_responses_api=True)
+    mock_client = MagicMock()
+
+    def mock_create(*args: Any, **kwargs: Any) -> MockSyncContextManager:
+        return MockSyncContextManager([responses_stream[0], failure_event])
+
+    mock_client.responses.create = mock_create
+
+    with (
+        patch.object(llm, "root_client", mock_client),
+        pytest.raises(ValueError, match=match),
+    ):
+        list(llm.stream("test"))
+
+
[email protected](("failure_event", "match"), _FAILURE_CASES)
+async def test_responses_astream_raises_on_failure(
+    failure_event: Any, match: str
+) -> None:
+    llm = ChatOpenAI(model=MODEL, use_responses_api=True)
+    mock_client = MagicMock()
+
+    async def mock_create(*args: Any, **kwargs: Any) -> 
MockAsyncContextManager:
+        return MockAsyncContextManager([responses_stream[0], failure_event])
+
+    mock_client.responses.create = mock_create
+
+    with (
+        patch.object(llm, "root_async_client", mock_client),
+        pytest.raises(ValueError, match=match),
+    ):
+        async for _ in llm.astream("test"):
+            pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_openai-1.6.3/uv.lock 
new/langchain_openai-1.6.6/uv.lock
--- old/langchain_openai-1.6.3/uv.lock  2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_openai-1.6.6/uv.lock  2020-02-02 01:00:00.000000000 +0100
@@ -766,7 +766,7 @@
 
 [[package]]
 name = "langchain-openai"
-version = "1.6.3"
+version = "1.6.6"
 source = { editable = "." }
 dependencies = [
     { name = "certifi" },

Reply via email to