mirror of
https://github.com/block/goose.git
synced 2026-07-09 16:09:22 +00:00
fix(providers): don't retry deterministically-permanent 400s (thinking-block immutability) (#10005)
Co-authored-by: Douwe M Osinga <douwe@sidewalklabs.com>
This commit is contained in:
parent
d056c72a2c
commit
c45f757c9d
1 changed files with 43 additions and 0 deletions
|
|
@ -81,11 +81,27 @@ impl RetryConfig {
|
|||
}
|
||||
}
|
||||
|
||||
/// Substrings marking a `RequestFailed` (4xx) as deterministically permanent:
|
||||
/// Anthropic rejects signed `thinking`/`redacted_thinking` blocks as immutable
|
||||
/// once a thinking model's config changes mid-conversation, and the identical
|
||||
/// payload is rebuilt on every retry — so retrying can never succeed.
|
||||
const PERMANENT_REQUEST_FAILURE_MARKERS: &[&str] = &[
|
||||
"blocks in the latest assistant message cannot be modified",
|
||||
"must remain as they were in the original response",
|
||||
];
|
||||
|
||||
fn is_permanent_request_failure(message: &str) -> bool {
|
||||
PERMANENT_REQUEST_FAILURE_MARKERS
|
||||
.iter()
|
||||
.any(|marker| message.contains(marker))
|
||||
}
|
||||
|
||||
pub fn should_retry(error: &ProviderError, config: &RetryConfig) -> bool {
|
||||
match error {
|
||||
ProviderError::RateLimitExceeded { .. }
|
||||
| ProviderError::ServerError(_)
|
||||
| ProviderError::NetworkError(_) => true,
|
||||
ProviderError::RequestFailed(message) if is_permanent_request_failure(message) => false,
|
||||
ProviderError::RequestFailed(_) => !config.transient_only,
|
||||
_ => false,
|
||||
}
|
||||
|
|
@ -254,6 +270,33 @@ mod tests {
|
|||
assert!(should_retry(&error, &config));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn never_retries_permanent_thinking_block_400() {
|
||||
let config = RetryConfig::default();
|
||||
let error = ProviderError::RequestFailed(
|
||||
"Bad request (400): {\"message\":\"messages.3.content.1: `thinking` or \
|
||||
`redacted_thinking` blocks in the latest assistant message cannot be \
|
||||
modified. These blocks must remain as they were in the original \
|
||||
response.\"}"
|
||||
.into(),
|
||||
);
|
||||
assert!(!should_retry(&error, &config));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn permanent_request_failure_marker_detection() {
|
||||
assert!(is_permanent_request_failure(
|
||||
"messages.3.content.1: `thinking` blocks in the latest assistant message \
|
||||
cannot be modified"
|
||||
));
|
||||
assert!(is_permanent_request_failure(
|
||||
"These blocks must remain as they were in the original response."
|
||||
));
|
||||
assert!(!is_permanent_request_failure(
|
||||
"Bad request (400): model not found"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transient_only_skips_request_failed() {
|
||||
let config = RetryConfig::default().transient_only();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue