Skip to main content

gproxy_transform/transform/common/
claude_mid_conv_system.rs

1//! Claude model capability gate for mid-conversation system turns
2//! (`mid_conv_system` content block / `system` message role).
3//!
4//! The Claude API surfaces both as a system-role turn internally; models
5//! released before Opus 4.8 reject them with `role 'system' is not supported
6//! on this model`. For those, a mid-conversation system message must be
7//! downgraded to a plain assistant turn instead.
8
9/// Models without mid-conversation system support. Substring match
10/// tolerates vendor prefixes (`anthropic.`, `us.anthropic.`) and date/version
11/// suffixes (`claude-opus-4-20250514` hits `claude-opus-4-2`).
12const PRE_OPUS_48: &[&str] = &[
13    "claude-instant",
14    "claude-1",
15    "claude-2",
16    "claude-3",
17    "claude-sonnet-4",
18    "claude-haiku-4",
19    "claude-4-",
20    "claude-opus-4-0",
21    "claude-opus-4-1",
22    "claude-opus-4-2",
23    "claude-opus-4-3",
24    "claude-opus-4-4",
25    "claude-opus-4-5",
26    "claude-opus-4-6",
27    "claude-opus-4-7",
28    "claude-opus-4@",
29    "claude-sonnet-5",
30];
31
32/// Whether this Claude model accepts a mid-conversation system turn.
33/// Unknown/future models default to `true`.
34pub fn supports_mid_conv_system(model: &str) -> bool {
35    let m = model.to_ascii_lowercase();
36    !PRE_OPUS_48.iter().any(|p| m.contains(p))
37}
38
39#[cfg(test)]
40mod tests {
41    use super::supports_mid_conv_system;
42
43    #[test]
44    fn pre_opus_48_downgrades_and_newer_keeps() {
45        for m in [
46            "claude-3-5-sonnet-20241022",
47            "claude-sonnet-4-5",
48            "claude-haiku-4-5-20251001",
49            "claude-opus-4-20250514",
50            "claude-opus-4-1@20250805",
51            "claude-opus-4-7",
52            "us.anthropic.claude-sonnet-4-20250514-v1:0",
53        ] {
54            assert!(!supports_mid_conv_system(m), "{m} should downgrade");
55        }
56        for m in [
57            "claude-opus-4-8",
58            "claude-opus-4-9",
59            "claude-fable-5",
60            "claude-opus-5",
61        ] {
62            assert!(supports_mid_conv_system(m), "{m} should keep mid_conv");
63        }
64        assert!(!supports_mid_conv_system("claude-sonnet-5"));
65    }
66}