mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
feat(grok): add provider pool and transport support
This commit is contained in:
@@ -8,6 +8,7 @@ use crate::conversion::{
|
||||
request_conversion_enabled_for_transport, request_conversion_transport_unsupported_reason,
|
||||
request_pair_allowed_for_transport,
|
||||
};
|
||||
use crate::grok::grok_browser_resolved_transport_profile_from_auth_config;
|
||||
use crate::network::{
|
||||
resolve_transport_profile, resolve_transport_profile_id, transport_proxy_is_locally_supported,
|
||||
};
|
||||
@@ -89,6 +90,31 @@ pub fn build_transport_diagnostics(
|
||||
.and_then(|value| value.get("transport_profile"))
|
||||
.cloned()
|
||||
.unwrap_or(Value::Null);
|
||||
let configured_legacy_grok_transport_profile = if transport
|
||||
.provider
|
||||
.provider_type
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("grok")
|
||||
{
|
||||
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())
|
||||
.and_then(|value| value.as_object().cloned())
|
||||
.and_then(|auth_config| {
|
||||
grok_browser_resolved_transport_profile_from_auth_config(
|
||||
&auth_config,
|
||||
"grok_auth_config",
|
||||
)
|
||||
.and_then(|profile| serde_json::to_value(profile).ok())
|
||||
})
|
||||
.unwrap_or(Value::Null)
|
||||
} else {
|
||||
Value::Null
|
||||
};
|
||||
let has_oauth_config = transport.key.decrypted_auth_config.is_some();
|
||||
let oauth_resolution_supported =
|
||||
!has_oauth_config || crate::supports_local_oauth_request_auth_resolution(transport);
|
||||
@@ -126,6 +152,7 @@ pub fn build_transport_diagnostics(
|
||||
"fingerprint": transport.key.fingerprint,
|
||||
"configured_key_transport_profile": configured_key_transport_profile,
|
||||
"configured_provider_transport_profile": configured_provider_transport_profile,
|
||||
"configured_legacy_grok_transport_profile": configured_legacy_grok_transport_profile,
|
||||
"resolved_transport_profile_id": resolved_transport_profile_id,
|
||||
"resolved_transport_profile": resolved_transport_profile,
|
||||
"request_pair": {
|
||||
@@ -423,6 +450,43 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
fn sample_grok_transport_with_legacy_user_agent() -> GatewayProviderTransportSnapshot {
|
||||
let mut transport = sample_transport();
|
||||
transport.provider.provider_type = "grok".to_string();
|
||||
transport.key.fingerprint = None;
|
||||
transport.provider.config = None;
|
||||
transport.key.decrypted_auth_config = Some(
|
||||
json!({
|
||||
"sso_token": "sso-token",
|
||||
"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"
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
transport
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transport_diagnostics_include_legacy_grok_transport_profile() {
|
||||
let diagnostics = build_transport_diagnostics(
|
||||
&sample_grok_transport_with_legacy_user_agent(),
|
||||
"openai:chat",
|
||||
"openai:chat",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
diagnostics["configured_legacy_grok_transport_profile"]["profile_id"],
|
||||
"chrome137"
|
||||
);
|
||||
assert_eq!(
|
||||
diagnostics["resolved_transport_profile"]["profile_id"],
|
||||
"chrome137"
|
||||
);
|
||||
assert_eq!(
|
||||
diagnostics["resolved_transport_profile"]["backend"],
|
||||
"browser_wreq"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_trace_proxy_value_sanitizes_url_and_marks_config_source() {
|
||||
let transport = sample_transport();
|
||||
|
||||
1320
crates/aether-provider-transport/src/grok.rs
Normal file
1320
crates/aether-provider-transport/src/grok.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ pub mod conversion;
|
||||
mod diagnostics;
|
||||
mod gemini_files;
|
||||
mod generic_oauth;
|
||||
pub mod grok;
|
||||
mod headers;
|
||||
pub mod kiro;
|
||||
mod network;
|
||||
@@ -44,6 +45,17 @@ pub use gemini_files::{
|
||||
pub use generic_oauth::{
|
||||
supports_local_generic_oauth_request_auth_resolution, GenericOAuthRefreshAdapter,
|
||||
};
|
||||
pub use grok::{
|
||||
build_grok_app_chat_body, build_grok_browser_headers, build_grok_upstream_url, grok_base_url,
|
||||
grok_browser_profile_id_from_user_agent,
|
||||
grok_browser_profile_metadata_from_resolved_transport_profile,
|
||||
grok_browser_resolved_transport_profile,
|
||||
grok_browser_resolved_transport_profile_from_auth_config,
|
||||
grok_browser_transport_fingerprint_from_auth_config, is_grok_provider_transport,
|
||||
resolve_grok_session_auth, GrokBrowserProfileMetadata, GrokHeaderInput, GROK_CHAT_PATH,
|
||||
GROK_DEFAULT_BASE_URL, GROK_DEFAULT_BROWSER_PROFILE, GROK_DEFAULT_USER_AGENT,
|
||||
GROK_INTERNAL_HEADER, GROK_RATE_LIMITS_PATH,
|
||||
};
|
||||
pub use headers::{should_skip_request_header, should_skip_upstream_passthrough_header};
|
||||
pub use network::{
|
||||
resolve_transport_execution_timeouts, resolve_transport_profile, resolve_transport_profile_id,
|
||||
|
||||
@@ -6,6 +6,8 @@ use async_trait::async_trait;
|
||||
use serde_json::{json, Map, Value};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::grok::grok_browser_resolved_transport_profile_from_auth_config;
|
||||
|
||||
use super::snapshot::GatewayProviderTransportSnapshot;
|
||||
|
||||
const TUNNEL_BASE_URL_EXTRA_KEY: &str = "tunnel_base_url";
|
||||
@@ -152,9 +154,39 @@ pub fn resolve_transport_profile(
|
||||
) -> Option<ResolvedTransportProfile> {
|
||||
resolve_transport_profile_from_fingerprint(transport.key.fingerprint.as_ref()).or_else(|| {
|
||||
resolve_transport_profile_from_provider_config(transport.provider.config.as_ref())
|
||||
.or_else(|| resolve_grok_browser_transport_profile(transport))
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_grok_browser_transport_profile(
|
||||
transport: &GatewayProviderTransportSnapshot,
|
||||
) -> Option<ResolvedTransportProfile> {
|
||||
if !transport
|
||||
.provider
|
||||
.provider_type
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("grok")
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let auth_config = 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())?;
|
||||
let object = auth_config.as_object()?;
|
||||
let has_session = json_string_field(object, "sso_token")
|
||||
.or_else(|| json_string_field(object, "access_token"))
|
||||
.or_else(|| json_string_field(object, "token"))
|
||||
.is_some();
|
||||
if !has_session {
|
||||
return None;
|
||||
}
|
||||
grok_browser_resolved_transport_profile_from_auth_config(object, "grok_auth_config")
|
||||
}
|
||||
|
||||
fn resolve_transport_profile_from_provider_config(
|
||||
config: Option<&Value>,
|
||||
) -> Option<ResolvedTransportProfile> {
|
||||
@@ -529,4 +561,218 @@ mod tests {
|
||||
assert!(resolve_transport_profile(&transport).is_none());
|
||||
assert!(!transport_profile_is_configured(&transport));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolves_grok_browser_transport_profile_from_session_auth_config() {
|
||||
let mut transport = sample_transport();
|
||||
transport.provider.provider_type = "grok".to_string();
|
||||
transport.key.fingerprint = None;
|
||||
transport.provider.config = None;
|
||||
transport.key.decrypted_auth_config = Some(
|
||||
json!({
|
||||
"sso_token": "sso-token",
|
||||
"browser_profile": "chrome136",
|
||||
"cf_clearance": "clearance"
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
let profile = resolve_transport_profile(&transport).expect("profile");
|
||||
|
||||
assert_eq!(profile.profile_id, "chrome136");
|
||||
assert_eq!(profile.backend, "browser_wreq");
|
||||
assert_eq!(profile.http_mode, "auto");
|
||||
assert_eq!(profile.pool_scope, "key");
|
||||
assert_eq!(
|
||||
profile
|
||||
.extra
|
||||
.as_ref()
|
||||
.and_then(|value| value.get("browser_profile"))
|
||||
.and_then(Value::as_str),
|
||||
Some("chrome136")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolves_grok_browser_transport_profile_default_from_session_auth_config() {
|
||||
let mut transport = sample_transport();
|
||||
transport.provider.provider_type = "grok".to_string();
|
||||
transport.key.fingerprint = None;
|
||||
transport.provider.config = None;
|
||||
transport.key.decrypted_auth_config = Some(
|
||||
json!({
|
||||
"sso_token": "sso-token"
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
let profile = resolve_transport_profile(&transport).expect("profile");
|
||||
|
||||
assert_eq!(profile.profile_id, "chrome136");
|
||||
assert_eq!(profile.backend, "browser_wreq");
|
||||
assert_eq!(
|
||||
profile
|
||||
.extra
|
||||
.as_ref()
|
||||
.and_then(|value| value.get("source"))
|
||||
.and_then(Value::as_str),
|
||||
Some("grok_auth_config")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolves_grok_browser_transport_profile_normalizes_auth_config_alias() {
|
||||
let mut transport = sample_transport();
|
||||
transport.provider.provider_type = "grok".to_string();
|
||||
transport.key.fingerprint = None;
|
||||
transport.provider.config = None;
|
||||
transport.key.decrypted_auth_config = Some(
|
||||
json!({
|
||||
"sso_token": "sso-token",
|
||||
"browser_profile": "Chrome-137"
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
let profile = resolve_transport_profile(&transport).expect("profile");
|
||||
|
||||
assert_eq!(profile.profile_id, "chrome137");
|
||||
assert_eq!(
|
||||
profile
|
||||
.extra
|
||||
.as_ref()
|
||||
.and_then(|value| value.get("browser_profile"))
|
||||
.and_then(Value::as_str),
|
||||
Some("chrome137")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolves_grok_browser_transport_profile_from_legacy_user_agent() {
|
||||
let mut transport = sample_transport();
|
||||
transport.provider.provider_type = "grok".to_string();
|
||||
transport.key.fingerprint = None;
|
||||
transport.provider.config = None;
|
||||
transport.key.decrypted_auth_config = Some(
|
||||
json!({
|
||||
"sso_token": "sso-token",
|
||||
"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"
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
let profile = resolve_transport_profile(&transport).expect("profile");
|
||||
|
||||
assert_eq!(profile.profile_id, "chrome137");
|
||||
assert_eq!(
|
||||
profile
|
||||
.extra
|
||||
.as_ref()
|
||||
.and_then(|value| value.get("browser_profile"))
|
||||
.and_then(Value::as_str),
|
||||
Some("chrome137")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_fingerprint_wins_over_grok_auth_config_fallback() {
|
||||
let mut transport = sample_transport();
|
||||
transport.provider.provider_type = "grok".to_string();
|
||||
transport.provider.config = None;
|
||||
transport.key.fingerprint = Some(json!({
|
||||
"transport_profile": {
|
||||
"profile_id": "chrome136",
|
||||
"backend": "browser_wreq",
|
||||
"extra": {"browser_profile": "chrome136", "source": "key"}
|
||||
}
|
||||
}));
|
||||
transport.key.decrypted_auth_config = Some(
|
||||
json!({
|
||||
"sso_token": "sso-token",
|
||||
"browser_profile": "chrome137"
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
let profile = resolve_transport_profile(&transport).expect("profile");
|
||||
|
||||
assert_eq!(profile.profile_id, "chrome136");
|
||||
assert_eq!(
|
||||
profile
|
||||
.extra
|
||||
.as_ref()
|
||||
.and_then(|value| value.get("source"))
|
||||
.and_then(Value::as_str),
|
||||
Some("key")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provider_fingerprint_wins_over_grok_auth_config_fallback() {
|
||||
let mut transport = sample_transport();
|
||||
transport.provider.provider_type = "grok".to_string();
|
||||
transport.key.fingerprint = None;
|
||||
transport.provider.config = Some(json!({
|
||||
"fingerprint": {
|
||||
"transport_profile": {
|
||||
"profile_id": "chrome136",
|
||||
"backend": "browser_wreq",
|
||||
"extra": {"browser_profile": "chrome136", "source": "provider"}
|
||||
}
|
||||
}
|
||||
}));
|
||||
transport.key.decrypted_auth_config = Some(
|
||||
json!({
|
||||
"sso_token": "sso-token",
|
||||
"browser_profile": "chrome137"
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
let profile = resolve_transport_profile(&transport).expect("profile");
|
||||
|
||||
assert_eq!(profile.profile_id, "chrome136");
|
||||
assert_eq!(
|
||||
profile
|
||||
.extra
|
||||
.as_ref()
|
||||
.and_then(|value| value.get("source"))
|
||||
.and_then(Value::as_str),
|
||||
Some("provider")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_unsupported_grok_auth_config_browser_profile() {
|
||||
let mut transport = sample_transport();
|
||||
transport.provider.provider_type = "grok".to_string();
|
||||
transport.key.fingerprint = None;
|
||||
transport.provider.config = None;
|
||||
transport.key.decrypted_auth_config = Some(
|
||||
json!({
|
||||
"sso_token": "sso-token",
|
||||
"browser_profile": "safari999"
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
assert!(resolve_transport_profile(&transport).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_unsupported_grok_auth_config_user_agent_profile() {
|
||||
let mut transport = sample_transport();
|
||||
transport.provider.provider_type = "grok".to_string();
|
||||
transport.key.fingerprint = None;
|
||||
transport.provider.config = None;
|
||||
transport.key.decrypted_auth_config = Some(
|
||||
json!({
|
||||
"sso_token": "sso-token",
|
||||
"user_agent": "Mozilla/5.0 Version/18.0 Safari/605.1.15"
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
assert!(resolve_transport_profile(&transport).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::collections::BTreeMap;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::auth::{build_passthrough_headers_with_auth, resolve_local_openai_bearer_auth};
|
||||
use crate::grok::{is_grok_provider_transport, resolve_grok_session_auth};
|
||||
use crate::policy::local_standard_transport_unsupported_reason_with_network;
|
||||
use crate::rules::apply_local_header_rules_with_request_headers;
|
||||
use crate::snapshot::GatewayProviderTransportSnapshot;
|
||||
@@ -23,21 +24,33 @@ pub fn openai_image_transport_unsupported_reason(
|
||||
api_format: &str,
|
||||
) -> Option<&'static str> {
|
||||
let reason = local_standard_transport_unsupported_reason_with_network(transport, api_format);
|
||||
if reason == Some("transport_provider_type_unsupported")
|
||||
&& transport
|
||||
.provider
|
||||
.provider_type
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("chatgpt_web")
|
||||
if is_dedicated_openai_image_provider(transport)
|
||||
&& matches!(
|
||||
reason,
|
||||
Some("transport_provider_type_unsupported")
|
||||
| Some("transport_oauth_resolution_unsupported")
|
||||
)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
reason
|
||||
}
|
||||
|
||||
fn is_dedicated_openai_image_provider(transport: &GatewayProviderTransportSnapshot) -> bool {
|
||||
transport
|
||||
.provider
|
||||
.provider_type
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("chatgpt_web")
|
||||
|| is_grok_provider_transport(transport)
|
||||
}
|
||||
|
||||
pub fn resolve_openai_image_auth(
|
||||
transport: &GatewayProviderTransportSnapshot,
|
||||
) -> Option<(String, String)> {
|
||||
if is_grok_provider_transport(transport) {
|
||||
return resolve_grok_session_auth(transport);
|
||||
}
|
||||
resolve_local_openai_bearer_auth(transport)
|
||||
}
|
||||
|
||||
@@ -159,6 +172,45 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grok_is_supported_by_dedicated_openai_image_transport_policy() {
|
||||
let mut transport = sample_transport();
|
||||
transport.provider.provider_type = "grok".to_string();
|
||||
|
||||
assert_eq!(
|
||||
openai_image_transport_unsupported_reason(&transport, "openai:image"),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grok_oauth_session_is_supported_by_dedicated_openai_image_transport_policy() {
|
||||
let mut transport = sample_transport();
|
||||
transport.provider.provider_type = "grok".to_string();
|
||||
transport.key.auth_type = "oauth".to_string();
|
||||
transport.key.decrypted_api_key = String::new();
|
||||
transport.key.decrypted_auth_config = Some(json!({"sso_token":"abc"}).to_string());
|
||||
|
||||
assert_eq!(
|
||||
openai_image_transport_unsupported_reason(&transport, "openai:image"),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chatgpt_web_oauth_is_supported_by_dedicated_openai_image_transport_policy() {
|
||||
let mut transport = sample_transport();
|
||||
transport.provider.provider_type = "chatgpt_web".to_string();
|
||||
transport.key.auth_type = "oauth".to_string();
|
||||
transport.key.decrypted_api_key = String::new();
|
||||
transport.key.decrypted_auth_config = Some(json!({"access_token":"token"}).to_string());
|
||||
|
||||
assert_eq!(
|
||||
openai_image_transport_unsupported_reason(&transport, "openai:image"),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_json_eventstream_headers_and_applies_rules() {
|
||||
let headers = build_openai_image_headers(ProviderOpenAiImageHeadersInput {
|
||||
|
||||
@@ -238,6 +238,15 @@ const ANTIGRAVITY_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy
|
||||
supports_local_same_format_transport: false,
|
||||
..STANDARD_RUNTIME_POLICY
|
||||
};
|
||||
const GROK_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
|
||||
fixed_provider: true,
|
||||
api_format_inheritance: ProviderApiFormatInheritance::OAuth,
|
||||
enable_format_conversion_by_default: true,
|
||||
supports_model_fetch: false,
|
||||
supports_local_openai_chat_transport: false,
|
||||
supports_local_same_format_transport: false,
|
||||
..STANDARD_RUNTIME_POLICY
|
||||
};
|
||||
|
||||
const CLAUDE_CODE_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTemplate {
|
||||
provider_type: "claude_code",
|
||||
@@ -352,6 +361,39 @@ const ANTIGRAVITY_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProvider
|
||||
runtime_policy: ANTIGRAVITY_RUNTIME_POLICY,
|
||||
};
|
||||
|
||||
const GROK_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTemplate {
|
||||
provider_type: "grok",
|
||||
version: 1,
|
||||
base_url: "https://grok.com",
|
||||
endpoints: &[
|
||||
FixedProviderEndpointTemplate {
|
||||
item_key: "openai:chat",
|
||||
api_format: "openai:chat",
|
||||
custom_path: None,
|
||||
config_defaults: EMPTY_ENDPOINT_CONFIG_DEFAULTS,
|
||||
},
|
||||
FixedProviderEndpointTemplate {
|
||||
item_key: "openai:responses",
|
||||
api_format: "openai:responses",
|
||||
custom_path: None,
|
||||
config_defaults: EMPTY_ENDPOINT_CONFIG_DEFAULTS,
|
||||
},
|
||||
FixedProviderEndpointTemplate {
|
||||
item_key: "claude:messages",
|
||||
api_format: "claude:messages",
|
||||
custom_path: None,
|
||||
config_defaults: EMPTY_ENDPOINT_CONFIG_DEFAULTS,
|
||||
},
|
||||
FixedProviderEndpointTemplate {
|
||||
item_key: "openai:image",
|
||||
api_format: "openai:image",
|
||||
custom_path: None,
|
||||
config_defaults: EMPTY_ENDPOINT_CONFIG_DEFAULTS,
|
||||
},
|
||||
],
|
||||
runtime_policy: GROK_RUNTIME_POLICY,
|
||||
};
|
||||
|
||||
pub fn provider_type_is_fixed(provider_type: &str) -> bool {
|
||||
provider_runtime_policy(provider_type).fixed_provider
|
||||
}
|
||||
@@ -398,6 +440,7 @@ pub fn fixed_provider_template(provider_type: &str) -> Option<&'static FixedProv
|
||||
"codex" => Some(&CODEX_FIXED_PROVIDER_TEMPLATE),
|
||||
"chatgpt_web" => Some(&CHATGPT_WEB_FIXED_PROVIDER_TEMPLATE),
|
||||
"kiro" => Some(&KIRO_FIXED_PROVIDER_TEMPLATE),
|
||||
"grok" => Some(&GROK_FIXED_PROVIDER_TEMPLATE),
|
||||
"gemini_cli" => Some(&GEMINI_CLI_FIXED_PROVIDER_TEMPLATE),
|
||||
"vertex_ai" => Some(&VERTEX_AI_FIXED_PROVIDER_TEMPLATE),
|
||||
"antigravity" => Some(&ANTIGRAVITY_FIXED_PROVIDER_TEMPLATE),
|
||||
@@ -598,6 +641,29 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grok_fixed_provider_template_exposes_chat_responses_messages_and_image() {
|
||||
let template = fixed_provider_template("grok").expect("grok template should exist");
|
||||
assert_eq!(template.base_url, "https://grok.com");
|
||||
assert_eq!(template.version, 1);
|
||||
assert_eq!(
|
||||
template
|
||||
.endpoints
|
||||
.iter()
|
||||
.map(|item| item.api_format)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
"openai:chat",
|
||||
"openai:responses",
|
||||
"claude:messages",
|
||||
"openai:image"
|
||||
]
|
||||
);
|
||||
assert!(!template.runtime_policy.supports_model_fetch);
|
||||
assert!(!template.runtime_policy.supports_local_openai_chat_transport);
|
||||
assert!(!template.runtime_policy.supports_local_same_format_transport);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixed_provider_key_inheritance_keeps_oauth_and_kiro_configured_bearer_keys_open() {
|
||||
assert!(fixed_provider_key_inherits_api_formats(
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::auth::{
|
||||
};
|
||||
use crate::claude_code::build_claude_code_passthrough_headers;
|
||||
use crate::claude_code::local_claude_code_transport_unsupported_reason_with_network;
|
||||
use crate::grok::{is_grok_provider_transport, resolve_grok_session_auth};
|
||||
use crate::kiro::{
|
||||
build_kiro_provider_headers, build_kiro_provider_request_body, is_kiro_provider_transport,
|
||||
local_kiro_request_transport_unsupported_reason_with_network, KiroAuthConfig,
|
||||
@@ -351,6 +352,10 @@ pub fn same_format_provider_transport_unsupported_reason(
|
||||
family: SameFormatProviderFamily,
|
||||
api_format: &str,
|
||||
) -> Option<&'static str> {
|
||||
if is_grok_provider_transport(transport) && matches!(family, SameFormatProviderFamily::Standard)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
if behavior.is_kiro {
|
||||
local_kiro_request_transport_unsupported_reason_with_network(transport)
|
||||
} else if behavior.is_antigravity {
|
||||
@@ -434,6 +439,10 @@ pub fn resolve_same_format_provider_direct_auth(
|
||||
transport: &GatewayProviderTransportSnapshot,
|
||||
family: SameFormatProviderFamily,
|
||||
) -> Option<(String, String)> {
|
||||
if is_grok_provider_transport(transport) && matches!(family, SameFormatProviderFamily::Standard)
|
||||
{
|
||||
return resolve_grok_session_auth(transport);
|
||||
}
|
||||
if behavior.is_vertex {
|
||||
None
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user