Files
Aether/crates/aether-provider-transport/src/grok.rs

1321 lines
46 KiB
Rust

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<GrokBrowserProfileMetadata> {
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<String> {
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<GrokBrowserProfileMetadata> {
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<String, Value>,
source: &str,
) -> Option<ResolvedTransportProfile> {
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<ResolvedTransportProfile> {
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<String, Value>,
) -> Option<Value> {
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<String, Value>,
) -> Option<Option<String>> {
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<u16> {
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::<String>();
(!digits.is_empty())
.then_some(digits)
.and_then(|digits| digits.parse::<u16>().ok())
}
fn supported_grok_chrome_profile_version(normalized: &str) -> Option<u16> {
let version = normalized.strip_prefix("chrome")?.parse::<u16>().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<BTreeMap<String, String>> {
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<String, Value> {
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::<Vec<_>>()
})
.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<String> {
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!(
"<tool_call name=\"{name}\">{arguments}</tool_call>"
));
}
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<String> {
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<u64> {
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<String> {
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<String> {
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<String> {
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<String, Value>) -> 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<Value> {
transport
.key
.decrypted_auth_config
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty())
.and_then(|value| serde_json::from_str::<Value>(value).ok())
}
fn grok_cookie_from_transport(transport: &GatewayProviderTransportSnapshot) -> Option<String> {
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<String> {
grok_config_string_from_object(config.and_then(Value::as_object), fields)
}
fn grok_config_string_from_object(
object: Option<&Map<String, Value>>,
fields: &[&str],
) -> Option<String> {
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<String> {
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::<Vec<_>>();
(!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("<tool_call name=\"lookup\">{\"q\":\"weather\"}</tool_call>"));
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"
);
}
}