Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ dependencies = [
"nbconvert>=7.17.1",
"langchain>=1.3.9",
"langchain-anthropic>=1.0.0",
"langchain-openai>=1.1.14",
"langchain-openai>=1.3.5",
"langchain-text-splitters>=0.3.0",
"beautifulsoup4>=4.12.0",
"requests>=2.31.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# :snippet-start: openai-prompt-cache-breakpoint-chat-completions-py
from langchain_openai import ChatOpenAI

# KEEP MODEL
llm = ChatOpenAI(
model="gpt-5.6-sol",
prompt_cache_options={"mode": "explicit"},
)

messages = [
{
"role": "system",
"content": [
{
"type": "text",
"text": (
"You are a helpful assistant with access to a large knowledge base."
),
"prompt_cache_breakpoint": {"mode": "explicit"}, # [!code highlight]
}
],
},
{"role": "user", "content": "Summarize the key points."},
]

response = llm.invoke(messages, prompt_cache_key="docs-breakpoint-v1")
# :snippet-end:

# :remove-start:
if __name__ == "__main__":
# Breakpoints only apply to GPT-5.6+; OpenAI requires a prefix of at least
# 1024 tokens before cache reads/writes appear in usage metadata.
stable_prefix = "Stable, cacheable instructions and reference material. " * 400
cache_messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": stable_prefix,
"prompt_cache_breakpoint": {"mode": "explicit"},
},
{"type": "text", "text": "Say hello."},
],
}
]
cache_key = "docs-breakpoint-cache-test-chat-completions-v1"

first = llm.invoke(cache_messages, prompt_cache_key=cache_key)
second = llm.invoke(cache_messages, prompt_cache_key=cache_key)

assert first.usage_metadata is not None
assert second.usage_metadata is not None
first_details = first.usage_metadata["input_token_details"]
second_details = second.usage_metadata["input_token_details"]
cache_read = second_details.get("cache_read") or 0

print(f"first invoke input_token_details: {first_details}")
print(f"second invoke input_token_details: {second_details}")
assert cache_read > 0, (
"expected cache_read > 0 on second invoke with identical "
f"breakpoint prefix, got {second_details}"
)
print("✓ prompt cache breakpoint (Chat Completions) sample completed")
# :remove-end:
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# :snippet-start: openai-prompt-cache-breakpoint-extras-py
content_block = {
"type": "text",
"text": "Long system prompt...",
"extras": {"prompt_cache_breakpoint": {"mode": "explicit"}},
}
# :snippet-end:

# :remove-start:
if __name__ == "__main__":
from langchain_openai import ChatOpenAI

assert content_block["extras"]["prompt_cache_breakpoint"] == {"mode": "explicit"}

# Breakpoints only apply to GPT-5.6+; OpenAI requires a prefix of at least
# 1024 tokens before cache reads/writes appear in usage metadata.
stable_prefix = "Stable, cacheable instructions and reference material. " * 400
# KEEP MODEL
llm = ChatOpenAI(
model="gpt-5.6-sol",
prompt_cache_options={"mode": "explicit"},
)
cache_messages = [
{
"role": "user",
"content": [
{
**content_block,
"text": stable_prefix,
},
{"type": "text", "text": "Say hello."},
],
}
]
cache_key = "docs-breakpoint-extras-v1"
first = llm.invoke(cache_messages, prompt_cache_key=cache_key)
second = llm.invoke(cache_messages, prompt_cache_key=cache_key)

assert first.usage_metadata is not None
assert second.usage_metadata is not None
cache_read = second.usage_metadata["input_token_details"].get("cache_read") or 0
assert cache_read > 0, (
"expected cache_read > 0 when breakpoint is nested in extras, "
f"got {second.usage_metadata['input_token_details']}"
)
print("✓ extras prompt_cache_breakpoint sample completed")
# :remove-end:
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# :snippet-start: openai-prompt-cache-breakpoint-responses-py
from langchain_openai import ChatOpenAI

# KEEP MODEL
llm = ChatOpenAI(
model="gpt-5.6-sol",
use_responses_api=True,
prompt_cache_options={"mode": "explicit"},
)

messages = [
{
"role": "system",
"content": [
{
"type": "text",
"text": (
"You are a helpful assistant with access to a large knowledge base."
),
"prompt_cache_breakpoint": {"mode": "explicit"}, # [!code highlight]
}
],
},
{"role": "user", "content": "Summarize the key points."},
]

response = llm.invoke(messages, prompt_cache_key="docs-breakpoint-v1")
# :snippet-end:

