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-07-12 19:02:58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-langchain-openai (Old)
 and      /work/SRC/openSUSE:Factory/.python-langchain-openai.new.1991 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-langchain-openai"

Sun Jul 12 19:02:58 2026 rev:3 rq:1364983 version:1.3.5

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-langchain-openai/python-langchain-openai.changes
  2026-07-09 22:21:24.006254837 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-langchain-openai.new.1991/python-langchain-openai.changes
        2026-07-12 19:03:00.022692493 +0200
@@ -1,0 +2,15 @@
+Sat Jul 11 05:37:48 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to 1.3.5:
+  * Add prompt cache breakpoint support: content blocks can carry
+    a prompt_cache_breakpoint and ChatOpenAI accepts a new
+    prompt_cache_options parameter for explicit/implicit caching
+  * Expose cache-write token counts as cache_creation in the
+    input token usage metadata (chat completions and responses)
+  * Add GPT-5.6 model profiles (gpt-5.6, -luna, -sol, -terra)
+  * Drop the legacy prompt_cache_retention in-memory value
+    normalization now that the newer openai SDK is required
+- Raise langchain-core requirement to >= 1.4.9
+- Raise openai requirement to >= 2.45.0
+
+-------------------------------------------------------------------

Old:
----
  langchain_openai-1.3.4.tar.gz

New:
----
  langchain_openai-1.3.5.tar.gz

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

Other differences:
------------------
++++++ python-langchain-openai.spec ++++++
--- /var/tmp/diff_new_pack.r38UQ3/_old  2026-07-12 19:03:00.762717236 +0200
+++ /var/tmp/diff_new_pack.r38UQ3/_new  2026-07-12 19:03:00.762717236 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           python-langchain-openai
-Version:        1.3.4
+Version:        1.3.5
 Release:        0
 Summary:        An integration package connecting OpenAI and LangChain
 License:        MIT
@@ -27,14 +27,14 @@
 BuildRequires:  %{python_module pip}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
-Requires:       python-langchain-core >= 1.4.8
-Requires:       python-openai >= 2.26.0
+Requires:       python-langchain-core >= 1.4.9
+Requires:       python-openai >= 2.45.0
 Requires:       python-tiktoken >= 0.7.0
 BuildArch:      noarch
 # SECTION test requirements
 BuildRequires:  %{python_module httpx}
-BuildRequires:  %{python_module langchain-core >= 1.4.8}
-BuildRequires:  %{python_module openai >= 2.26.0}
+BuildRequires:  %{python_module langchain-core >= 1.4.9}
+BuildRequires:  %{python_module openai >= 2.45.0}
 BuildRequires:  %{python_module pytest-asyncio}
 BuildRequires:  %{python_module tiktoken >= 0.7.0}
 # /SECTION

++++++ langchain_openai-1.3.4.tar.gz -> langchain_openai-1.3.5.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_openai-1.3.4/PKG-INFO 
new/langchain_openai-1.3.5/PKG-INFO
--- old/langchain_openai-1.3.4/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_openai-1.3.5/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: langchain-openai
-Version: 1.3.4
+Version: 1.3.5
 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/
@@ -24,7 +24,7 @@
 Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
 Requires-Python: <4.0.0,>=3.10.0
 Requires-Dist: langchain-core<2.0.0,>=1.4.9
-Requires-Dist: openai<3.0.0,>=2.26.0
+Requires-Dist: openai<3.0.0,>=2.45.0
 Requires-Dist: tiktoken<1.0.0,>=0.7.0
 Description-Content-Type: text/markdown
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_openai-1.3.4/langchain_openai/_version.py 
new/langchain_openai-1.3.5/langchain_openai/_version.py
--- old/langchain_openai-1.3.4/langchain_openai/_version.py     2020-02-02 
01:00:00.000000000 +0100
+++ new/langchain_openai-1.3.5/langchain_openai/_version.py     2020-02-02 
01:00:00.000000000 +0100
@@ -1,3 +1,3 @@
 """Version information for `langchain-openai`."""
 
-__version__ = "1.3.4"
+__version__ = "1.3.5"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_openai-1.3.4/langchain_openai/chat_models/base.py 
new/langchain_openai-1.3.5/langchain_openai/chat_models/base.py
--- old/langchain_openai-1.3.4/langchain_openai/chat_models/base.py     
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_openai-1.3.5/langchain_openai/chat_models/base.py     
2020-02-02 01:00:00.000000000 +0100
@@ -264,10 +264,30 @@
     return ChatMessage(content=_dict.get("content", ""), role=role, id=id_)  # 
type: ignore[arg-type]
 
 
+def _apply_prompt_cache_breakpoint(
+    source_block: dict[str, Any], formatted_block: dict[str, Any]
+) -> dict[str, Any]:
+    """Apply an OpenAI prompt cache breakpoint to a formatted content block.
+
+    A breakpoint set directly on the block takes precedence over one nested in
+    `extras`. Membership (not truthiness) decides whether to copy it, so a
+    present-but-falsy value (e.g. `None`) is still preserved.
+    """
+    if "prompt_cache_breakpoint" in source_block:
+        formatted_block["prompt_cache_breakpoint"] = source_block[
+            "prompt_cache_breakpoint"
+        ]
+    elif isinstance(extras := source_block.get("extras"), dict) and (
+        "prompt_cache_breakpoint" in extras
+    ):
+        formatted_block["prompt_cache_breakpoint"] = 
extras["prompt_cache_breakpoint"]
+    return formatted_block
+
+
 def _sanitize_chat_completions_content(content: str | list[dict]) -> str | 
list[dict]:
     """Sanitize content for chat/completions API.
 
-    For list content, filters text blocks to only keep 'type' and 'text' keys.
+    For list content, filters text blocks to only keep supported keys.
     """
     if isinstance(content, list):
         sanitized = []
@@ -277,7 +297,12 @@
                 and block.get("type") == "text"
                 and "text" in block
             ):
