Add per-turn prompt cache TTL options (#59480)

## What changed

- Expose `PromptCacheTtl` through `TurnStartOptions` and `RecoverTurnRequest`, supporting `30m` and `12h` minimum cache lifetimes.
- Carry the selected TTL through turn execution and compaction into `prompt_cache_options.ttl` on Responses HTTP and WebSocket requests. Omit the field when unset and keep the selection scoped to the turn.
- Include prompt cache options in WebSocket request reuse checks.

## Testing

Add coverage for both TTL values and omission on a subsequent turn without an override, and extend WebSocket serialization coverage to include prompt cache options.

GitOrigin-RevId: 8529306e6ad6030fe77c5e6bf19f3810717c0c5e
This commit is contained in:
Rohan Mehta
2026-11-04 19:58:24 +00:00
committed by copyberry
parent c9296fa1a9
commit 41cbd5b9b2
26 changed files with 106 additions and 0 deletions
+11
View File
@@ -10,6 +10,7 @@ use codex_protocol::protocol::TokenUsage;
use codex_protocol::protocol::TurnModerationMetadataEvent;
use codex_protocol::protocol::W3cTraceContext;
use codex_protocol::turn_input::CyberAccessProgram;
use codex_protocol::turn_input::PromptCacheTtl;
use futures::Stream;
use serde::Deserialize;
use serde::Serialize;
@@ -277,6 +278,11 @@ impl Serialize for ResponsesApiTools {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct PromptCacheOptions {
pub ttl: PromptCacheTtl,
}
#[derive(Debug, Serialize, Clone, PartialEq)]
pub struct ResponsesApiRequest {
// Keep routing fields first: serde serializes struct fields in declaration order, and
@@ -298,6 +304,8 @@ pub struct ResponsesApiRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_cache_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_cache_options: Option<PromptCacheOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<TextControls>,
#[serde(skip_serializing_if = "Option::is_none")]
pub client_metadata: Option<HashMap<String, String>>,
@@ -321,6 +329,7 @@ impl<'a> From<&'a ResponsesApiRequest> for ResponseCreateWsRequest<'a> {
include: &request.include,
service_tier: request.service_tier.as_deref(),
prompt_cache_key: request.prompt_cache_key.as_deref(),
prompt_cache_options: request.prompt_cache_options,
text: request.text.as_ref(),
generate: None,
client_metadata: request.client_metadata.clone(),
@@ -352,6 +361,8 @@ pub struct ResponseCreateWsRequest<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_cache_key: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_cache_options: Option<PromptCacheOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<&'a TextControls>,
#[serde(skip_serializing_if = "Option::is_none")]
pub generate: Option<bool>,
@@ -1117,6 +1117,9 @@ mod tests {
include: vec!["reasoning.encrypted_content".to_string()],
service_tier: Some("priority".to_string()),
prompt_cache_key: Some("cache-key".to_string()),
prompt_cache_options: Some(crate::PromptCacheOptions {
ttl: codex_protocol::turn_input::PromptCacheTtl::TwelveHours,
}),
text: None,
access_programs: Some(
codex_protocol::turn_input::CyberAccessProgram::DaybreakBlue.into(),
+1
View File
@@ -37,6 +37,7 @@ pub use crate::common::AccessPrograms;
pub use crate::common::MemorySummarizeInput;
pub use crate::common::MemorySummarizeOutput;
pub use crate::common::OpenAiVerbosity;
pub use crate::common::PromptCacheOptions;
pub use crate::common::RawMemory;
pub use crate::common::RawMemoryMetadata;
pub use crate::common::Reasoning;