# :remove-start:
if __name__ == "__main__":
# Breakpoints only apply to GPT-5.6+; OpenAI requires a prefix of at least
# 1024 tokens before cache reads/writes appear in usage metadata.
stable_prefix = "Stable, cacheable instructions and reference material. " * 400
cache_messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": stable_prefix,
"prompt_cache_breakpoint": {"mode": "explicit"},
},
{"type": "text", "text": "Say hello."},
],
}
]
cache_key = "docs-breakpoint-cache-test-responses-v1"

first = llm.invoke(cache_messages, prompt_cache_key=cache_key)
second = llm.invoke(cache_messages, prompt_cache_key=cache_key)

assert first.usage_metadata is not None
assert second.usage_metadata is not None
first_details = first.usage_metadata["input_token_details"]
second_details = second.usage_metadata["input_token_details"]
cache_read = second_details.get("cache_read") or 0

print(f"first invoke input_token_details: {first_details}")
print(f"second invoke input_token_details: {second_details}")
assert cache_read > 0, (
"expected cache_read > 0 on second invoke with identical "
f"breakpoint prefix, got {second_details}"
)
print("✓ prompt cache breakpoint (Responses API) sample completed")
# :remove-end:
41 changes: 41 additions & 0 deletions src/code-samples/langchain/openai-prompt-cache-options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# :snippet-start: openai-prompt-cache-options-py
from langchain_openai import ChatOpenAI

# KEEP MODEL
llm = ChatOpenAI(
model="gpt-5.6-sol",
prompt_cache_options={"mode": "explicit", "ttl": "30m"},
)

messages = [{"role": "user", "content": "Hello"}]

# Override per request
response = llm.invoke(
messages,
prompt_cache_options={"mode": "implicit"},
)
# :snippet-end:

# :remove-start:
if __name__ == "__main__":
assert response is not None
assert response.usage_metadata is not None

# Confirm model-level options remain available on a follow-up call, and that
# a per-request override is accepted without error.
default_response = llm.invoke(messages)
assert default_response is not None

# KEEP MODEL
responses_llm = ChatOpenAI(
model="gpt-5.6-sol",
use_responses_api=True,
prompt_cache_options={"mode": "explicit", "ttl": "30m"},
)
responses_result = responses_llm.invoke(
messages,
prompt_cache_options={"mode": "implicit"},
)
assert responses_result is not None
print("✓ prompt_cache_options model-level and per-request override completed")
# :remove-end:
55 changes: 55 additions & 0 deletions src/code-samples/langchain/openai-prompt-cache-write-tokens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# :remove-start:
from langchain_openai import ChatOpenAI

# Breakpoints only apply to GPT-5.6+; OpenAI requires a prefix of at least
# 1024 tokens before cache reads/writes appear in usage metadata.
stable_prefix = "Stable, cacheable instructions and reference material. " * 400
# KEEP MODEL
llm = ChatOpenAI(
model="gpt-5.6-sol",
prompt_cache_options={"mode": "explicit"},
)
messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": stable_prefix,
"prompt_cache_breakpoint": {"mode": "explicit"},
},
{"type": "text", "text": "Say hello."},
],
}
]
# :remove-end:

# :snippet-start: openai-prompt-cache-write-tokens-py
response = llm.invoke(messages)

cache_read = response.usage_metadata["input_token_details"].get("cache_read")
cache_creation = response.usage_metadata["input_token_details"].get("cache_creation")
print(f"Cache read tokens: {cache_read}")
print(f"Cache creation tokens: {cache_creation}")
# :snippet-end:

# :remove-start:
if __name__ == "__main__":
assert response is not None
assert response.usage_metadata is not None
# Exercise the documented accessors; a second call should show a cache read.
cache_key = "docs-prompt-cache-write-tokens-v1"
first = llm.invoke(messages, prompt_cache_key=cache_key)
second = llm.invoke(messages, prompt_cache_key=cache_key)
assert first.usage_metadata is not None
assert second.usage_metadata is not None
first_details = first.usage_metadata["input_token_details"]
second_details = second.usage_metadata["input_token_details"]
print(f"first invoke input_token_details: {first_details}")
print(f"second invoke input_token_details: {second_details}")
cache_read_second = second_details.get("cache_read") or 0
assert cache_read_second > 0, (
f"expected cache_read > 0 on second invoke, got {second_details}"
)
print("✓ cache write/read token reporting sample completed")
# :remove-end:
52 changes: 52 additions & 0 deletions src/oss/python/integrations/chat/openai.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ title: "ChatOpenAI integration"
description: "Integrate with the ChatOpenAI chat model using LangChain Python."
---

