use std::collections::BTreeMap; use aether_contracts::{ ResolvedTransportProfile, TRANSPORT_BACKEND_BROWSER_WREQ, TRANSPORT_HTTP_MODE_AUTO, TRANSPORT_POOL_SCOPE_KEY, }; use serde_json::Value; use serde_json::{json, Map}; use uuid::Uuid; use crate::rules::apply_local_header_rules_with_request_headers; use crate::snapshot::GatewayProviderTransportSnapshot; pub const GROK_INTERNAL_HEADER: &str = "x-aether-grok-runtime"; pub const GROK_DEFAULT_BASE_URL: &str = "https://grok.com"; pub const GROK_CHAT_PATH: &str = "/rest/app-chat/conversations/new"; pub const GROK_RATE_LIMITS_PATH: &str = "/rest/rate-limits"; pub const GROK_IMAGE_EDIT_MODEL_NAME: &str = "imagine-image-edit"; pub const GROK_IMAGE_EDIT_MODEL_KIND: &str = "imagine"; pub const GROK_DEFAULT_BROWSER_PROFILE: &str = "chrome136"; pub const GROK_DEFAULT_USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"; const GROK_IMAGE_GENERATION_MAX_COUNT: u64 = 4; const GROK_SEC_CH_UA_PLATFORM: &str = r#""macOS""#; const GROK_STATSIG_ID: &str = "ZTpUeXBlRXJyb3I6IENhbm5vdCByZWFkIHByb3BlcnRpZXMgb2YgdW5kZWZpbmVkIChyZWFkaW5nICdjaGlsZE5vZGVzJyk="; #[derive(Debug, Clone, PartialEq, Eq)] pub struct GrokBrowserProfileMetadata { pub profile_id: String, pub browser_profile: String, pub user_agent: String, pub sec_ch_ua: String, pub sec_ch_ua_platform: String, } #[derive(Debug, Clone)] pub struct GrokHeaderInput<'a> { pub transport: &'a GatewayProviderTransportSnapshot, pub transport_profile: Option<&'a ResolvedTransportProfile>, pub request_headers: Option<&'a http::HeaderMap>, pub content_type: &'a str, pub accept: &'a str, pub header_rules: Option<&'a Value>, pub provider_request_body: &'a Value, pub original_request_body: &'a Value, } pub fn is_grok_provider_transport(transport: &GatewayProviderTransportSnapshot) -> bool { transport .provider .provider_type .trim() .eq_ignore_ascii_case("grok") } pub fn grok_base_url(base_url: &str) -> String { let base_url = base_url.trim().trim_end_matches('/'); if base_url.is_empty() { GROK_DEFAULT_BASE_URL.to_string() } else { base_url.to_string() } } pub fn build_grok_upstream_url(transport: &GatewayProviderTransportSnapshot, path: &str) -> String { let base_url = grok_base_url(&transport.endpoint.base_url); let path = path.trim(); if path.starts_with('/') { format!("{base_url}{path}") } else { format!("{base_url}/{path}") } } pub fn resolve_grok_session_auth( transport: &GatewayProviderTransportSnapshot, ) -> Option<(String, String)> { let cookie = grok_cookie_from_transport(transport)?; Some(("cookie".to_string(), cookie)) } pub fn grok_browser_profile_metadata( profile_id: Option<&str>, ) -> Option { let raw = profile_id .map(str::trim) .filter(|value| !value.is_empty()) .unwrap_or(GROK_DEFAULT_BROWSER_PROFILE); let normalized = raw.trim().to_ascii_lowercase().replace(['_', '-', ' '], ""); let version = supported_grok_chrome_profile_version(&normalized)?; let profile_id = format!("chrome{version}"); Some(GrokBrowserProfileMetadata { profile_id: profile_id.clone(), browser_profile: profile_id, user_agent: format!( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{version}.0.0.0 Safari/537.36" ), sec_ch_ua: format!( r#""Google Chrome";v="{version}", "Chromium";v="{version}", "Not(A:Brand";v="24""# ), sec_ch_ua_platform: GROK_SEC_CH_UA_PLATFORM.to_string(), }) } pub fn grok_browser_profile_id_from_user_agent(user_agent: &str) -> Option { chrome_major_version_from_user_agent(user_agent, "Chrome/") .or_else(|| chrome_major_version_from_user_agent(user_agent, "Chromium/")) .and_then(|version| supported_grok_chrome_profile_version(&format!("chrome{version}"))) .map(|version| format!("chrome{version}")) } pub fn grok_browser_profile_metadata_from_resolved_transport_profile( profile: &ResolvedTransportProfile, ) -> Option { if !profile .backend .trim() .eq_ignore_ascii_case(TRANSPORT_BACKEND_BROWSER_WREQ) { return None; } let profile_id = profile .extra .as_ref() .and_then(|value| value.get("browser_profile")) .and_then(Value::as_str) .or(Some(profile.profile_id.as_str())); grok_browser_profile_metadata(profile_id) } pub fn grok_browser_resolved_transport_profile_from_auth_config( auth_config: &Map, source: &str, ) -> Option { let browser_profile = grok_browser_profile_id_from_auth_config(auth_config)?; grok_browser_resolved_transport_profile(browser_profile.as_deref(), source) } pub fn grok_browser_resolved_transport_profile( profile_id: Option<&str>, source: &str, ) -> Option { let metadata = grok_browser_profile_metadata(profile_id)?; Some(ResolvedTransportProfile { profile_id: metadata.profile_id.clone(), backend: TRANSPORT_BACKEND_BROWSER_WREQ.to_string(), http_mode: TRANSPORT_HTTP_MODE_AUTO.to_string(), pool_scope: TRANSPORT_POOL_SCOPE_KEY.to_string(), header_fingerprint: None, extra: Some(json!({ "browser_profile": metadata.browser_profile, "source": source, })), }) } pub fn grok_browser_transport_fingerprint_from_auth_config( auth_config: &Map, ) -> Option { let transport_profile = grok_browser_resolved_transport_profile_from_auth_config(auth_config, "grok_import")?; Some(json!({ "transport_profile": transport_profile })) } fn grok_browser_profile_id_from_auth_config( auth_config: &Map, ) -> Option> { if let Some(browser_profile) = grok_config_string_from_object( Some(auth_config), &[ "browser_profile", "browserProfile", "browser", "impersonate", ], ) { return Some(Some(browser_profile)); } let Some(user_agent) = grok_config_string_from_object(Some(auth_config), &["user_agent", "userAgent"]) else { return Some(None); }; grok_browser_profile_id_from_user_agent(&user_agent).map(Some) } fn chrome_major_version_from_user_agent(user_agent: &str, marker: &str) -> Option { let user_agent_lower = user_agent.to_ascii_lowercase(); let marker_lower = marker.to_ascii_lowercase(); let index = user_agent_lower.find(&marker_lower)?; let version_start = index + marker.len(); let digits = user_agent[version_start..] .chars() .take_while(|ch| ch.is_ascii_digit()) .collect::(); (!digits.is_empty()) .then_some(digits) .and_then(|digits| digits.parse::().ok()) } fn supported_grok_chrome_profile_version(normalized: &str) -> Option { let version = normalized.strip_prefix("chrome")?.parse::().ok()?; match version { 100 | 101 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 114 | 116 | 117 | 118 | 119 | 120 | 123 | 124 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 => Some(version), _ => None, } } pub fn build_grok_browser_headers(input: GrokHeaderInput<'_>) -> Option> { let cookie = grok_cookie_from_transport(input.transport)?; let base_url = grok_base_url(&input.transport.endpoint.base_url); let browser_profile = grok_browser_profile_metadata_from_resolved_transport_profile(input.transport_profile?)?; let mut headers = BTreeMap::from([ ("accept".to_string(), input.accept.to_string()), ( "accept-language".to_string(), "zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7".to_string(), ), ( "baggage".to_string(), "sentry-environment=production,sentry-release=d6add6fb0460641fd482d767a335ef72b9b6abb8,sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c".to_string(), ), ("content-type".to_string(), input.content_type.to_string()), ("cookie".to_string(), cookie), (GROK_INTERNAL_HEADER.to_string(), "1".to_string()), ("origin".to_string(), base_url.clone()), ("priority".to_string(), "u=1, i".to_string()), ("referer".to_string(), format!("{base_url}/")), ("sec-ch-ua".to_string(), browser_profile.sec_ch_ua), ("sec-ch-ua-mobile".to_string(), "?0".to_string()), ( "sec-ch-ua-platform".to_string(), browser_profile.sec_ch_ua_platform, ), ("sec-fetch-dest".to_string(), "empty".to_string()), ("sec-fetch-mode".to_string(), "cors".to_string()), ("sec-fetch-site".to_string(), "same-origin".to_string()), ("user-agent".to_string(), browser_profile.user_agent), ("x-statsig-id".to_string(), GROK_STATSIG_ID.to_string()), ("x-xai-request-id".to_string(), Uuid::new_v4().to_string()), ]); if !apply_local_header_rules_with_request_headers( &mut headers, input.header_rules, &["cookie", "content-type", "accept", GROK_INTERNAL_HEADER], input.provider_request_body, Some(input.original_request_body), input.request_headers, ) { return None; } Some(headers) } pub fn build_grok_app_chat_body( client_api_format: &str, model: Option<&str>, original_body: &Value, ) -> Value { let model = model.map(str::trim).filter(|value| !value.is_empty()); let client_api_format = client_api_format.trim().to_ascii_lowercase(); if client_api_format.eq_ignore_ascii_case("openai:image") && grok_is_image_edit_request(original_body) { return build_grok_image_edit_body(original_body); } let is_image_generation = grok_is_image_generation_request(client_api_format.as_str(), model, original_body); let message = if is_image_generation { extract_grok_image_prompt(original_body) .map(|prompt| format!("Drawing: {prompt}")) .unwrap_or_else(|| "Drawing:".to_string()) } else { match client_api_format.as_str() { "openai:responses" | "openai:responses:compact" => { extract_grok_message_from_openai_responses_body(original_body) } "claude:messages" => extract_grok_message_from_claude_messages_body(original_body), _ => extract_grok_message_from_openai_chat_body(original_body), } }; let mut payload = grok_base_app_chat_payload(message, grok_mode_id_for_model(model)); if is_image_generation { payload.insert( "imageGenerationCount".to_string(), json!(extract_grok_image_count(original_body) .unwrap_or(1) .clamp(1, GROK_IMAGE_GENERATION_MAX_COUNT)), ); if let Some(size) = extract_grok_image_option( original_body, &["aspect_ratio", "aspectRatio", "ratio", "size"], ) { payload.insert("size".to_string(), Value::String(size)); } } Value::Object(payload) } fn grok_is_image_generation_request( client_api_format: &str, model: Option<&str>, body: &Value, ) -> bool { if client_api_format.eq_ignore_ascii_case("openai:image") { return !grok_is_image_edit_request(body); } if !matches!( client_api_format, "openai:chat" | "openai:responses" | "openai:responses:compact" ) { return false; } grok_is_image_generation_model(model) || grok_has_image_generation_tool(body) } fn grok_is_image_generation_model(model: Option<&str>) -> bool { let model = model.unwrap_or_default().trim().to_ascii_lowercase(); model.contains("grok-imagine-image") && !model.contains("edit") } fn grok_has_image_generation_tool(body: &Value) -> bool { body.get("tools") .and_then(Value::as_array) .into_iter() .flatten() .any(|tool| { tool.get("type") .and_then(Value::as_str) .is_some_and(|value| value.eq_ignore_ascii_case("image_generation")) && !tool .get("action") .and_then(Value::as_str) .is_some_and(|value| value.eq_ignore_ascii_case("edit")) }) } pub fn grok_is_image_edit_request(body: &Value) -> bool { body.get("tools") .and_then(Value::as_array) .into_iter() .flatten() .any(|tool| { tool.get("type") .and_then(Value::as_str) .is_some_and(|value| value.eq_ignore_ascii_case("image_generation")) && tool .get("action") .and_then(Value::as_str) .is_some_and(|value| value.eq_ignore_ascii_case("edit")) }) } pub fn build_grok_image_edit_body(body: &Value) -> Value { json!({ "temporary": true, "modelName": GROK_IMAGE_EDIT_MODEL_NAME, "message": extract_grok_image_prompt(body).unwrap_or_default(), "enableImageGeneration": true, "returnImageBytes": false, "returnRawGrokInXaiRequest": false, "enableImageStreaming": true, "imageGenerationCount": extract_grok_image_count(body).unwrap_or(2).clamp(1, 2), "forceConcise": false, "enableSideBySide": true, "sendFinalMetadata": true, "isReasoning": false, "disableTextFollowUps": true, "responseMetadata": { "modelConfigOverride": { "modelMap": { "imageEditModel": GROK_IMAGE_EDIT_MODEL_KIND, "imageEditModelConfig": { "imageReferences": [], "parentPostId": "" } } } }, "disableMemory": true, "forceSideBySide": false, }) } fn grok_base_app_chat_payload(message: String, mode_id: &'static str) -> Map { let mut payload = Map::new(); payload.insert("collectionIds".to_string(), json!([])); payload.insert("connectors".to_string(), json!([])); payload.insert( "deviceEnvInfo".to_string(), json!({ "darkModeEnabled": false, "devicePixelRatio": 2, "screenHeight": 1329, "screenWidth": 2056, "viewportHeight": 1083, "viewportWidth": 2056, }), ); payload.insert("disableMemory".to_string(), json!(true)); payload.insert("disableSearch".to_string(), json!(false)); payload.insert("disableSelfHarmShortCircuit".to_string(), json!(false)); payload.insert("disableTextFollowUps".to_string(), json!(false)); payload.insert("enableImageGeneration".to_string(), json!(true)); payload.insert("enableImageStreaming".to_string(), json!(true)); payload.insert("enableSideBySide".to_string(), json!(true)); payload.insert("fileAttachments".to_string(), json!([])); payload.insert("forceConcise".to_string(), json!(false)); payload.insert("forceSideBySide".to_string(), json!(false)); payload.insert("imageAttachments".to_string(), json!([])); payload.insert("imageGenerationCount".to_string(), json!(2)); payload.insert("isAsyncChat".to_string(), json!(false)); payload.insert("message".to_string(), Value::String(message)); payload.insert("modeId".to_string(), Value::String(mode_id.to_string())); payload.insert("responseMetadata".to_string(), json!({})); payload.insert("returnImageBytes".to_string(), json!(false)); payload.insert("returnRawGrokInXaiRequest".to_string(), json!(false)); payload.insert("searchAllConnectors".to_string(), json!(false)); payload.insert("sendFinalMetadata".to_string(), json!(true)); payload.insert("temporary".to_string(), json!(true)); payload.insert( "toolOverrides".to_string(), json!({ "gmailSearch": false, "googleCalendarSearch": false, "outlookSearch": false, "outlookCalendarSearch": false, "googleDriveSearch": false, }), ); payload } fn grok_mode_id_for_model(model: Option<&str>) -> &'static str { let model = model.unwrap_or_default().to_ascii_lowercase(); if model.contains("4.3") || model.contains("computer") { "grok-420-computer-use-sa" } else if model.contains("multi-agent") { "heavy" } else if model.contains("non-reasoning") || model.contains("fast") || model.contains("lite") { "fast" } else if model.contains("expert") || model.contains("reasoning") { "expert" } else if model.contains("0309-heavy") { "auto" } else if model.contains("heavy") { "heavy" } else { "auto" } } fn extract_grok_message_from_openai_chat_body(body: &Value) -> String { let parts = body .get("messages") .and_then(Value::as_array) .map(|items| { items .iter() .filter_map(|message| { let role = message .get("role") .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .unwrap_or("user"); grok_chat_message_text(message).map(|text| format!("[{role}]: {text}")) }) .collect::>() }) .unwrap_or_default(); (!parts.is_empty()) .then(|| parts.join("\n\n")) .or_else(|| value_text(body.get("prompt")?)) .unwrap_or_default() } fn extract_grok_message_from_openai_responses_body(body: &Value) -> String { let mut parts = Vec::new(); if let Some(instructions) = body .get("instructions") .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) { parts.push(format!("system: {instructions}")); } if let Some(input) = body.get("input").and_then(value_text) { if !input.trim().is_empty() { parts.push(input); } } parts.join("\n\n") } fn extract_grok_message_from_claude_messages_body(body: &Value) -> String { let mut parts = Vec::new(); if let Some(system) = body.get("system").and_then(value_text) { if !system.trim().is_empty() { parts.push(format!("system: {system}")); } } if let Some(messages) = body.get("messages").and_then(Value::as_array) { for message in messages { let role = message .get("role") .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .unwrap_or("user"); if let Some(text) = grok_chat_message_text(message) { parts.push(format!("[{role}]: {text}")); } } } parts.join("\n\n") } fn grok_chat_message_text(message: &Value) -> Option { let role = message .get("role") .and_then(Value::as_str) .map(str::trim) .unwrap_or_default(); if role.eq_ignore_ascii_case("tool") { let text = value_text(message.get("content")?)?; let text = text.trim(); if text.is_empty() { return None; } let label = message .get("tool_call_id") .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .map(|id| format!("[tool result for {id}]")) .unwrap_or_else(|| "[tool result]".to_string()); return Some(format!("{label}:\n{text}")); } if role.eq_ignore_ascii_case("assistant") { if let Some(tool_calls) = message.get("tool_calls").and_then(Value::as_array) { if !tool_calls.is_empty() { let mut parts = Vec::new(); if let Some(text) = message.get("content").and_then(value_text) { let text = text.trim(); if !text.is_empty() { parts.push(text.to_string()); } } parts.push(grok_tool_calls_text(tool_calls)); return Some(parts.join("\n")); } } } let text = value_text(message.get("content")?)?; let text = strip_grok_generated_artifacts(text.trim()) .trim() .to_string(); (!text.is_empty()).then_some(text) } fn grok_tool_calls_text(tool_calls: &[Value]) -> String { let mut parts = Vec::new(); for tool_call in tool_calls { let function = tool_call.get("function").unwrap_or(tool_call); let name = function .get("name") .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .unwrap_or("tool"); let arguments = function .get("arguments") .and_then(value_text) .unwrap_or_else(|| "{}".to_string()); parts.push(format!( "{arguments}" )); } parts.join("\n") } fn strip_grok_generated_artifacts(text: &str) -> String { let marker = "[grok2api-sources]: #"; let Some(marker_index) = text.find(marker) else { return text.to_string(); }; let prefix = &text[..marker_index]; if let Some(sources_index) = prefix.rfind("## Sources") { prefix[..sources_index].trim_end().to_string() } else { text.to_string() } } fn extract_grok_image_prompt(body: &Value) -> Option { body.get("prompt") .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned) .or_else(|| body.get("input").and_then(value_text)) .or_else(|| extract_grok_last_user_text_from_openai_chat_body(body)) } fn extract_grok_image_count(body: &Value) -> Option { body.get("n") .and_then(Value::as_u64) .or_else(|| body.get("imageGenerationCount").and_then(Value::as_u64)) .or_else(|| { body.get("image_config").and_then(|config| { config .get("n") .or_else(|| config.get("imageGenerationCount")) .and_then(Value::as_u64) }) }) .or_else(|| { body.get("tools") .and_then(Value::as_array) .into_iter() .flatten() .find_map(|tool| { tool.get("n") .or_else(|| tool.get("imageGenerationCount")) .and_then(Value::as_u64) }) }) } fn extract_grok_image_option(body: &Value, keys: &[&str]) -> Option { for key in keys { if let Some(value) = body .get(*key) .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) { return Some(value.to_string()); } } if let Some(config) = body.get("image_config").and_then(Value::as_object) { for key in keys { if let Some(value) = config .get(*key) .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) { return Some(value.to_string()); } } } body.get("tools") .and_then(Value::as_array) .into_iter() .flatten() .find_map(|tool| { keys.iter().find_map(|key| { tool.get(*key) .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned) }) }) } fn extract_grok_last_user_text_from_openai_chat_body(body: &Value) -> Option { body.get("messages") .and_then(Value::as_array)? .iter() .rev() .find_map(|message| { let role = message.get("role").and_then(Value::as_str)?; if !role.eq_ignore_ascii_case("user") { return None; } grok_chat_message_text(message) }) } fn value_text(value: &Value) -> Option { match value { Value::String(text) => Some(text.clone()), Value::Array(items) => { let mut parts = Vec::new(); for item in items { if let Some(text) = value_text(item) { if !text.trim().is_empty() { parts.push(text); } } } Some(parts.join("\n")) } Value::Object(object) => { if is_attachment_content_block(object) { return None; } if let Some(text) = object .get("text") .or_else(|| object.get("input_text")) .or_else(|| object.get("content")) .and_then(value_text) { return Some(text); } None } _ => None, } } fn is_attachment_content_block(object: &Map) -> bool { let Some(block_type) = object.get("type").and_then(Value::as_str) else { return object.contains_key("image_url") || object.contains_key("file") || object.contains_key("file_data") || object.contains_key("file_url"); }; matches!( block_type, "image_url" | "input_image" | "input_file" | "file" | "image" | "document" ) } fn parse_grok_auth_config(transport: &GatewayProviderTransportSnapshot) -> Option { transport .key .decrypted_auth_config .as_deref() .map(str::trim) .filter(|value| !value.is_empty()) .and_then(|value| serde_json::from_str::(value).ok()) } fn grok_cookie_from_transport(transport: &GatewayProviderTransportSnapshot) -> Option { let auth_config = parse_grok_auth_config(transport); let token = auth_config .as_ref() .and_then(|config| { grok_config_string( Some(config), &[ "sso_token", "ssoToken", "access_token", "token", "session_token", "sessionToken", ], ) }) .or_else(|| { let raw = transport.key.decrypted_api_key.trim(); (!raw.is_empty()).then(|| raw.to_string()) })?; let token = strip_cookie_prefix(token.trim(), "sso="); if token.is_empty() { return None; } let sso_rw = auth_config .as_ref() .and_then(|config| grok_config_string(Some(config), &["sso_rw_token", "ssoRwToken"])) .map(|value| strip_cookie_prefix(value.trim(), "sso-rw=")) .filter(|value| !value.is_empty()) .unwrap_or_else(|| token.clone()); let mut parts = vec![format!("sso={token}"), format!("sso-rw={sso_rw}")]; if let Some(extra_cookies) = auth_config .as_ref() .and_then(|config| grok_config_string(Some(config), &["cf_cookies", "cfCookies", "cookie"])) .and_then(|value| normalize_grok_extra_cookies(value.as_str())) { parts.push(extra_cookies); } if let Some(cf_clearance) = auth_config .as_ref() .and_then(|config| grok_config_string(Some(config), &["cf_clearance", "cfClearance"])) .map(|value| strip_cookie_prefix(value.trim(), "cf_clearance=")) .filter(|value| !value.is_empty()) { if !parts.iter().any(|part| part.contains("cf_clearance=")) { parts.push(format!("cf_clearance={cf_clearance}")); } } Some(parts.join("; ")) } fn grok_config_string(config: Option<&Value>, fields: &[&str]) -> Option { grok_config_string_from_object(config.and_then(Value::as_object), fields) } fn grok_config_string_from_object( object: Option<&Map>, fields: &[&str], ) -> Option { let object = object?; fields.iter().find_map(|field| { object .get(*field) .and_then(Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned) }) } fn strip_cookie_prefix(value: &str, prefix: &str) -> String { value .strip_prefix(prefix) .unwrap_or(value) .trim() .to_string() } fn normalize_grok_extra_cookies(value: &str) -> Option { let parts = value .trim() .trim_matches(';') .split(';') .filter_map(|segment| { let (name, value) = segment.trim().split_once('=')?; let name = name.trim(); let value = value.trim(); if name.is_empty() || value.is_empty() || name.eq_ignore_ascii_case("sso") || name.eq_ignore_ascii_case("sso-rw") { return None; } Some(format!("{name}={value}")) }) .collect::>(); (!parts.is_empty()).then(|| parts.join("; ")) } #[cfg(test)] mod tests { use aether_contracts::{ ResolvedTransportProfile, TRANSPORT_BACKEND_BROWSER_WREQ, TRANSPORT_HTTP_MODE_AUTO, TRANSPORT_POOL_SCOPE_KEY, }; use serde_json::json; use super::{ build_grok_app_chat_body, build_grok_browser_headers, build_grok_upstream_url, grok_browser_resolved_transport_profile, grok_browser_resolved_transport_profile_from_auth_config, resolve_grok_session_auth, GrokHeaderInput, GROK_CHAT_PATH, GROK_INTERNAL_HEADER, }; use crate::snapshot::{ GatewayProviderTransportEndpoint, GatewayProviderTransportKey, GatewayProviderTransportProvider, GatewayProviderTransportSnapshot, }; fn sample_transport(auth_config: serde_json::Value) -> GatewayProviderTransportSnapshot { GatewayProviderTransportSnapshot { provider: GatewayProviderTransportProvider { id: "provider-1".to_string(), name: "Grok".to_string(), provider_type: "grok".to_string(), website: None, is_active: true, keep_priority_on_conversion: false, enable_format_conversion: true, concurrent_limit: None, max_retries: None, proxy: None, request_timeout_secs: None, stream_first_byte_timeout_secs: None, config: None, }, endpoint: GatewayProviderTransportEndpoint { id: "endpoint-1".to_string(), provider_id: "provider-1".to_string(), api_format: "openai:chat".to_string(), api_family: None, endpoint_kind: None, is_active: true, base_url: "https://grok.com/".to_string(), header_rules: None, body_rules: None, max_retries: None, custom_path: None, config: None, format_acceptance_config: None, proxy: None, }, key: GatewayProviderTransportKey { id: "key-1".to_string(), provider_id: "provider-1".to_string(), name: "key".to_string(), auth_type: "oauth".to_string(), is_active: true, api_formats: None, auth_type_by_format: None, allow_auth_channel_mismatch_formats: None, allowed_models: None, capabilities: None, rate_multipliers: None, global_priority_by_format: None, expires_at_unix_secs: None, proxy: None, fingerprint: None, decrypted_api_key: String::new(), decrypted_auth_config: Some(auth_config.to_string()), }, } } #[test] fn resolves_sso_cookie_from_auth_config() { let transport = sample_transport(json!({ "sso_token": "sso=abc", "sso_rw_token": "rw", "cf_clearance": "cf" })); let (_, value) = resolve_grok_session_auth(&transport).expect("cookie should resolve"); assert_eq!(value, "sso=abc; sso-rw=rw; cf_clearance=cf"); } #[test] fn resolves_sso_cookie_without_duplicate_session_cookies() { let transport = sample_transport(json!({ "sso_token": "abc", "sso_rw_token": "rw", "cf_cookies": "i18nextLng=zh; sso=ignored; sso-rw=ignored-rw; cf_clearance=cf" })); let (_, value) = resolve_grok_session_auth(&transport).expect("cookie should resolve"); assert_eq!(value, "sso=abc; sso-rw=rw; i18nextLng=zh; cf_clearance=cf"); } #[test] fn builds_grok_browser_headers_with_internal_marker() { let transport = sample_transport(json!({"sso_token": "abc"})); let transport_profile = grok_browser_resolved_transport_profile(None, "test").expect("profile should resolve"); let headers = build_grok_browser_headers(GrokHeaderInput { transport: &transport, transport_profile: Some(&transport_profile), request_headers: None, content_type: "application/json", accept: "*/*", header_rules: None, provider_request_body: &json!({"message":"hi"}), original_request_body: &json!({"messages":[]}), }) .expect("headers should build"); assert_eq!(headers.get(GROK_INTERNAL_HEADER), Some(&"1".to_string())); assert_eq!( headers.get("cookie"), Some(&"sso=abc; sso-rw=abc".to_string()) ); assert_eq!(headers.get("origin"), Some(&"https://grok.com".to_string())); assert!(headers .get("user-agent") .is_some_and(|value| value.contains("Chrome/136.0.0.0"))); assert_eq!( headers.get("sec-ch-ua"), Some( &r#""Google Chrome";v="136", "Chromium";v="136", "Not(A:Brand";v="24""#.to_string() ) ); assert_eq!( headers.get("sec-ch-ua-platform"), Some(&r#""macOS""#.to_string()) ); } #[test] fn builds_grok_browser_headers_from_transport_profile() { let mut transport = sample_transport(json!({ "sso_token": "abc", "user_agent": "Mozilla/5.0 custom", "browser_profile": "chrome136" })); transport.key.fingerprint = Some(json!({ "transport_profile": { "profile_id": "chrome137", "backend": "browser_wreq", "extra": {"browser_profile": "chrome137"} } })); let transport_profile = grok_browser_resolved_transport_profile(Some("chrome137"), "test") .expect("profile should resolve"); let headers = build_grok_browser_headers(GrokHeaderInput { transport: &transport, transport_profile: Some(&transport_profile), request_headers: None, content_type: "application/json", accept: "*/*", header_rules: None, provider_request_body: &json!({"message":"hi"}), original_request_body: &json!({"messages":[]}), }) .expect("headers should build"); assert!(headers .get("user-agent") .is_some_and(|value| value.contains("Chrome/137.0.0.0"))); assert_eq!( headers.get("sec-ch-ua"), Some( &r#""Google Chrome";v="137", "Chromium";v="137", "Not(A:Brand";v="24""#.to_string() ) ); } #[test] fn infers_grok_browser_profile_from_legacy_user_agent() { let auth_config = json!({ "sso_token": "abc", "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" }); let auth_config = auth_config.as_object().expect("object"); let profile = grok_browser_resolved_transport_profile_from_auth_config(auth_config, "test") .expect("profile should resolve from user agent"); assert_eq!(profile.profile_id, "chrome137"); assert_eq!( profile .extra .as_ref() .and_then(|value| value.get("browser_profile")) .and_then(serde_json::Value::as_str), Some("chrome137") ); } #[test] fn rejects_unsupported_legacy_grok_user_agent_profile() { let auth_config = json!({ "sso_token": "abc", "user_agent": "Mozilla/5.0 Version/18.0 Safari/605.1.15" }); let auth_config = auth_config.as_object().expect("object"); assert!( grok_browser_resolved_transport_profile_from_auth_config(auth_config, "test").is_none() ); } #[test] fn rejects_unsupported_grok_browser_header_profile() { let mut transport = sample_transport(json!({"sso_token": "abc"})); transport.key.fingerprint = Some(json!({ "transport_profile": { "profile_id": "safari999", "backend": "browser_wreq", "extra": {"browser_profile": "safari999"} } })); let transport_profile = ResolvedTransportProfile { profile_id: "safari999".to_string(), backend: TRANSPORT_BACKEND_BROWSER_WREQ.to_string(), http_mode: TRANSPORT_HTTP_MODE_AUTO.to_string(), pool_scope: TRANSPORT_POOL_SCOPE_KEY.to_string(), header_fingerprint: None, extra: Some(json!({"browser_profile": "safari999"})), }; let headers = build_grok_browser_headers(GrokHeaderInput { transport: &transport, transport_profile: Some(&transport_profile), request_headers: None, content_type: "application/json", accept: "*/*", header_rules: None, provider_request_body: &json!({"message":"hi"}), original_request_body: &json!({"messages":[]}), }); assert!(headers.is_none()); } #[test] fn builds_rest_chat_url_from_endpoint_base() { let transport = sample_transport(json!({"sso_token": "abc"})); assert_eq!( build_grok_upstream_url(&transport, GROK_CHAT_PATH), "https://grok.com/rest/app-chat/conversations/new" ); } #[test] fn builds_app_chat_body_from_openai_chat_messages() { let body = build_grok_app_chat_body( "openai:chat", Some("grok-4.20-fast"), &json!({ "messages": [ {"role": "system", "content": "be short"}, {"role": "user", "content": [{"type":"text", "text":"hello"}]} ] }), ); assert_eq!(body["modeId"], "fast"); assert_eq!(body["message"], "[system]: be short\n\n[user]: hello"); assert_eq!(body["enableImageGeneration"], true); } #[test] fn builds_app_chat_body_omits_uploaded_attachment_blocks_from_prompt() { let body = build_grok_app_chat_body( "openai:chat", Some("grok-4.20-fast"), &json!({ "messages": [{ "role": "user", "content": [ {"type": "text", "text": "describe this"}, {"type": "image_url", "image_url": {"url": "https://example.com/a.png"}}, {"type": "file", "file": {"filename": "notes.txt", "file_data": "data:text/plain;base64,bm90ZXM="}} ] }] }), ); assert_eq!(body["message"], "[user]: describe this"); } #[test] fn builds_app_chat_body_keeps_openai_chat_history() { let body = build_grok_app_chat_body( "openai:chat", Some("grok-4.20-fast"), &json!({ "messages": [ {"role": "system", "content": "remember the code word"}, {"role": "user", "content": "the code word is lantern"}, {"role": "assistant", "content": "Got it.\n\n## Sources\n[grok2api-sources]: #\n- [old](https://example.com)"}, {"role": "user", "content": "what is the code word?"} ] }), ); assert_eq!( body["message"], "[system]: remember the code word\n\n[user]: the code word is lantern\n\n[assistant]: Got it.\n\n[user]: what is the code word?" ); } #[test] fn builds_app_chat_body_keeps_tool_context() { let body = build_grok_app_chat_body( "openai:chat", Some("grok-4.20-fast"), &json!({ "messages": [ { "role": "assistant", "content": null, "tool_calls": [{ "id": "call_1", "type": "function", "function": {"name": "lookup", "arguments": "{\"q\":\"weather\"}"} }] }, {"role": "tool", "tool_call_id": "call_1", "content": "sunny"} ] }), ); assert!(body["message"] .as_str() .expect("message should be string") .contains("{\"q\":\"weather\"}")); assert!(body["message"] .as_str() .expect("message should be string") .contains("[tool]: [tool result for call_1]:\nsunny")); } #[test] fn maps_grok_0309_model_variants_to_web_modes() { let request = json!({"messages": [{"role": "user", "content": "hello"}]}); let fast = build_grok_app_chat_body( "openai:chat", Some("grok-4.20-0309-non-reasoning-heavy"), &request, ); let auto = build_grok_app_chat_body("openai:chat", Some("grok-4.20-0309-heavy"), &request); let expert = build_grok_app_chat_body( "openai:chat", Some("grok-4.20-0309-reasoning-super"), &request, ); let heavy = build_grok_app_chat_body("openai:chat", Some("grok-4.20-multi-agent-0309"), &request); assert_eq!(fast["modeId"], "fast"); assert_eq!(auto["modeId"], "auto"); assert_eq!(expert["modeId"], "expert"); assert_eq!(heavy["modeId"], "heavy"); } #[test] fn builds_app_chat_body_from_openai_image_prompt() { let body = build_grok_app_chat_body( "openai:image", Some("grok-imagine-image"), &json!({"prompt":"a red chair", "n": 3}), ); assert_eq!(body["modeId"], "auto"); assert_eq!(body["message"], "Drawing: a red chair"); assert_eq!(body["imageGenerationCount"], 3); } #[test] fn builds_app_chat_body_from_openai_responses_image_generation_tool() { let body = build_grok_app_chat_body( "openai:responses", Some("grok-imagine-image-lite"), &json!({ "input": "a red chair", "tools": [{ "type": "image_generation", "n": 4, "size": "1280x720" }] }), ); assert_eq!(body["modeId"], "fast"); assert_eq!(body["message"], "Drawing: a red chair"); assert_eq!(body["imageGenerationCount"], 4); assert_eq!(body["size"], "1280x720"); } #[test] fn app_chat_image_generation_count_uses_gateway_ceiling() { let body = build_grok_app_chat_body( "openai:responses", Some("grok-imagine-image-lite"), &json!({ "input": "a red chair", "tools": [{ "type": "image_generation", "n": 10 }] }), ); assert_eq!(body["imageGenerationCount"], 4); } #[test] fn builds_app_chat_body_from_openai_chat_image_model_request() { let body = build_grok_app_chat_body( "openai:chat", Some("grok-imagine-image-pro"), &json!({ "messages": [ {"role": "system", "content": "ignore prior style"}, {"role": "user", "content": [{"type":"text", "text":"a blue sofa"}]} ], "image_config": { "n": 2, "size": "720x1280" } }), ); assert_eq!(body["message"], "Drawing: a blue sofa"); assert_eq!(body["imageGenerationCount"], 2); assert_eq!(body["size"], "720x1280"); } #[test] fn builds_app_chat_body_from_openai_image_edit_request() { let body = build_grok_app_chat_body( "openai:image", Some("grok-imagine-image-edit"), &json!({ "input": [ {"type": "input_text", "text": "make the chair blue"}, {"type": "input_image", "image_url": "data:image/png;base64,aW1hZ2U="} ], "tools": [{ "type": "image_generation", "action": "edit" }] }), ); assert_eq!(body["modelName"], "imagine-image-edit"); assert_eq!(body["message"], "make the chair blue"); assert_eq!(body["enableImageGeneration"], true); assert_eq!(body["enableImageStreaming"], true); assert_eq!(body["disableTextFollowUps"], true); assert_eq!(body["imageGenerationCount"], 2); assert_eq!( body["responseMetadata"]["modelConfigOverride"]["modelMap"]["imageEditModel"], "imagine" ); } }