-                sanitized.append({"type": "text", "text": block["text"]})
+                sanitized_block = {"type": "text", "text": block["text"]}
+                if "prompt_cache_breakpoint" in block:
+                    sanitized_block["prompt_cache_breakpoint"] = block[
+                        "prompt_cache_breakpoint"
+                    ]
+                sanitized.append(sanitized_block)
             else:
                 sanitized.append(block)
         return sanitized
@@ -313,7 +338,21 @@
                 # image generation calls)
                 and not (api == "responses" and 
str(role).lower().startswith("ai"))
             ):
-                formatted_content.append(convert_to_openai_data_block(block, 
api=api))
+                formatted_block = convert_to_openai_data_block(block, api=api)
+                formatted_content.append(
+                    _apply_prompt_cache_breakpoint(block, formatted_block)
+                )
+            elif (
+                isinstance(block, dict)
+                and block.get("type") == "text"
+                and "text" in block
+                and isinstance(extras := block.get("extras"), dict)
+                and "prompt_cache_breakpoint" in extras
+            ):
+                formatted_block = {"type": "text", "text": block["text"]}
+                formatted_content.append(
+                    _apply_prompt_cache_breakpoint(block, formatted_block)
+                )
             # Anthropic image blocks
             elif (
                 isinstance(block, dict)
@@ -984,6 +1023,12 @@
     !!! version-added "Added in `langchain-openai` 0.3.24"
     """
 
+    prompt_cache_options: dict[str, Any] | None = None
+    """Options controlling OpenAI prompt cache behavior.
+
+    !!! version-added "Added in `langchain-openai` 1.3.5"
+    """
+
     service_tier: str | None = None
     """Latency tier for request.
 
@@ -1309,6 +1354,7 @@
             "verbosity": self.verbosity,
             "context_management": self.context_management,
             "include": self.include,
+            "prompt_cache_options": self.prompt_cache_options,
             "service_tier": self.service_tier,
             "truncation": self.truncation,
             "store": self.store,
@@ -3297,8 +3343,9 @@
 
     ??? info "Prompt caching optimization"
 
-        For high-volume applications with repetitive prompts, use 
`prompt_cache_key`
-        per-invocation to improve cache hit rates and reduce costs:
+        OpenAI prompt caching is automatic for eligible prompts. For 
high-volume
+        applications with repetitive prompts, use `prompt_cache_key`
+        per invocation to improve cache hit rates and reduce costs:
 
         ```python
         model = ChatOpenAI(model="...")
@@ -3316,9 +3363,69 @@
         response = model.invoke(messages, prompt_cache_key=cache_key)
         ```
 
-        Cache keys help ensure requests with the same prompt prefix are routed 
to
-        machines with existing cache, providing cost reduction and latency 
improvement on
-        cached tokens.
+        The default `"implicit"` mode keeps OpenAI's automatic breakpoint and
+        also uses explicit breakpoints. The `"explicit"` mode uses only the
+        breakpoints you provide.
+
+        For models that support explicit cache breakpoints, pass
+        request-level cache options and mark supported content blocks
+        with `prompt_cache_breakpoint`:
+
+        ```python
+        model = ChatOpenAI(model="gpt-5.6-sol")
+        response = model.invoke(
+            [
+                {
+                    "role": "system",
+                    "content": [
+                        {
+                            "type": "text",
+                            "text": "Stable instructions and examples...",
+                            "prompt_cache_breakpoint": {"mode": "explicit"},
+                        }
+                    ],
+                },
+                {"role": "user", "content": "Current request"},
+            ],
+            prompt_cache_key="tenant:acme:support-v1",
+            prompt_cache_options={"mode": "explicit", "ttl": "30m"},
+        )
+        ```
+
+        Set `prompt_cache_options` per invocation, as above, or persist it on
+        the model:
+
+        ```python
+        model = ChatOpenAI(
+            model="gpt-5.6-sol",
+            prompt_cache_options={"mode": "explicit", "ttl": "30m"},
+        )
+        ```
+
+        `prompt_cache_options["mode"]` can be `"implicit"` or `"explicit"`.
+        OpenAI limits how many breakpoints can write to the cache in a single
+        request. In `"implicit"` mode, the implicit breakpoint on the latest
+        message uses one write slot, so up to three explicit breakpoints can
+        write. In `"explicit"` mode, up to four explicit breakpoints can write.
+        For reads, OpenAI considers up to the latest 50 breakpoints.
+
+        For models before the GPT-5.6 family that support legacy prompt cache
+        retention, pass `prompt_cache_retention`. See OpenAI's
+        [prompt caching 
docs](https://platform.openai.com/docs/guides/prompt-caching)
+        for the current model support list and retention semantics.
+
+        ```python
+        response = model.invoke(messages, prompt_cache_retention="24h")
+        ```
+
+        Cache keys help ensure requests with the same prompt prefix are routed
+        to machines with existing cache, providing cost reduction and
+        latency improvement on cached tokens. Cache reads are available as
+        `response.usage_metadata["input_token_details"]["cache_read"]`; cache
+        writes are available as `"cache_creation"` when the OpenAI response
+        includes `cache_write_tokens`. On the `"priority"` and `"flex"`
+        service tiers these keys are prefixed with the tier name
+        (e.g. `"priority_cache_read"`).
     """  # noqa: E501
 
     max_tokens: int | None = Field(default=None, alias="max_completion_tokens")
@@ -4037,13 +4144,13 @@
     if service_tier not in {"priority", "flex"}:
         service_tier = None
     service_tier_prefix = f"{service_tier}_" if service_tier else ""
+    prompt_tokens_details = oai_token_usage.get("prompt_tokens_details") or {}
     input_token_details: dict = {
-        "audio": (oai_token_usage.get("prompt_tokens_details") or {}).get(
-            "audio_tokens"
+        "audio": prompt_tokens_details.get("audio_tokens"),
+        f"{service_tier_prefix}cache_read": 
prompt_tokens_details.get("cached_tokens"),
+        f"{service_tier_prefix}cache_creation": prompt_tokens_details.get(
+            "cache_write_tokens"
         ),
-        f"{service_tier_prefix}cache_read": (
-            oai_token_usage.get("prompt_tokens_details") or {}
-        ).get("cached_tokens"),
     }
     output_token_details: dict = {
         "audio": (oai_token_usage.get("completion_tokens_details") or {}).get(
@@ -4054,13 +4161,13 @@
         ).get("reasoning_tokens"),
     }
     if service_tier is not None:
-        # Avoid counting cache and reasoning tokens towards the service tier 
token
-        # counts, since service tier tokens are already priced differently
-        input_token_details[service_tier] = input_tokens - 
input_token_details.get(
-            f"{service_tier_prefix}cache_read", 0
+        # Avoid counting cache-read and reasoning tokens towards the service 
tier
+        # token counts, since service tier tokens are already priced 
differently
+        input_token_details[service_tier] = input_tokens - (
+            input_token_details.get(f"{service_tier_prefix}cache_read", 0) or 0
         )
-        output_token_details[service_tier] = output_tokens - 
output_token_details.get(
-            f"{service_tier_prefix}reasoning", 0
+        output_token_details[service_tier] = output_tokens - (
+            output_token_details.get(f"{service_tier_prefix}reasoning", 0) or 0
         )
     return UsageMetadata(
         input_tokens=input_tokens,
@@ -4078,9 +4185,12 @@
 def _create_usage_metadata_responses(
     oai_token_usage: dict, service_tier: str | None = None
 ) -> UsageMetadata:
-    input_tokens = oai_token_usage.get("input_tokens", 0)
-    output_tokens = oai_token_usage.get("output_tokens", 0)
-    total_tokens = oai_token_usage.get("total_tokens", input_tokens + 
output_tokens)
+    _input = oai_token_usage.get("input_tokens")
+    input_tokens = _input if _input is not None else 0
+    _output = oai_token_usage.get("output_tokens")
+    output_tokens = _output if _output is not None else 0
+    _total = oai_token_usage.get("total_tokens")
+    total_tokens = _total if _total is not None else input_tokens + 
output_tokens
     if service_tier not in {"priority", "flex"}:
         service_tier = None
     service_tier_prefix = f"{service_tier}_" if service_tier else ""
@@ -4089,19 +4199,21 @@
             oai_token_usage.get("output_tokens_details") or {}
         ).get("reasoning_tokens")
     }
+    input_tokens_details = oai_token_usage.get("input_tokens_details") or {}
     input_token_details: dict = {
-        f"{service_tier_prefix}cache_read": (
-            oai_token_usage.get("input_tokens_details") or {}
-        ).get("cached_tokens")
+        f"{service_tier_prefix}cache_read": 
input_tokens_details.get("cached_tokens"),
+        f"{service_tier_prefix}cache_creation": input_tokens_details.get(
+            "cache_write_tokens"
+        ),
     }
     if service_tier is not None:
-        # Avoid counting cache and reasoning tokens towards the service tier 
token
-        # counts, since service tier tokens are already priced differently
-        output_token_details[service_tier] = output_tokens - 
output_token_details.get(
-            f"{service_tier_prefix}reasoning", 0
+        # Avoid counting cache-read and reasoning tokens towards the service 
tier
+        # token counts, since service tier tokens are already priced 
differently
+        output_token_details[service_tier] = output_tokens - (
+            output_token_details.get(f"{service_tier_prefix}reasoning", 0) or 0
         )
-        input_token_details[service_tier] = input_tokens - 
input_token_details.get(
-            f"{service_tier_prefix}cache_read", 0
+        input_token_details[service_tier] = input_tokens - (
+            input_token_details.get(f"{service_tier_prefix}cache_read", 0) or 0
         )
     return UsageMetadata(
         input_tokens=input_tokens,
@@ -4301,19 +4413,27 @@
     if block["type"] == "text":
         # chat api: {"type": "text", "text": "..."}
         # responses api: {"type": "input_text", "text": "..."}
-        return {"type": "input_text", "text": block["text"]}
+        new_block = {"type": "input_text", "text": block["text"]}
+        if "prompt_cache_breakpoint" in block:
+            new_block["prompt_cache_breakpoint"] = 
block["prompt_cache_breakpoint"]
+        return new_block
     if block["type"] == "image_url":
         # chat api: {"type": "image_url", "image_url": {"url": "...", 
"detail": "..."}}  # noqa: E501
-        # responses api: {"type": "image_url", "image_url": "...", "detail": 
"...", "file_id": "..."}  # noqa: E501
+        # responses api: {"type": "input_image", "image_url": "...", "detail": 
"..."}  # noqa: E501
         new_block = {
             "type": "input_image",
             "image_url": block["image_url"]["url"],
         }
         if block["image_url"].get("detail"):
             new_block["detail"] = block["image_url"]["detail"]
+        if "prompt_cache_breakpoint" in block:
+            new_block["prompt_cache_breakpoint"] = 
block["prompt_cache_breakpoint"]
         return new_block
     if block["type"] == "file":
-        return {"type": "input_file", **block["file"]}
+        new_block = {"type": "input_file", **block["file"]}
+        if "prompt_cache_breakpoint" in block:
+            new_block["prompt_cache_breakpoint"] = 
block["prompt_cache_breakpoint"]
+        return new_block
     return block
 
 
@@ -4842,13 +4962,6 @@
     if isinstance(resp, dict):
         from openai.types.responses import Response
 
-        # Known mismatch: API emits `prompt_cache_retention="in_memory"` while
-        # older `openai` packages declare only `"in-memory"` in the Literal
-        # (openai-python#2883). Pre-normalize so validation succeeds on
-        # currently-released SDK versions.
-        if resp.get("prompt_cache_retention") == "in_memory":
-            resp = {**resp, "prompt_cache_retention": "in-memory"}
-
         try:
             return Response.model_validate(resp)
         except ValidationError as e:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_openai-1.3.4/langchain_openai/data/_profiles.py 
new/langchain_openai-1.3.5/langchain_openai/data/_profiles.py
--- old/langchain_openai-1.3.4/langchain_openai/data/_profiles.py       
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_openai-1.3.5/langchain_openai/data/_profiles.py       
2020-02-02 01:00:00.000000000 +0100
@@ -987,6 +987,114 @@
         "tool_choice": True,
         "tool_call_streaming": True,
     },
+    "gpt-5.6": {
+        "name": "GPT-5.6",
+        "release_date": "2026-07-09",
+        "last_updated": "2026-07-09",
+        "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,
+    },
+    "gpt-5.6-luna": {
+        "name": "GPT-5.6 Luna",
+        "release_date": "2026-07-09",
+        "last_updated": "2026-07-09",
+        "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,
+    },
+    "gpt-5.6-sol": {
+        "name": "GPT-5.6 Sol",
+        "release_date": "2026-07-09",
+        "last_updated": "2026-07-09",
+        "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,
+    },
+    "gpt-5.6-terra": {
+        "name": "GPT-5.6 Terra",
+        "release_date": "2026-07-09",
+        "last_updated": "2026-07-09",
+        "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,
+    },
     "gpt-image-1": {
         "name": "gpt-image-1",
         "release_date": "2025-04-24",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_openai-1.3.4/pyproject.toml 
new/langchain_openai-1.3.5/pyproject.toml
--- old/langchain_openai-1.3.4/pyproject.toml   2020-02-02 01:00:00.000000000 
+0100
+++ new/langchain_openai-1.3.5/pyproject.toml   2020-02-02 01:00:00.000000000 
+0100
@@ -20,11 +20,11 @@
     "Topic :: Scientific/Engineering :: Artificial Intelligence",
 ]
 
-version = "1.3.4"
+version = "1.3.5"
 requires-python = ">=3.10.0,<4.0.0"
 dependencies = [
     "langchain-core>=1.4.9,<2.0.0",
-    "openai>=2.26.0,<3.0.0",
+    "openai>=2.45.0,<3.0.0",
     "tiktoken>=0.7.0,<1.0.0",
 ]
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_openai-1.3.4/tests/integration_tests/chat_models/test_base.py 
new/langchain_openai-1.3.5/tests/integration_tests/chat_models/test_base.py
--- old/langchain_openai-1.3.4/tests/integration_tests/chat_models/test_base.py 
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_openai-1.3.5/tests/integration_tests/chat_models/test_base.py 
2020-02-02 01:00:00.000000000 +0100
@@ -1200,6 +1200,45 @@
     assert isinstance(response_model_level.content, str)
 
 
[email protected]
[email protected](retries=3, delay=1)
+def test_explicit_prompt_cache_breakpoint_invoke() -> None:
+    """Explicit cache breakpoints produce a cache read on a repeated prefix.
+
+    Uses a long, stable prefix marked with a `prompt_cache_breakpoint` and
+    `prompt_cache_options={"mode": "explicit"}`. The first invocation writes 
the
+    cache and the second should read it back.
+    """
+    chat = ChatOpenAI(
+        model="gpt-5.6-sol",
+        max_completion_tokens=10,
+        prompt_cache_options={"mode": "explicit"},
+    )
+    # A prefix long enough to exceed OpenAI's minimum cacheable prompt length.
+    stable_prefix = "Stable, cacheable instructions and reference material. " 
* 400
+    messages = [
+        HumanMessage(
+            content=[
+                {
+                    "type": "text",
+                    "text": stable_prefix,
+                    "prompt_cache_breakpoint": {"mode": "explicit"},
+                },
+                {"type": "text", "text": "Say hello."},
+            ]
+        )
+    ]
+
+    first = chat.invoke(messages)
+    assert isinstance(first, AIMessage)
+
+    second = chat.invoke(messages)
+    assert isinstance(second, AIMessage)
+    assert second.usage_metadata is not None
+    cache_read = 
second.usage_metadata["input_token_details"].get("cache_read", 0)
+    assert cache_read > 0
+
+
 class BadModel(BaseModel):
     response: str
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_openai-1.3.4/tests/unit_tests/chat_models/test_base.py 
new/langchain_openai-1.3.5/tests/unit_tests/chat_models/test_base.py
--- old/langchain_openai-1.3.4/tests/unit_tests/chat_models/test_base.py        
2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_openai-1.3.5/tests/unit_tests/chat_models/test_base.py        
2020-02-02 01:00:00.000000000 +0100
@@ -1157,6 +1157,90 @@
     assert result["total_tokens"] == 0
 
 
+def test__create_usage_metadata_cache_write_tokens() -> None:
+    """`cache_write_tokens` is surfaced under the standard `cache_creation` 
key."""
+    usage_metadata = {
+        "completion_tokens": 15,
+        # OpenAI's `cache_write_tokens` maps to core's `cache_creation`
+        "prompt_tokens_details": {"cached_tokens": 50, "cache_write_tokens": 
25},
+        "completion_tokens_details": None,
+        "prompt_tokens": 100,
+        "total_tokens": 115,
+    }
+    result = _create_usage_metadata(usage_metadata)
+    assert result["input_token_details"] == {
+        "cache_read": 50,
+        "cache_creation": 25,
+    }
+
+
+def test__create_usage_metadata_cache_read_only() -> None:
+    """Responses without `cache_write_tokens` emit no `cache_creation` key."""
+    usage_metadata = {
+        "completion_tokens": 15,
+        "prompt_tokens_details": {"cached_tokens": 50},
+        "completion_tokens_details": None,
+        "prompt_tokens": 100,
+        "total_tokens": 115,
+    }
+    result = _create_usage_metadata(usage_metadata)
+    assert result["input_token_details"] == {"cache_read": 50}
+
+
+def test__create_usage_metadata_cache_tokens_zero_retained() -> None:
+    """Explicit zero cache counts are retained (filtered on `None`, not 
falsiness)."""
+    usage_metadata = {
+        "completion_tokens": 15,
+        "prompt_tokens_details": {"cached_tokens": 0, "cache_write_tokens": 0},
+        "completion_tokens_details": None,
+        "prompt_tokens": 100,
+        "total_tokens": 115,
+    }
+    result = _create_usage_metadata(usage_metadata)
+    assert result["input_token_details"] == {
+        "cache_read": 0,
+        "cache_creation": 0,
+    }
+
+
+def test__create_usage_metadata_service_tier_excludes_cache_read_tokens() -> 
None:
+    """Tier counts exclude cache reads but not overlapping cache writes."""
+    usage_metadata = {
+        "completion_tokens": 50,
+        "prompt_tokens_details": {
+            "cached_tokens": 256,
+            "cache_write_tokens": 3072,
+        },
+        "completion_tokens_details": {"reasoning_tokens": 10},
+        "prompt_tokens": 2304,
+        "total_tokens": 2354,
+    }
+    result = _create_usage_metadata(usage_metadata, service_tier="priority")
+    assert result["input_token_details"] == {
+        "priority_cache_read": 256,
+        "priority_cache_creation": 3072,
+        "priority": 2048,
+    }
+    assert result["output_token_details"] == {
+        "priority_reasoning": 10,
+        "priority": 40,  # 50 - 10 (reasoning)
+    }
+
+
+def test__create_usage_metadata_service_tier_without_detail_fields() -> None:
+    """Tier arithmetic tolerates missing cache/reasoning fields (no 
TypeError)."""
+    usage_metadata = {
+        "completion_tokens": 50,
+        "prompt_tokens_details": None,
+        "completion_tokens_details": None,
+        "prompt_tokens": 100,
+        "total_tokens": 150,
+    }
+    result = _create_usage_metadata(usage_metadata, service_tier="flex")
+    assert result["input_token_details"] == {"flex": 100}
+    assert result["output_token_details"] == {"flex": 50}
+
+
 def test__create_usage_metadata_responses() -> None:
     response_usage_metadata = {
         "input_tokens": 100,
@@ -1176,6 +1260,68 @@
     )
 
 
+def test__create_usage_metadata_responses_cache_write_tokens() -> None:
+    """Responses usage maps `cache_write_tokens` to the `cache_creation` 
key."""
+    response_usage_metadata = {
+        "input_tokens": 100,
+        "input_tokens_details": {"cached_tokens": 50, "cache_write_tokens": 
25},
+        "output_tokens": 50,
+        "output_tokens_details": {"reasoning_tokens": 10},
+        "total_tokens": 150,
+    }
+    result = _create_usage_metadata_responses(response_usage_metadata)
+
+    assert result == UsageMetadata(
+        output_tokens=50,
+        input_tokens=100,
+        total_tokens=150,
+        input_token_details={"cache_read": 50, "cache_creation": 25},
+        output_token_details={"reasoning": 10},
+    )
+
+
+def test__create_usage_metadata_responses_service_tier_cache_write_overlap() 
-> None:
+    """Tier counts exclude cache reads but not overlapping cache writes."""
+    response_usage_metadata = {
+        "input_tokens": 2304,
+        "input_tokens_details": {
+            "cached_tokens": 256,
+            "cache_write_tokens": 3072,
+        },
+        "output_tokens": 50,
+        "output_tokens_details": {"reasoning_tokens": 10},
+        "total_tokens": 2354,
+    }
+    result = _create_usage_metadata_responses(
+        response_usage_metadata, service_tier="flex"
+    )
+    assert result["input_token_details"] == {
+        "flex_cache_read": 256,
+        "flex_cache_creation": 3072,
+        "flex": 2048,
+    }
+    assert result["output_token_details"] == {
+        "flex_reasoning": 10,
+        "flex": 40,  # 50 - 10 (reasoning)
+    }
+
+
+def test__create_usage_metadata_responses_service_tier_without_detail_fields() 
-> None:
+    """Tier arithmetic tolerates missing cache/reasoning fields (no 
TypeError)."""
+    response_usage_metadata = {
+        "input_tokens": 100,
+        "input_tokens_details": None,
+        "output_tokens": 50,
+        "output_tokens_details": None,
+        "total_tokens": 150,
+    }
+    result = _create_usage_metadata_responses(
+        response_usage_metadata, service_tier="priority"
+    )
+    assert result["input_token_details"] == {"priority": 100}
+    assert result["output_token_details"] == {"priority": 50}
+
+
 def test__resize_caps_dimensions_preserving_ratio() -> None:
     """Larger side capped at 2048 then smaller at 768 keeping aspect ratio."""
     assert _resize(2048, 4096) == (768, 1536)
@@ -1889,7 +2035,9 @@
             input_tokens=10,
             output_tokens=3,
             total_tokens=13,
-            input_tokens_details=InputTokensDetails(cached_tokens=0),
+            input_tokens_details=InputTokensDetails(
+                cache_write_tokens=0, cached_tokens=0
+            ),
             output_tokens_details=OutputTokensDetails(reasoning_tokens=0),
         ),
     )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_openai-1.3.4/tests/unit_tests/chat_models/test_prompt_cache_key.py
 
new/langchain_openai-1.3.5/tests/unit_tests/chat_models/test_prompt_cache_key.py
--- 
old/langchain_openai-1.3.4/tests/unit_tests/chat_models/test_prompt_cache_key.py
    2020-02-02 01:00:00.000000000 +0100
+++ 
new/langchain_openai-1.3.5/tests/unit_tests/chat_models/test_prompt_cache_key.py
    2020-02-02 01:00:00.000000000 +0100
@@ -1,9 +1,12 @@
 """Unit tests for prompt_cache_key parameter."""
 
-from langchain_core.messages import HumanMessage
+from langchain_core.messages import HumanMessage, ToolMessage
+from langchain_core.messages.content import create_text_block
 
 from langchain_openai import ChatOpenAI
 
+MODEL_NAME = "gpt-5.5"
+
 
 def test_prompt_cache_key_parameter_inclusion() -> None:
     """Test that prompt_cache_key parameter is properly included in request 
payload."""
@@ -85,3 +88,332 @@
     # prompt_cache_key should be present regardless of API type
     assert "prompt_cache_key" in payload
     assert payload["prompt_cache_key"] == "responses-api-cache-v1"
+
+
+def test_prompt_cache_options_and_retention_request_payload() -> None:
+    """Per-invocation cache options/retention flow into the request payload."""
+    chat = ChatOpenAI(model=MODEL_NAME, max_completion_tokens=10)
+    messages = [HumanMessage("Hello")]
+
+    payload = chat._get_request_payload(
+        messages,
+        prompt_cache_options={"mode": "explicit", "ttl": "30m"},
+        prompt_cache_retention="24h",
+    )
+
+    assert payload["prompt_cache_options"] == {"mode": "explicit", "ttl": 
"30m"}
+    assert payload["prompt_cache_retention"] == "24h"
+
+
+def test_prompt_cache_options_and_retention_responses_api_payload() -> None:
+    """Cache options/retention survive Responses API payload construction."""
+    chat = ChatOpenAI(
+        model=MODEL_NAME,
+        use_responses_api=True,
+        output_version="responses/v1",
+        max_completion_tokens=10,
+    )
+    messages = [HumanMessage("Hello")]
+
+    payload = chat._get_request_payload(
+        messages,
+        prompt_cache_options={"mode": "explicit", "ttl": "30m"},
+        prompt_cache_retention="24h",
+    )
+
+    assert payload["prompt_cache_options"] == {"mode": "explicit", "ttl": 
"30m"}
+    assert payload["prompt_cache_retention"] == "24h"
+
+
+def test_prompt_cache_options_init_param() -> None:
+    """Model-level cache options flow into the payload and can be 
overridden."""
+    chat = ChatOpenAI(
+        model=MODEL_NAME,
+        max_completion_tokens=10,
+        prompt_cache_options={"mode": "explicit", "ttl": "30m"},
+        model_kwargs={"prompt_cache_retention": "24h"},
+    )
+    messages = [HumanMessage("Hello")]
+
+    payload = chat._get_request_payload(messages)
+    override_payload = chat._get_request_payload(
+        messages, prompt_cache_options={"mode": "implicit"}
+    )
+
+    assert payload["prompt_cache_options"] == {"mode": "explicit", "ttl": 
"30m"}
+    assert payload["prompt_cache_retention"] == "24h"
+    assert override_payload["prompt_cache_options"] == {"mode": "implicit"}
+
+
+def test_prompt_cache_breakpoint_chat_completions_text_block() -> None:
+    """A `prompt_cache_breakpoint` on a text block is preserved for Chat 
Completions."""
+    chat = ChatOpenAI(model=MODEL_NAME, max_completion_tokens=10)
+    messages = [
+        HumanMessage(
+            content=[
+                {
+                    "type": "text",
+                    "text": "Stable prefix",
+                    "prompt_cache_breakpoint": {"mode": "explicit"},
+                }
+            ]
+        )
+    ]
+
+    payload = chat._get_request_payload(messages)
+
+    assert payload["messages"][0]["content"][0]["prompt_cache_breakpoint"] == {
+        "mode": "explicit"
+    }
+
+
+def test_prompt_cache_breakpoint_chat_completions_tool_message() -> None:
+    """A cache breakpoint on a tool result text block is preserved."""
+    chat = ChatOpenAI(model=MODEL_NAME, max_completion_tokens=10)
+    messages = [
+        ToolMessage(
+            tool_call_id="call_123",
+            content_blocks=[
+                {
+                    "type": "text",
+                    "text": "Stable tool result",
+                    "extras": {"prompt_cache_breakpoint": {"mode": 
"explicit"}},
+                }
+            ],
+        )
+    ]
+
+    payload = chat._get_request_payload(messages)
+
+    assert payload["messages"][0]["content"] == [
+        {
+            "type": "text",
+            "text": "Stable tool result",
+            "prompt_cache_breakpoint": {"mode": "explicit"},
+        }
+    ]
+
+
+def test_prompt_cache_breakpoint_responses_api_converted_blocks() -> None:
+    """`prompt_cache_breakpoint` survives Responses API conversion for each 
block."""
+    chat = ChatOpenAI(
+        model=MODEL_NAME,
+        use_responses_api=True,
+        output_version="responses/v1",
+        max_completion_tokens=10,
+    )
+    messages = [
+        HumanMessage(
+            content=[
+                {
+                    "type": "text",
+                    "text": "Stable text prefix",
+                    "prompt_cache_breakpoint": {"mode": "explicit"},
+                },
+                {
+                    "type": "image_url",
+                    "image_url": {"url": "https://example.com/image.png"},
+                    "prompt_cache_breakpoint": {"mode": "explicit"},
+                },
+                {
+                    "type": "file",
+                    "file": {"file_id": "file_123"},
+                    "prompt_cache_breakpoint": {"mode": "explicit"},
+                },
+            ]
+        )
+    ]
+
+    payload = chat._get_request_payload(messages)
+    content = payload["input"][0]["content"]
+
+    assert content == [
+        {
+            "type": "input_text",
+            "text": "Stable text prefix",
+            "prompt_cache_breakpoint": {"mode": "explicit"},
+        },
+        {
+            "type": "input_image",
+            "image_url": "https://example.com/image.png";,
+            "prompt_cache_breakpoint": {"mode": "explicit"},
+        },
+        {
+            "type": "input_file",
+            "file_id": "file_123",
+            "prompt_cache_breakpoint": {"mode": "explicit"},
+        },
+    ]
+
+
+def test_prompt_cache_breakpoint_chat_completions_content_blocks() -> None:
+    """Standard content block cache breakpoints reach Chat Completions."""
+    chat = ChatOpenAI(model=MODEL_NAME, output_version="v1", 
max_completion_tokens=10)
+    message = HumanMessage(
+        content_blocks=[
+            create_text_block(
+                "Stable text prefix",
+                prompt_cache_breakpoint={"mode": "explicit"},
+            ),
+            {
+                "type": "image",
+                "url": "https://example.com/image.png";,
+                "extras": {"prompt_cache_breakpoint": {"mode": "explicit"}},
+            },
+            {
+                "type": "file",
+                "file_id": "file_123",
+                "extras": {"prompt_cache_breakpoint": {"mode": "explicit"}},
+            },
+        ]
+    )
+
+    payload = chat._get_request_payload([message])
+
+    assert payload["messages"][0]["content"] == [
+        {
+            "type": "text",
+            "text": "Stable text prefix",
+            "prompt_cache_breakpoint": {"mode": "explicit"},
+        },
+        {
+            "type": "image_url",
+            "image_url": {"url": "https://example.com/image.png"},
+            "prompt_cache_breakpoint": {"mode": "explicit"},
+        },
+        {
+            "type": "file",
+            "file": {"file_id": "file_123"},
+            "prompt_cache_breakpoint": {"mode": "explicit"},
+        },
+    ]
+
+
+def test_prompt_cache_breakpoint_responses_api_content_blocks() -> None:
+    """Standard content block cache breakpoints reach the Responses API."""
+    chat = ChatOpenAI(
+        model=MODEL_NAME,
+        use_responses_api=True,
+        output_version="v1",
+        max_completion_tokens=10,
+    )
+    message = HumanMessage(
+        content_blocks=[
+            {
+                "type": "text",
+                "text": "Stable text prefix",
+                "extras": {"prompt_cache_breakpoint": {"mode": "explicit"}},
+            },
+            {
+                "type": "image",
+                "url": "https://example.com/image.png";,
+                "extras": {"prompt_cache_breakpoint": {"mode": "explicit"}},
+            },
+            {
+                "type": "file",
+                "file_id": "file_123",
+                "extras": {"prompt_cache_breakpoint": {"mode": "explicit"}},
+            },
+        ]
+    )
+
+    payload = chat._get_request_payload([message])
+
+    assert payload["input"][0]["content"] == [
+        {
+            "type": "input_text",
+            "text": "Stable text prefix",
+            "prompt_cache_breakpoint": {"mode": "explicit"},
+        },
+        {
+            "type": "input_image",
+            "image_url": "https://example.com/image.png";,
+            "prompt_cache_breakpoint": {"mode": "explicit"},
+        },
+        {
+            "type": "input_file",
+            "file_id": "file_123",
+            "prompt_cache_breakpoint": {"mode": "explicit"},
+        },
+    ]
+
+
+def test_prompt_cache_breakpoint_top_level_on_data_blocks() -> None:
+    """A top-level `prompt_cache_breakpoint` on a data block is preserved."""
+    chat = ChatOpenAI(model=MODEL_NAME, max_completion_tokens=10)
+    message = HumanMessage(
+        content=[
+            {
+                "type": "image",
+                "url": "https://example.com/image.png";,
+                "prompt_cache_breakpoint": {"mode": "explicit"},
+            },
+            {
+                "type": "file",
+                "file_id": "file_123",
+                "prompt_cache_breakpoint": {"mode": "explicit"},
+            },
+        ]
+    )
+
+    payload = chat._get_request_payload([message])
+
+    assert payload["messages"][0]["content"] == [
+        {
+            "type": "image_url",
+            "image_url": {"url": "https://example.com/image.png"},
+            "prompt_cache_breakpoint": {"mode": "explicit"},
+        },
+        {
+            "type": "file",
+            "file": {"file_id": "file_123"},
+            "prompt_cache_breakpoint": {"mode": "explicit"},
+        },
+    ]
+
+
+def test_prompt_cache_breakpoint_preserves_falsy_value() -> None:
+    """A present-but-falsy breakpoint value is copied (membership, not 
truthiness)."""
+    chat = ChatOpenAI(model=MODEL_NAME, max_completion_tokens=10)
+    message = HumanMessage(
+        content=[
+            {
+                "type": "text",
+                "text": "Stable prefix",
+                "extras": {"prompt_cache_breakpoint": None},
+            }
+        ]
+    )
+
+    payload = chat._get_request_payload([message])
+
+    block = payload["messages"][0]["content"][0]
+    assert "prompt_cache_breakpoint" in block
+    assert block["prompt_cache_breakpoint"] is None
+
+
+def test_prompt_cache_breakpoint_text_block_drops_other_extras() -> None:
+    """Promoting a breakpoint from `extras` drops the block's other 
`extras`."""
+    chat = ChatOpenAI(model=MODEL_NAME, max_completion_tokens=10)
+    message = HumanMessage(
+        content=[
+            {
+                "type": "text",
+                "text": "Stable prefix",
+                "extras": {
+                    "prompt_cache_breakpoint": {"mode": "explicit"},
+                    "unsupported": "value",
+                },
+            }
+        ]
+    )
+
+    payload = chat._get_request_payload([message])
+
+    assert payload["messages"][0]["content"] == [
+        {
+            "type": "text",
+            "text": "Stable prefix",
+            "prompt_cache_breakpoint": {"mode": "explicit"},
+        }
+    ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/langchain_openai-1.3.4/tests/unit_tests/chat_models/test_responses_stream.py
 
new/langchain_openai-1.3.5/tests/unit_tests/chat_models/test_responses_stream.py
--- 
old/langchain_openai-1.3.4/tests/unit_tests/chat_models/test_responses_stream.py
    2020-02-02 01:00:00.000000000 +0100
+++ 
new/langchain_openai-1.3.5/tests/unit_tests/chat_models/test_responses_stream.py
    2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,7 @@
 from __future__ import annotations
 
 import copy
+import logging
 from typing import Any, cast
 from unittest.mock import MagicMock, patch
 
@@ -608,7 +609,9 @@
             truncation="disabled",
             usage=ResponseUsage(
                 input_tokens=13,
-                input_tokens_details=InputTokensDetails(cached_tokens=0),
+                input_tokens_details=InputTokensDetails(
+                    cache_write_tokens=0, cached_tokens=0
+                ),
                 output_tokens=71,
                 output_tokens_details=OutputTokensDetails(reasoning_tokens=64),
                 total_tokens=84,
@@ -1004,7 +1007,9 @@
                 truncation="disabled",
                 usage=ResponseUsage(
                     input_tokens=10,
-                    input_tokens_details=InputTokensDetails(cached_tokens=0),
+                    input_tokens_details=InputTokensDetails(
+                        cache_write_tokens=0, cached_tokens=0
+                    ),
                     output_tokens=20,
                     
output_tokens_details=OutputTokensDetails(reasoning_tokens=0),
                     total_tokens=30,
@@ -1078,15 +1083,15 @@
     ("event_index", "event_type"),
     [(0, ResponseCreatedEvent), (46, ResponseCompletedEvent)],
 )
-def test_responses_stream_normalizes_in_memory_prompt_cache_retention(
-    event_index: int, event_type: type
+def test_responses_stream_validates_in_memory_prompt_cache_retention(
+    event_index: int, event_type: type, caplog: pytest.LogCaptureFixture
 ) -> None:
     """`prompt_cache_retention="in_memory"` from the API must not abort 
streams.
 
-    The API emits the underscore form while older `openai` packages declare 
only
-    `"in-memory"` in the Literal (openai-python#2883). `_coerce_chunk_response`
-    should normalize so both the `response.created` and `response.completed`
-    handlers can validate successfully.
+    The OpenAI SDK accepts the underscore form, so both the `response.created`
+    and `response.completed` handlers should validate it via the strict
+    `Response.model_validate` path -- not the non-validating `model_construct`
+    fallback (which would also complete the stream, masking a regression).
     """
     stream = copy.deepcopy(responses_stream)
     target = stream[event_index]
@@ -1105,12 +1110,19 @@
     mock_client.responses.create = mock_create
 
     full: BaseMessageChunk | None = None
-    with patch.object(llm, "root_client", mock_client):
+    with (
+        caplog.at_level(logging.WARNING),
+        patch.object(llm, "root_client", mock_client),
+    ):
         for chunk in llm.stream("test"):
             assert isinstance(chunk, AIMessageChunk)
             full = chunk if full is None else full + chunk
     assert isinstance(full, AIMessageChunk)
     assert full.id == "resp_123"
+    # `in_memory` must validate cleanly: no fallback to the non-validating
+    # construct. Otherwise this test would pass even if the SDK rejected the
+    # value, giving false confidence in the removed normalization workaround.
+    assert "falling back to non-validating construct" not in caplog.text
     # The completed event drives usage/metadata aggregation, so assert it
     # survived coercion when that branch is exercised.
     if event_type is ResponseCompletedEvent:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/langchain_openai-1.3.4/uv.lock 
new/langchain_openai-1.3.5/uv.lock
--- old/langchain_openai-1.3.4/uv.lock  2020-02-02 01:00:00.000000000 +0100
+++ new/langchain_openai-1.3.5/uv.lock  2020-02-02 01:00:00.000000000 +0100
@@ -727,7 +727,7 @@
 
 [[package]]
 name = "langchain-openai"
-version = "1.3.4"
+version = "1.3.5"
 source = { editable = "." }
 dependencies = [
     { name = "langchain-core" },
@@ -770,7 +770,7 @@
 [package.metadata]
 requires-dist = [
     { name = "langchain-core", editable = "../../core" },
-    { name = "openai", specifier = ">=2.26.0,<3.0.0" },
+    { name = "openai", specifier = ">=2.45.0,<3.0.0" },
     { name = "tiktoken", specifier = ">=0.7.0,<1.0.0" },
 ]
 
@@ -1272,7 +1272,7 @@
 
 [[package]]
 name = "openai"
-version = "2.32.0"
+version = "2.45.0"
 source = { registry = "https://pypi.org/simple"; }
 dependencies = [
     { name = "anyio" },
@@ -1284,9 +1284,9 @@
     { name = "tqdm" },
     { name = "typing-extensions" },
 ]
-sdist = { url = 
"https://files.pythonhosted.org/packages/ed/59/bdcc6b759b8c42dd73afaf5bf8f902c04b37987a5514dbc1c64dba390fef/openai-2.32.0.tar.gz";,
 hash = 
"sha256:c54b27a9e4cb8d51f0dd94972ffd1a04437efeb259a9e60d8922b8bd26fe55e0", size 
= 693286, upload-time = "2026-04-15T22:28:19.434Z" }
+sdist = { url = 
"https://files.pythonhosted.org/packages/78/60/d4219875289b11d2c2f7da93c36283da224a2e55865ed865ab64e0ce9217/openai-2.45.0.tar.gz";,
 hash = 
"sha256:10d34ca9c5643bce775852fddbfc172505cb1d4de1ccd101696c3ecff358765d", size 
= 1109653, upload-time = "2026-07-09T18:02:44.091Z" }
 wheels = [
-    { url = 
"https://files.pythonhosted.org/packages/1e/c1/d6e64ccd0536bf616556f0cad2b6d94a8125f508d25cfd814b1d2db4e2f1/openai-2.32.0-py3-none-any.whl";,
 hash = 
"sha256:4dcc9badeb4bf54ad0d187453742f290226d30150890b7890711bda4f32f192f", size 
= 1162570, upload-time = "2026-04-15T22:28:17.714Z" },
+    { url = 
"https://files.pythonhosted.org/packages/f1/b0/2291689e3ec4723fbf5bbf3b54afcd7b160f9ddc98ca7aedfd0132af5677/openai-2.45.0-py3-none-any.whl";,
 hash = 
"sha256:5df105f5f8c9b711fcb9d06d2d3888cebc82506db216484c14a4e53cdf651777", size 
= 1629470, upload-time = "2026-07-09T18:02:42.21Z" },
 ]
 
 [[package]]

Reply via email to