pub const GEMINI: &str = "gemini:";
pub const GEMINI_NEXT: &str = "gemini-next:";
pub const CLAUDE: &str = "claude:";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Carried<'a> {
GeminiThought(&'a str),
GeminiNext(&'a str),
Claude(&'a str),
}
impl<'a> Carried<'a> {
pub fn parse(value: &'a str) -> Option<Self> {
if let Some(signature) = value.strip_prefix(GEMINI_NEXT) {
Some(Self::GeminiNext(signature))
} else if let Some(signature) = value.strip_prefix(GEMINI) {
Some(Self::GeminiThought(signature))
} else {
value.strip_prefix(CLAUDE).map(Self::Claude)
}
}
pub fn is_gemini(self) -> bool {
matches!(self, Self::GeminiThought(_) | Self::GeminiNext(_))
}
}
pub fn is_gemini(value: &str) -> bool {
Carried::parse(value).is_some_and(Carried::is_gemini)
}
pub fn is_carried(value: &str) -> bool {
Carried::parse(value).is_some()
}
pub fn gemini_thought(signature: Option<&str>) -> String {
format!("{GEMINI}{}", signature.unwrap_or_default())
}
pub fn gemini_next(signature: &str) -> String {
format!("{GEMINI_NEXT}{signature}")
}
pub fn claude(signature: &str) -> String {
format!("{CLAUDE}{signature}")
}
pub fn without_carried_reasoning(body: &[u8]) -> Option<Vec<u8>> {
let needles = [GEMINI, GEMINI_NEXT, CLAUDE].map(str::as_bytes);
if !needles
.iter()
.any(|needle| body.windows(needle.len()).any(|window| window == *needle))
{
return None;
}
let mut value: serde_json::Value = serde_json::from_slice(body).ok()?;
let items = value.get_mut("input")?.as_array_mut()?;
let before = items.len();
items.retain(|item| {
item.get("type").and_then(serde_json::Value::as_str) != Some("reasoning")
|| !item
.get("encrypted_content")
.and_then(serde_json::Value::as_str)
.is_some_and(is_carried)
});
if items.len() == before {
return None;
}
serde_json::to_vec(&value).ok()
}