import OpenaiPromptCacheBreakpointChatCompletionsPy from '/snippets/code-samples/openai-prompt-cache-breakpoint-chat-completions-py.mdx';
import OpenaiPromptCacheBreakpointResponsesPy from '/snippets/code-samples/openai-prompt-cache-breakpoint-responses-py.mdx';
import OpenaiPromptCacheBreakpointExtrasPy from '/snippets/code-samples/openai-prompt-cache-breakpoint-extras-py.mdx';
import OpenaiPromptCacheOptionsPy from '/snippets/code-samples/openai-prompt-cache-options-py.mdx';
import OpenaiPromptCacheWriteTokensPy from '/snippets/code-samples/openai-prompt-cache-write-tokens-py.mdx';

You can find information about OpenAI's latest models, their costs, context windows, and supported input types in the [OpenAI Platform](https://platform.openai.com) docs.

<Tip>
Expand Down Expand Up @@ -1887,6 +1893,52 @@ response1 = llm.invoke(messages)
response2 = llm.invoke(messages, prompt_cache_key="override-cache-v1")
```

### Explicit caching with breakpoints

<Note>
Requires `langchain-openai>=1.3.5`. Supported on both the Chat Completions API and the [Responses API](/oss/python/integrations/chat/openai#responses-api).
</Note>

OpenAI supports [explicit prompt-cache breakpoints](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-breakpoints), which let you designate specific content blocks as cache boundaries. This gives you fine-grained control over which parts of a prompt are cached, rather than relying solely on automatic prefix caching.

To mark a content block as a cache breakpoint, add `"prompt_cache_breakpoint": {"mode": "explicit"}` to the block. Explicit breakpoints require GPT-5.6 or later model families.

<Tabs>
<Tab title="Chat Completions">
<OpenaiPromptCacheBreakpointChatCompletionsPy />
</Tab>
<Tab title="Responses API">
<OpenaiPromptCacheBreakpointResponsesPy />
</Tab>
</Tabs>

Breakpoints are supported on text, image, and file content blocks. You can also nest `prompt_cache_breakpoint` inside an `extras` dict if you prefer to keep the LangChain content block structure clean:

<OpenaiPromptCacheBreakpointExtrasPy />

### Request-level cache options

<Note>
Requires `langchain-openai>=1.3.5`. `prompt_cache_options` applies to GPT-5.6 and later model families.
</Note>

You can pass request-level prompt cache options using the `prompt_cache_options` parameter on the model or per invocation:

- **`mode`**: `"implicit"` (default) or `"explicit"`. In `"implicit"` mode, OpenAI places a cache breakpoint on the latest message and also uses any explicit breakpoints you provide. In `"explicit"` mode, only your breakpoints are used for cache reads and writes. If the request has no explicit breakpoints, it does not use prompt caching.
- **`ttl`**: Minimum cache lifetime for breakpoints written by the request. The only supported value is `"30m"`, which is also the default.

<OpenaiPromptCacheOptionsPy />

For models before the GPT-5.6 family, use `prompt_cache_retention` instead (`"in_memory"` or `"24h"`). That field is separate from `prompt_cache_options` and is deprecated on GPT-5.6 and later model families.

### Cache write tokens

When OpenAI writes new content to the prompt cache, it reports `cache_write_tokens` in the response. `ChatOpenAI` surfaces this as `cache_creation` in `input_token_details`:

<OpenaiPromptCacheWriteTokensPy />

On the `"priority"` and `"flex"` service tiers, these keys are prefixed with the tier name — for example, `"priority_cache_read"` and `"priority_cache_creation"`.

---

## Flex processing
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
```python
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
model="gpt-5.6-sol",
prompt_cache_options={"mode": "explicit"},
)

messages = [
{
"role": "system",
"content": [
{
"type": "text",
"text": (
"You are a helpful assistant with access to a large knowledge base."
),
"prompt_cache_breakpoint": {"mode": "explicit"}, # [!code highlight]
}
],
},
{"role": "user", "content": "Summarize the key points."},
]

response = llm.invoke(messages, prompt_cache_key="docs-breakpoint-v1")
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```python
content_block = {
"type": "text",
"text": "Long system prompt...",
"extras": {"prompt_cache_breakpoint": {"mode": "explicit"}},
}
```
Loading
Loading