Add recovery guidance to content-filter retries (#49119)

## Why

Responses stopped by `content_filter` previously followed generic stream-error retry handling without guidance about the block. Retried requests need instructions to explain the limitation and offer a permitted alternative.

## What changed

- Recognize content-filter failures as a distinct error while preserving retryability and the existing public error representation.
- Append developer guidance to the conversation after each sampling block, retaining delivered output and using the existing retry handling.
- Support a model catalog override through `model_messages.content_filter_guidance`. Missing, null, blank, or values exceeding 512 UTF-8 bytes fall back to bundled guidance.
- The bundled guidance tells the model to avoid reproducing or working around blocked content, briefly explain the limitation, offer a permitted alternative, and continue unrelated authorized work.

## Testing

Add a scenario and request-history snapshot covering repeated blocks, catalog guidance on each retry, retained partial output, and eventual completion. Add error-mapping assertions for retryability, public error classification, and error text.

GitOrigin-RevId: ba0c201b3a5a3acf5cfa971332027240e0690225
This commit is contained in:
Won Park
2026-09-29 01:14:41 +00:00
committed by copyberry
parent 6c49240565
commit 8bd5a136ff
23 changed files with 225 additions and 13 deletions
+6
View File
@@ -94,6 +94,11 @@ pub enum CodexErrorDetails {
/// The Session loop treats this as a transient error and will automatically retry the turn.
#[error("stream disconnected before completion: {0}")]
Stream(String),
/// A response stopped by the content filter. Sampling retries need developer guidance.
#[error(
"stream disconnected before completion: Incomplete response returned, reason: content_filter"
)]
ContentFilter,
/// A retryable upstream rate limit received inside the response stream.
#[error("rate limit exceeded: {0}")]
RateLimitExceeded(String),
@@ -411,6 +416,7 @@ impl CodexErr {
| CodexErrorDetails::BioPolicy { .. }
| CodexErrorDetails::MisalignmentPolicyViolation { .. } => None,
CodexErrorDetails::Stream(..)
| CodexErrorDetails::ContentFilter
| CodexErrorDetails::RateLimitExceeded(_)
| CodexErrorDetails::Timeout
| CodexErrorDetails::RequestTimeout