mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 16:37:46 +08:00
Merge branch 'fawney19:main' into main
This commit is contained in:
@@ -719,6 +719,8 @@ mod tests {
|
||||
provider_request_body: Some(json!({"model":"gpt-5","metadata":{}})),
|
||||
provider_request_body_base64: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
content_encoding: None,
|
||||
request_gzip: None,
|
||||
proxy: None,
|
||||
transport_profile: None,
|
||||
timeouts: None,
|
||||
|
||||
@@ -20,6 +20,7 @@ mod pool_scheduler;
|
||||
pub(crate) mod pool_scores;
|
||||
mod redaction;
|
||||
mod report_context;
|
||||
mod request_gzip;
|
||||
mod route;
|
||||
mod runtime_miss;
|
||||
mod spec_metadata;
|
||||
@@ -46,6 +47,7 @@ pub(crate) use self::plan_builders::{
|
||||
pub(crate) use self::pool_scores::{
|
||||
build_provider_key_pool_score_upsert, provider_key_pool_score_id, provider_key_pool_score_scope,
|
||||
};
|
||||
pub(crate) use self::request_gzip::resolve_transport_request_gzip_policy;
|
||||
pub(crate) use self::route::is_matching_stream_request as planner_is_matching_stream_request;
|
||||
pub(crate) use self::runtime_miss::{
|
||||
apply_local_runtime_candidate_terminal_reason, record_local_runtime_candidate_skip_reason,
|
||||
|
||||
@@ -17,7 +17,8 @@ use crate::ai_serving::planner::report_context::{
|
||||
use crate::ai_serving::planner::spec_metadata::local_same_format_provider_spec_metadata;
|
||||
use crate::ai_serving::planner::CandidateFailureDiagnostic;
|
||||
use crate::ai_serving::planner::{
|
||||
build_ai_execution_decision_response, AiExecutionDecisionResponseParts,
|
||||
build_ai_execution_decision_response, resolve_transport_request_gzip_policy,
|
||||
AiExecutionDecisionResponseParts,
|
||||
};
|
||||
use crate::ai_serving::transport::{
|
||||
resolve_transport_execution_timeouts, resolve_transport_profile,
|
||||
@@ -107,6 +108,11 @@ pub(crate) async fn maybe_build_local_same_format_provider_decision_payload_for_
|
||||
json!(crate::ai_serving::transport::GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME),
|
||||
);
|
||||
}
|
||||
if !resolved.compatibility_edits.is_empty() {
|
||||
if let Ok(value) = serde_json::to_value(&resolved.compatibility_edits) {
|
||||
extra_fields.insert("request_body_compatibility_edits".to_string(), value);
|
||||
}
|
||||
}
|
||||
let provider_api_format = resolved.provider_api_format.clone();
|
||||
let effective_headers = input.effective_headers(&parts.headers);
|
||||
let report_context = append_local_failover_policy_to_value(
|
||||
@@ -175,8 +181,10 @@ pub(crate) async fn maybe_build_local_same_format_provider_decision_payload_for_
|
||||
provider_request_headers,
|
||||
provider_request_body,
|
||||
transport_profile: _,
|
||||
compatibility_edits: _,
|
||||
request_redacted: _,
|
||||
} = resolved;
|
||||
let request_gzip = resolve_transport_request_gzip_policy(&transport);
|
||||
|
||||
let mut decision = build_ai_execution_decision_response(AiExecutionDecisionResponseParts {
|
||||
decision_is_stream: spec_metadata.require_streaming,
|
||||
@@ -203,6 +211,8 @@ pub(crate) async fn maybe_build_local_same_format_provider_decision_payload_for_
|
||||
provider_request_body: Some(provider_request_body),
|
||||
provider_request_body_base64: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
content_encoding: None,
|
||||
request_gzip,
|
||||
proxy,
|
||||
transport_profile,
|
||||
timeouts: resolve_transport_execution_timeouts(&transport),
|
||||
|
||||
@@ -19,7 +19,9 @@ use crate::ai_serving::transport::{
|
||||
build_gemini_cli_v1internal_request, build_grok_browser_headers, build_grok_upstream_url,
|
||||
build_same_format_provider_headers, resolve_local_gemini_cli_request_auth,
|
||||
GeminiCliRequestAuth, GeminiCliRequestAuthSupport, GeminiCliRequestEnvelopeSupport,
|
||||
GrokHeaderInput, SameFormatProviderHeadersInput, GEMINI_CLI_USER_AGENT, GROK_CHAT_PATH,
|
||||
GrokHeaderInput, SameFormatProviderCompatibilityEdit,
|
||||
SameFormatProviderCompatibilityEditAction, SameFormatProviderHeadersInput,
|
||||
GEMINI_CLI_USER_AGENT, GROK_CHAT_PATH,
|
||||
};
|
||||
use crate::ai_serving::{CandidateFailureDiagnostic, GatewayProviderTransportSnapshot};
|
||||
use crate::{AppState, GatewayError};
|
||||
@@ -107,6 +109,7 @@ pub(crate) struct LocalSameFormatProviderCandidatePayloadParts {
|
||||
pub(super) provider_request_headers: BTreeMap<String, String>,
|
||||
pub(super) provider_request_body: Value,
|
||||
pub(super) transport_profile: Option<ResolvedTransportProfile>,
|
||||
pub(super) compatibility_edits: Vec<SameFormatProviderCompatibilityEdit>,
|
||||
pub(super) request_redacted: bool,
|
||||
}
|
||||
|
||||
@@ -153,8 +156,8 @@ pub(crate) async fn resolve_local_same_format_provider_candidate_payload_parts(
|
||||
let body_json = redaction.body_json.as_ref();
|
||||
let mut transport = Arc::clone(&prepared.transport);
|
||||
|
||||
let Some(mut base_provider_request_body) =
|
||||
super::super::request::build_same_format_provider_request_body(
|
||||
let Some(base_provider_request) =
|
||||
super::super::request::build_same_format_provider_request_body_with_compatibility_report(
|
||||
body_json,
|
||||
prepared.provider_api_format.as_str(),
|
||||
&prepared.mapped_model,
|
||||
@@ -190,6 +193,8 @@ pub(crate) async fn resolve_local_same_format_provider_candidate_payload_parts(
|
||||
.await;
|
||||
return Ok(None);
|
||||
};
|
||||
let mut base_provider_request_body = base_provider_request.body;
|
||||
let mut compatibility_edits = base_provider_request.compatibility_edits;
|
||||
if let Some(mapping) =
|
||||
crate::system_features::reasoning_model_directive_mapping_for_api_format_and_model(
|
||||
state,
|
||||
@@ -198,10 +203,18 @@ pub(crate) async fn resolve_local_same_format_provider_candidate_payload_parts(
|
||||
)
|
||||
.await
|
||||
{
|
||||
let before_mapping = base_provider_request_body.clone();
|
||||
crate::ai_serving::apply_model_directive_mapping_patch(
|
||||
&mut base_provider_request_body,
|
||||
&mapping,
|
||||
);
|
||||
if before_mapping != base_provider_request_body {
|
||||
compatibility_edits.push(SameFormatProviderCompatibilityEdit {
|
||||
field: "model_directive_mapping".to_string(),
|
||||
action: SameFormatProviderCompatibilityEditAction::RuntimeRewrite,
|
||||
detail: "applied configured model directive mapping patch".to_string(),
|
||||
});
|
||||
}
|
||||
// Directive mapping is a deep-merge patch and may overwrite/add `stream`;
|
||||
// re-enforce stream-field policy afterward.
|
||||
// Kiro behavior classification already hard-requires upstream streaming,
|
||||
@@ -452,6 +465,7 @@ pub(crate) async fn resolve_local_same_format_provider_candidate_payload_parts(
|
||||
provider_request_headers,
|
||||
provider_request_body,
|
||||
transport_profile,
|
||||
compatibility_edits,
|
||||
request_redacted: redaction.redacted,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -2,4 +2,5 @@ mod body;
|
||||
mod url;
|
||||
|
||||
pub(super) use self::body::build_same_format_provider_request_body;
|
||||
pub(super) use self::body::build_same_format_provider_request_body_with_compatibility_report;
|
||||
pub(super) use self::url::build_same_format_upstream_url;
|
||||
|
||||
@@ -3,7 +3,9 @@ use serde_json::Value;
|
||||
use super::super::LocalSameFormatProviderSpec;
|
||||
use crate::ai_serving::transport::{
|
||||
build_same_format_provider_request_body as build_same_format_provider_request_body_impl,
|
||||
build_same_format_provider_request_body_with_compatibility_report as build_same_format_provider_request_body_with_compatibility_report_impl,
|
||||
SameFormatProviderFamily, SameFormatProviderRequestBodyInput,
|
||||
SameFormatProviderRequestBodyOutput,
|
||||
};
|
||||
|
||||
pub(crate) fn build_same_format_provider_request_body(
|
||||
@@ -36,6 +38,38 @@ pub(crate) fn build_same_format_provider_request_body(
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn build_same_format_provider_request_body_with_compatibility_report(
|
||||
body_json: &Value,
|
||||
provider_api_format: &str,
|
||||
mapped_model: &str,
|
||||
spec: LocalSameFormatProviderSpec,
|
||||
body_rules: Option<&Value>,
|
||||
request_headers: Option<&http::HeaderMap>,
|
||||
upstream_is_stream: bool,
|
||||
force_body_stream_field: bool,
|
||||
kiro_auth: Option<&crate::ai_serving::transport::kiro::KiroRequestAuth>,
|
||||
is_claude_code: bool,
|
||||
enable_model_directives: bool,
|
||||
) -> Option<SameFormatProviderRequestBodyOutput> {
|
||||
build_same_format_provider_request_body_with_compatibility_report_impl(
|
||||
SameFormatProviderRequestBodyInput {
|
||||
body_json,
|
||||
mapped_model,
|
||||
client_api_format: spec.api_format,
|
||||
provider_api_format,
|
||||
source_model: body_json.get("model").and_then(Value::as_str),
|
||||
family: same_format_provider_family(spec.family),
|
||||
body_rules,
|
||||
request_headers,
|
||||
upstream_is_stream,
|
||||
force_body_stream_field,
|
||||
kiro_auth_config: kiro_auth.map(|auth| &auth.auth_config),
|
||||
is_claude_code,
|
||||
enable_model_directives,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn same_format_provider_family(
|
||||
family: super::super::LocalSameFormatProviderFamily,
|
||||
) -> SameFormatProviderFamily {
|
||||
|
||||
@@ -29,7 +29,6 @@ impl<'a> ProviderRequestRedaction<'a> {
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
struct ChatPiiRedactionFeatureSettings {
|
||||
enabled: Option<bool>,
|
||||
inject_model_instruction: Option<bool>,
|
||||
}
|
||||
|
||||
impl ChatPiiRedactionFeatureSettings {
|
||||
@@ -44,21 +43,11 @@ impl ChatPiiRedactionFeatureSettings {
|
||||
if let Some(enabled) = settings.get("enabled").and_then(Value::as_bool) {
|
||||
self.enabled = Some(enabled);
|
||||
}
|
||||
if let Some(inject_model_instruction) = settings
|
||||
.get("inject_model_instruction")
|
||||
.and_then(Value::as_bool)
|
||||
{
|
||||
self.inject_model_instruction = Some(inject_model_instruction);
|
||||
}
|
||||
}
|
||||
|
||||
fn effective_enabled(self) -> bool {
|
||||
self.enabled.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn effective_inject_model_instruction(self) -> bool {
|
||||
self.inject_model_instruction.unwrap_or(true)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn request_identity_response_encoding_when_redacted(
|
||||
@@ -122,7 +111,7 @@ pub(crate) async fn resolve_provider_chat_pii_redaction<'a>(
|
||||
&body_bytes,
|
||||
format,
|
||||
build_redaction_session_config(hmac_key, &runtime_config, now_unix_secs),
|
||||
MaskChatRequestOptions::runtime(feature_settings.effective_inject_model_instruction()),
|
||||
MaskChatRequestOptions::runtime(),
|
||||
Some(&cache),
|
||||
)
|
||||
.await
|
||||
@@ -190,3 +179,22 @@ fn redaction_mask_error_to_gateway_error(error: RedactionMaskError) -> GatewayEr
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
use super::ChatPiiRedactionFeatureSettings;
|
||||
|
||||
#[test]
|
||||
fn chat_pii_redaction_feature_settings_only_control_enablement() {
|
||||
let mut settings = ChatPiiRedactionFeatureSettings::default();
|
||||
settings.merge_from_value(Some(&json!({
|
||||
"chat_pii_redaction": {
|
||||
"enabled": true
|
||||
}
|
||||
})));
|
||||
|
||||
assert!(settings.effective_enabled());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,326 @@
|
||||
use aether_ai_serving::AiRequestGzipPolicy;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::ai_serving::is_openai_responses_family_format;
|
||||
|
||||
use super::state::GatewayProviderTransportSnapshot;
|
||||
|
||||
const DEFAULT_CODEX_REQUEST_GZIP_MIN_BYTES: usize = 64 * 1024;
|
||||
|
||||
pub(crate) fn resolve_transport_request_gzip_policy(
|
||||
transport: &GatewayProviderTransportSnapshot,
|
||||
) -> Option<AiRequestGzipPolicy> {
|
||||
transport_request_gzip_policy_from_config(transport.endpoint.config.as_ref())
|
||||
.or_else(|| transport_request_gzip_policy_from_config(transport.provider.config.as_ref()))
|
||||
.or_else(|| default_transport_request_gzip_policy(transport))
|
||||
}
|
||||
|
||||
fn default_transport_request_gzip_policy(
|
||||
transport: &GatewayProviderTransportSnapshot,
|
||||
) -> Option<AiRequestGzipPolicy> {
|
||||
if !transport
|
||||
.provider
|
||||
.provider_type
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("codex")
|
||||
{
|
||||
return None;
|
||||
}
|
||||
if !is_codex_request_gzip_endpoint_api_format(transport.endpoint.api_format.as_str()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(AiRequestGzipPolicy {
|
||||
enabled: Some(true),
|
||||
min_bytes: Some(DEFAULT_CODEX_REQUEST_GZIP_MIN_BYTES),
|
||||
})
|
||||
}
|
||||
|
||||
fn is_codex_request_gzip_endpoint_api_format(api_format: &str) -> bool {
|
||||
is_openai_responses_family_format(api_format)
|
||||
|| api_format.trim().eq_ignore_ascii_case("openai:image")
|
||||
}
|
||||
|
||||
fn transport_request_gzip_policy_from_config(
|
||||
config: Option<&Value>,
|
||||
) -> Option<AiRequestGzipPolicy> {
|
||||
let object = config?.as_object()?;
|
||||
|
||||
for key in ["request_gzip", "request_body_gzip"] {
|
||||
if let Some(policy) = object
|
||||
.get(key)
|
||||
.and_then(transport_request_gzip_policy_from_value)
|
||||
{
|
||||
return Some(policy);
|
||||
}
|
||||
}
|
||||
|
||||
let enabled = first_config_bool(
|
||||
object,
|
||||
&["request_gzip_enabled", "request_body_gzip_enabled"],
|
||||
);
|
||||
let min_bytes = first_config_usize(
|
||||
object,
|
||||
&["request_gzip_min_bytes", "request_body_gzip_min_bytes"],
|
||||
);
|
||||
|
||||
match (enabled, min_bytes) {
|
||||
(Some(false), _) => Some(AiRequestGzipPolicy {
|
||||
enabled: Some(false),
|
||||
min_bytes: None,
|
||||
}),
|
||||
(Some(true), min_bytes) => Some(AiRequestGzipPolicy {
|
||||
enabled: Some(true),
|
||||
min_bytes,
|
||||
}),
|
||||
(None, Some(min_bytes)) => Some(AiRequestGzipPolicy {
|
||||
enabled: Some(true),
|
||||
min_bytes: Some(min_bytes),
|
||||
}),
|
||||
(None, None) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn transport_request_gzip_policy_from_value(value: &Value) -> Option<AiRequestGzipPolicy> {
|
||||
if let Some(enabled) = value.as_bool() {
|
||||
return Some(AiRequestGzipPolicy {
|
||||
enabled: Some(enabled),
|
||||
min_bytes: None,
|
||||
});
|
||||
}
|
||||
|
||||
let object = value.as_object()?;
|
||||
let enabled = first_config_bool(object, &["enabled"]);
|
||||
let min_bytes = first_config_usize(object, &["min_bytes"]);
|
||||
|
||||
match (enabled, min_bytes) {
|
||||
(Some(false), _) => Some(AiRequestGzipPolicy {
|
||||
enabled: Some(false),
|
||||
min_bytes: None,
|
||||
}),
|
||||
(Some(true), min_bytes) => Some(AiRequestGzipPolicy {
|
||||
enabled: Some(true),
|
||||
min_bytes,
|
||||
}),
|
||||
(None, Some(min_bytes)) => Some(AiRequestGzipPolicy {
|
||||
enabled: Some(true),
|
||||
min_bytes: Some(min_bytes),
|
||||
}),
|
||||
(None, None) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn first_config_bool(object: &serde_json::Map<String, Value>, keys: &[&str]) -> Option<bool> {
|
||||
keys.iter()
|
||||
.find_map(|key| object.get(*key).and_then(config_bool))
|
||||
}
|
||||
|
||||
fn config_bool(value: &Value) -> Option<bool> {
|
||||
value.as_bool().or_else(|| {
|
||||
value.as_str().and_then(|text| {
|
||||
let normalized = text.trim();
|
||||
if normalized.eq_ignore_ascii_case("true") {
|
||||
Some(true)
|
||||
} else if normalized.eq_ignore_ascii_case("false") {
|
||||
Some(false)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn first_config_usize(object: &serde_json::Map<String, Value>, keys: &[&str]) -> Option<usize> {
|
||||
keys.iter()
|
||||
.find_map(|key| object.get(*key).and_then(config_usize))
|
||||
}
|
||||
|
||||
fn config_usize(value: &Value) -> Option<usize> {
|
||||
value
|
||||
.as_u64()
|
||||
.and_then(|number| usize::try_from(number).ok())
|
||||
.or_else(|| {
|
||||
value
|
||||
.as_str()
|
||||
.and_then(|text| text.trim().parse::<usize>().ok())
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use aether_provider_transport::snapshot::{
|
||||
GatewayProviderTransportEndpoint, GatewayProviderTransportKey,
|
||||
GatewayProviderTransportProvider, GatewayProviderTransportSnapshot,
|
||||
};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
fn sample_transport(
|
||||
provider_type: &str,
|
||||
endpoint_api_format: &str,
|
||||
provider_config: Option<Value>,
|
||||
endpoint_config: Option<Value>,
|
||||
) -> GatewayProviderTransportSnapshot {
|
||||
GatewayProviderTransportSnapshot {
|
||||
provider: GatewayProviderTransportProvider {
|
||||
id: "provider-1".to_string(),
|
||||
name: "Provider".to_string(),
|
||||
provider_type: provider_type.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: provider_config,
|
||||
},
|
||||
endpoint: GatewayProviderTransportEndpoint {
|
||||
id: "endpoint-1".to_string(),
|
||||
provider_id: "provider-1".to_string(),
|
||||
api_format: endpoint_api_format.to_string(),
|
||||
api_family: None,
|
||||
endpoint_kind: None,
|
||||
is_active: true,
|
||||
base_url: "https://api.example.test".to_string(),
|
||||
header_rules: None,
|
||||
body_rules: None,
|
||||
max_retries: None,
|
||||
custom_path: None,
|
||||
config: endpoint_config,
|
||||
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: "api_key".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,
|
||||
upstream_metadata: None,
|
||||
decrypted_api_key: "secret".to_string(),
|
||||
decrypted_auth_config: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn endpoint_request_gzip_policy_overrides_provider_policy() {
|
||||
let transport = sample_transport(
|
||||
"openai",
|
||||
"openai:responses",
|
||||
Some(json!({"request_gzip": false})),
|
||||
Some(json!({"request_gzip": {"enabled": true, "min_bytes": 1024}})),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_transport_request_gzip_policy(&transport),
|
||||
Some(AiRequestGzipPolicy {
|
||||
enabled: Some(true),
|
||||
min_bytes: Some(1024),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn endpoint_request_gzip_false_disables_provider_and_codex_defaults() {
|
||||
let transport = sample_transport(
|
||||
"codex",
|
||||
"openai:responses",
|
||||
Some(json!({"request_gzip": {"enabled": true, "min_bytes": 1024}})),
|
||||
Some(json!({"request_gzip": false})),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_transport_request_gzip_policy(&transport),
|
||||
Some(AiRequestGzipPolicy {
|
||||
enabled: Some(false),
|
||||
min_bytes: None,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_gzip_policy_supports_top_level_aliases() {
|
||||
let transport = sample_transport(
|
||||
"openai",
|
||||
"openai:responses",
|
||||
None,
|
||||
Some(json!({
|
||||
"request_body_gzip_enabled": true,
|
||||
"request_body_gzip_min_bytes": "4096"
|
||||
})),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_transport_request_gzip_policy(&transport),
|
||||
Some(AiRequestGzipPolicy {
|
||||
enabled: Some(true),
|
||||
min_bytes: Some(4096),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_gzip_policy_treats_min_bytes_only_as_enabled() {
|
||||
let transport = sample_transport(
|
||||
"openai",
|
||||
"openai:responses",
|
||||
None,
|
||||
Some(json!({"request_gzip_min_bytes": 1})),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolve_transport_request_gzip_policy(&transport),
|
||||
Some(AiRequestGzipPolicy {
|
||||
enabled: Some(true),
|
||||
min_bytes: Some(1),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_responses_endpoint_gets_default_request_gzip_policy() {
|
||||
let transport = sample_transport("codex", "openai:responses", None, None);
|
||||
|
||||
assert_eq!(
|
||||
resolve_transport_request_gzip_policy(&transport),
|
||||
Some(AiRequestGzipPolicy {
|
||||
enabled: Some(true),
|
||||
min_bytes: Some(DEFAULT_CODEX_REQUEST_GZIP_MIN_BYTES),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_image_endpoint_gets_default_request_gzip_policy() {
|
||||
let transport = sample_transport("codex", "openai:image", None, None);
|
||||
|
||||
assert_eq!(
|
||||
resolve_transport_request_gzip_policy(&transport),
|
||||
Some(AiRequestGzipPolicy {
|
||||
enabled: Some(true),
|
||||
min_bytes: Some(DEFAULT_CODEX_REQUEST_GZIP_MIN_BYTES),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_codex_endpoint_does_not_get_default_request_gzip_policy() {
|
||||
let transport = sample_transport("openai", "openai:responses", None, None);
|
||||
|
||||
assert_eq!(resolve_transport_request_gzip_policy(&transport), None);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,8 @@ use crate::ai_serving::planner::report_context::{
|
||||
};
|
||||
use crate::ai_serving::planner::spec_metadata::local_gemini_files_spec_metadata;
|
||||
use crate::ai_serving::planner::{
|
||||
build_ai_execution_decision_response, AiExecutionDecisionResponseParts,
|
||||
build_ai_execution_decision_response, resolve_transport_request_gzip_policy,
|
||||
AiExecutionDecisionResponseParts,
|
||||
};
|
||||
use crate::ai_serving::transport::{
|
||||
resolve_transport_execution_timeouts, resolve_transport_profile,
|
||||
@@ -123,6 +124,7 @@ pub(super) async fn maybe_build_local_gemini_files_decision_payload_for_candidat
|
||||
upstream_url,
|
||||
file_name: _,
|
||||
} = resolved;
|
||||
let request_gzip = resolve_transport_request_gzip_policy(&transport);
|
||||
|
||||
let mut decision = build_ai_execution_decision_response(AiExecutionDecisionResponseParts {
|
||||
decision_is_stream: spec_metadata.require_streaming,
|
||||
@@ -154,6 +156,8 @@ pub(super) async fn maybe_build_local_gemini_files_decision_payload_for_candidat
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(ToOwned::to_owned),
|
||||
content_encoding: None,
|
||||
request_gzip,
|
||||
proxy,
|
||||
transport_profile,
|
||||
timeouts: resolve_transport_execution_timeouts(&transport),
|
||||
|
||||
@@ -5,7 +5,8 @@ use crate::ai_serving::planner::report_context::{
|
||||
};
|
||||
use crate::ai_serving::planner::spec_metadata::local_openai_image_spec_metadata;
|
||||
use crate::ai_serving::planner::{
|
||||
build_ai_execution_decision_response, AiExecutionDecisionResponseParts,
|
||||
build_ai_execution_decision_response, resolve_transport_request_gzip_policy,
|
||||
AiExecutionDecisionResponseParts,
|
||||
};
|
||||
use crate::ai_serving::transport::{
|
||||
resolve_transport_execution_timeouts, resolve_transport_profile,
|
||||
@@ -82,15 +83,6 @@ pub(super) async fn maybe_build_local_openai_image_decision_payload_for_candidat
|
||||
"chatgpt_web_image".to_string(),
|
||||
serde_json::Value::Bool(true),
|
||||
);
|
||||
extra_fields.insert(
|
||||
"local_failover_policy".to_string(),
|
||||
serde_json::json!({
|
||||
"stop_status_codes": [400, 401, 403, 429, 500, 502, 503, 504],
|
||||
"error_stop_patterns": [
|
||||
{ "pattern": ".*" }
|
||||
]
|
||||
}),
|
||||
);
|
||||
}
|
||||
let upstream_is_stream = resolved
|
||||
.provider_request_body
|
||||
@@ -143,6 +135,7 @@ pub(super) async fn maybe_build_local_openai_image_decision_payload_for_candidat
|
||||
spec_metadata.api_format,
|
||||
provider_api_format.as_str(),
|
||||
);
|
||||
let request_gzip = resolve_transport_request_gzip_policy(&transport);
|
||||
|
||||
let mut decision = build_ai_execution_decision_response(AiExecutionDecisionResponseParts {
|
||||
decision_is_stream: spec_metadata.require_streaming,
|
||||
@@ -169,6 +162,8 @@ pub(super) async fn maybe_build_local_openai_image_decision_payload_for_candidat
|
||||
provider_request_body: Some(resolved.provider_request_body),
|
||||
provider_request_body_base64: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
content_encoding: None,
|
||||
request_gzip,
|
||||
proxy,
|
||||
transport_profile,
|
||||
timeouts: resolve_transport_execution_timeouts(&transport),
|
||||
|
||||
@@ -5,7 +5,8 @@ use crate::ai_serving::planner::report_context::{
|
||||
};
|
||||
use crate::ai_serving::planner::spec_metadata::local_video_create_spec_metadata;
|
||||
use crate::ai_serving::planner::{
|
||||
build_ai_execution_decision_response, AiExecutionDecisionResponseParts,
|
||||
build_ai_execution_decision_response, resolve_transport_request_gzip_policy,
|
||||
AiExecutionDecisionResponseParts,
|
||||
};
|
||||
use crate::ai_serving::transport::{
|
||||
resolve_transport_execution_timeouts, resolve_transport_profile,
|
||||
@@ -103,6 +104,7 @@ pub(super) async fn maybe_build_local_video_create_decision_payload_for_candidat
|
||||
provider_request_body,
|
||||
upstream_url,
|
||||
} = resolved;
|
||||
let request_gzip = resolve_transport_request_gzip_policy(&transport);
|
||||
|
||||
let mut decision = build_ai_execution_decision_response(AiExecutionDecisionResponseParts {
|
||||
decision_is_stream: false,
|
||||
@@ -135,6 +137,8 @@ pub(super) async fn maybe_build_local_video_create_decision_payload_for_candidat
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(ToOwned::to_owned),
|
||||
content_encoding: None,
|
||||
request_gzip,
|
||||
proxy,
|
||||
transport_profile,
|
||||
timeouts: resolve_transport_execution_timeouts(&transport),
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::collections::BTreeMap;
|
||||
use super::{
|
||||
apply_codex_openai_responses_special_body_edits, apply_codex_openai_responses_special_headers,
|
||||
};
|
||||
use crate::ai_serving::planner::standard::build_local_openai_responses_request_body;
|
||||
use http::{HeaderMap, HeaderValue};
|
||||
use serde_json::json;
|
||||
|
||||
@@ -36,6 +37,40 @@ fn applies_codex_defaults_when_body_rules_do_not_handle_fields() {
|
||||
assert!(body.get("reasoning").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_openai_responses_codex_body_wraps_string_input_for_backend() {
|
||||
let body = json!({
|
||||
"model": "gpt-5",
|
||||
"input": "hello"
|
||||
});
|
||||
|
||||
let provider_request_body = build_local_openai_responses_request_body(
|
||||
&body,
|
||||
"gpt-5-upstream",
|
||||
false,
|
||||
false,
|
||||
"codex",
|
||||
"openai:responses",
|
||||
None,
|
||||
Some("key-123"),
|
||||
&HeaderMap::new(),
|
||||
false,
|
||||
)
|
||||
.expect("codex local openai responses body should build");
|
||||
|
||||
assert_eq!(
|
||||
provider_request_body["input"],
|
||||
json!([{
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [{
|
||||
"type": "input_text",
|
||||
"text": "hello"
|
||||
}]
|
||||
}])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strips_store_for_compact_even_when_body_rules_handle_it() {
|
||||
let body_rules = json!([
|
||||
|
||||
@@ -15,7 +15,8 @@ use crate::ai_serving::planner::report_context::{
|
||||
use crate::ai_serving::planner::spec_metadata::local_standard_spec_metadata;
|
||||
use crate::ai_serving::planner::CandidateFailureDiagnostic;
|
||||
use crate::ai_serving::planner::{
|
||||
build_ai_execution_decision_response, AiExecutionDecisionResponseParts,
|
||||
build_ai_execution_decision_response, resolve_transport_request_gzip_policy,
|
||||
AiExecutionDecisionResponseParts,
|
||||
};
|
||||
use crate::ai_serving::transport::{
|
||||
resolve_transport_execution_timeouts, resolve_transport_profile,
|
||||
@@ -175,6 +176,7 @@ pub(super) async fn maybe_build_local_standard_decision_payload_for_candidate(
|
||||
transport_profile: _,
|
||||
request_redacted: _,
|
||||
} = resolved;
|
||||
let request_gzip = resolve_transport_request_gzip_policy(&transport);
|
||||
|
||||
let mut decision = build_ai_execution_decision_response(AiExecutionDecisionResponseParts {
|
||||
decision_is_stream: spec_metadata.require_streaming,
|
||||
@@ -201,6 +203,8 @@ pub(super) async fn maybe_build_local_standard_decision_payload_for_candidate(
|
||||
provider_request_body: Some(provider_request_body),
|
||||
provider_request_body_base64: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
content_encoding: None,
|
||||
request_gzip,
|
||||
proxy,
|
||||
transport_profile,
|
||||
timeouts,
|
||||
|
||||
@@ -23,6 +23,7 @@ use crate::ai_serving::planner::spec_metadata::local_standard_spec_metadata;
|
||||
use crate::ai_serving::planner::standard::{
|
||||
apply_codex_openai_responses_special_headers, apply_deepseek_tool_call_thinking_compat,
|
||||
is_deepseek_provider, request_body_build_failure_extra_data,
|
||||
request_conversion_failure_extra_data,
|
||||
};
|
||||
use crate::ai_serving::transport::kiro::{
|
||||
build_kiro_provider_headers, build_kiro_provider_request_body,
|
||||
@@ -599,10 +600,14 @@ pub(crate) async fn resolve_local_standard_candidate_payload_parts(
|
||||
attempt.candidate_index,
|
||||
&attempt.candidate_id,
|
||||
"provider_request_body_build_failed",
|
||||
request_body_build_failure_extra_data(
|
||||
request_conversion_failure_extra_data(
|
||||
body_json,
|
||||
spec_metadata.api_format,
|
||||
provider_api_format,
|
||||
Some(prepared_candidate.mapped_model.as_str()),
|
||||
Some(parts.uri.path()),
|
||||
upstream_is_stream,
|
||||
"standard_family_request_conversion",
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -62,7 +62,8 @@ pub(crate) use crate::ai_serving::{
|
||||
normalize_openai_responses_request_to_openai_chat_request, parse_openai_tool_result_content,
|
||||
};
|
||||
pub(crate) use aether_ai_serving::{
|
||||
request_body_build_failure_extra_data, same_format_provider_request_body_failure_extra_data,
|
||||
request_body_build_failure_extra_data, request_conversion_failure_extra_data,
|
||||
same_format_provider_request_body_failure_extra_data,
|
||||
};
|
||||
|
||||
pub(crate) fn build_standard_upstream_url(
|
||||
|
||||
+5
-10
@@ -6,7 +6,8 @@ use crate::ai_serving::planner::report_context::{
|
||||
insert_provider_stream_event_api_format, LocalExecutionReportContextParts,
|
||||
};
|
||||
use crate::ai_serving::planner::{
|
||||
build_ai_execution_decision_response, AiExecutionDecisionResponseParts,
|
||||
build_ai_execution_decision_response, resolve_transport_request_gzip_policy,
|
||||
AiExecutionDecisionResponseParts,
|
||||
};
|
||||
use crate::ai_serving::transport::{
|
||||
resolve_transport_execution_timeouts, resolve_transport_profile,
|
||||
@@ -104,15 +105,6 @@ pub(crate) async fn maybe_build_local_openai_chat_decision_payload_for_candidate
|
||||
.eq_ignore_ascii_case("chatgpt_web")
|
||||
{
|
||||
extra_fields.insert("chatgpt_web_image".to_string(), serde_json::json!(true));
|
||||
extra_fields.insert(
|
||||
"local_failover_policy".to_string(),
|
||||
serde_json::json!({
|
||||
"stop_status_codes": [400, 401, 403, 429, 500, 502, 503, 504],
|
||||
"error_stop_patterns": [
|
||||
{ "pattern": ".*" }
|
||||
]
|
||||
}),
|
||||
);
|
||||
}
|
||||
let super::request::LocalOpenAiChatCandidatePayloadParts {
|
||||
client_api_format,
|
||||
@@ -192,6 +184,7 @@ pub(crate) async fn maybe_build_local_openai_chat_decision_payload_for_candidate
|
||||
),
|
||||
&transport,
|
||||
);
|
||||
let request_gzip = resolve_transport_request_gzip_policy(&transport);
|
||||
|
||||
let mut decision = build_ai_execution_decision_response(AiExecutionDecisionResponseParts {
|
||||
decision_is_stream,
|
||||
@@ -218,6 +211,8 @@ pub(crate) async fn maybe_build_local_openai_chat_decision_payload_for_candidate
|
||||
provider_request_body: Some(provider_request_body),
|
||||
provider_request_body_base64: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
content_encoding: None,
|
||||
request_gzip,
|
||||
proxy,
|
||||
transport_profile,
|
||||
timeouts,
|
||||
|
||||
@@ -25,6 +25,7 @@ use crate::ai_serving::planner::standard::{
|
||||
apply_deepseek_tool_call_thinking_compat, build_cross_format_openai_chat_request_body,
|
||||
build_cross_format_openai_chat_upstream_url, build_local_openai_chat_request_body,
|
||||
build_local_openai_chat_upstream_url, request_body_build_failure_extra_data,
|
||||
request_conversion_failure_extra_data,
|
||||
};
|
||||
use crate::ai_serving::transport::auth::resolve_local_openai_bearer_auth;
|
||||
use crate::ai_serving::transport::kiro::{
|
||||
@@ -601,10 +602,14 @@ pub(crate) async fn resolve_local_openai_chat_candidate_payload_parts(
|
||||
candidate_index,
|
||||
candidate_id,
|
||||
"provider_request_body_build_failed",
|
||||
request_body_build_failure_extra_data(
|
||||
request_conversion_failure_extra_data(
|
||||
body_json,
|
||||
"openai:chat",
|
||||
provider_api_format.as_str(),
|
||||
Some(prepared_candidate.mapped_model.as_str()),
|
||||
Some(parts.uri.path()),
|
||||
upstream_is_stream,
|
||||
"openai_chat_request_conversion",
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -326,6 +326,8 @@ mod tests {
|
||||
})),
|
||||
provider_request_body_base64: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
content_encoding: None,
|
||||
request_gzip: None,
|
||||
proxy: None,
|
||||
transport_profile: None,
|
||||
timeouts: None,
|
||||
@@ -422,6 +424,8 @@ mod tests {
|
||||
provider_request_body: Some(json!({"model":"gpt-5.4","messages":[],"stream":true})),
|
||||
provider_request_body_base64: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
content_encoding: None,
|
||||
request_gzip: None,
|
||||
proxy: None,
|
||||
transport_profile: None,
|
||||
timeouts: None,
|
||||
@@ -488,6 +492,8 @@ mod tests {
|
||||
provider_request_body,
|
||||
provider_request_body_base64: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
content_encoding: None,
|
||||
request_gzip: None,
|
||||
proxy: None,
|
||||
transport_profile: None,
|
||||
timeouts: None,
|
||||
@@ -595,6 +601,8 @@ mod tests {
|
||||
),
|
||||
provider_request_body_base64: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
content_encoding: None,
|
||||
request_gzip: None,
|
||||
proxy: None,
|
||||
transport_profile: None,
|
||||
timeouts: None,
|
||||
|
||||
@@ -292,6 +292,8 @@ mod tests {
|
||||
})),
|
||||
provider_request_body_base64: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
content_encoding: None,
|
||||
request_gzip: None,
|
||||
proxy: None,
|
||||
transport_profile: None,
|
||||
timeouts: None,
|
||||
@@ -387,6 +389,8 @@ mod tests {
|
||||
provider_request_body: Some(json!({"model":"gpt-5.4","messages":[],"stream":false})),
|
||||
provider_request_body_base64: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
content_encoding: None,
|
||||
request_gzip: None,
|
||||
proxy: None,
|
||||
transport_profile: None,
|
||||
timeouts: None,
|
||||
@@ -458,6 +462,8 @@ mod tests {
|
||||
),
|
||||
provider_request_body_base64: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
content_encoding: None,
|
||||
request_gzip: None,
|
||||
proxy: None,
|
||||
transport_profile: None,
|
||||
timeouts: None,
|
||||
|
||||
+5
-10
@@ -9,7 +9,8 @@ use crate::ai_serving::planner::report_context::{
|
||||
};
|
||||
use crate::ai_serving::planner::spec_metadata::local_openai_responses_spec_metadata;
|
||||
use crate::ai_serving::planner::{
|
||||
build_ai_execution_decision_response, AiExecutionDecisionResponseParts,
|
||||
build_ai_execution_decision_response, resolve_transport_request_gzip_policy,
|
||||
AiExecutionDecisionResponseParts,
|
||||
};
|
||||
use crate::ai_serving::transport::{
|
||||
resolve_transport_execution_timeouts, resolve_transport_profile,
|
||||
@@ -101,15 +102,6 @@ pub(crate) async fn maybe_build_local_openai_responses_decision_payload_for_cand
|
||||
.eq_ignore_ascii_case("chatgpt_web")
|
||||
{
|
||||
extra_fields.insert("chatgpt_web_image".to_string(), json!(true));
|
||||
extra_fields.insert(
|
||||
"local_failover_policy".to_string(),
|
||||
json!({
|
||||
"stop_status_codes": [400, 401, 403, 429, 500, 502, 503, 504],
|
||||
"error_stop_patterns": [
|
||||
{ "pattern": ".*" }
|
||||
]
|
||||
}),
|
||||
);
|
||||
}
|
||||
insert_provider_stream_event_api_format(
|
||||
&mut extra_fields,
|
||||
@@ -212,6 +204,7 @@ pub(crate) async fn maybe_build_local_openai_responses_decision_payload_for_cand
|
||||
image_request_summary: _,
|
||||
request_redacted: _,
|
||||
} = resolved;
|
||||
let request_gzip = resolve_transport_request_gzip_policy(&transport);
|
||||
|
||||
let mut decision = build_ai_execution_decision_response(AiExecutionDecisionResponseParts {
|
||||
decision_is_stream: spec_metadata.require_streaming,
|
||||
@@ -238,6 +231,8 @@ pub(crate) async fn maybe_build_local_openai_responses_decision_payload_for_cand
|
||||
provider_request_body: Some(provider_request_body),
|
||||
provider_request_body_base64: None,
|
||||
content_type: Some("application/json".to_string()),
|
||||
content_encoding: None,
|
||||
request_gzip,
|
||||
proxy,
|
||||
transport_profile,
|
||||
timeouts,
|
||||
|
||||
+6
-1
@@ -27,6 +27,7 @@ use crate::ai_serving::planner::standard::{
|
||||
apply_deepseek_tool_call_thinking_compat, build_cross_format_openai_responses_request_body,
|
||||
build_cross_format_openai_responses_upstream_url, build_local_openai_responses_request_body,
|
||||
build_local_openai_responses_upstream_url, request_body_build_failure_extra_data,
|
||||
request_conversion_failure_extra_data,
|
||||
};
|
||||
use crate::ai_serving::transport::antigravity::{
|
||||
build_antigravity_safe_v1internal_request, build_antigravity_static_identity_headers,
|
||||
@@ -371,10 +372,14 @@ pub(crate) async fn resolve_local_openai_responses_candidate_payload_parts(
|
||||
candidate_index,
|
||||
candidate_id,
|
||||
"provider_request_body_build_failed",
|
||||
request_body_build_failure_extra_data(
|
||||
request_conversion_failure_extra_data(
|
||||
body_json,
|
||||
spec_metadata.api_format,
|
||||
provider_api_format,
|
||||
Some(mapped_model.as_str()),
|
||||
Some(parts.uri.path()),
|
||||
upstream_is_stream,
|
||||
"openai_responses_request_conversion",
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -72,8 +72,10 @@ pub(crate) use aether_provider_transport::{
|
||||
build_local_openai_chat_upstream_url, build_local_openai_responses_upstream_url,
|
||||
build_openai_image_headers, build_openai_image_upstream_url, build_passthrough_headers,
|
||||
build_request_trace_proxy_value, build_same_format_provider_headers,
|
||||
build_same_format_provider_request_body, build_same_format_provider_upstream_url,
|
||||
build_standard_plan_fallback_headers, build_standard_plan_fallback_openai_chat_url,
|
||||
build_same_format_provider_request_body,
|
||||
build_same_format_provider_request_body_with_compatibility_report,
|
||||
build_same_format_provider_upstream_url, build_standard_plan_fallback_headers,
|
||||
build_standard_plan_fallback_openai_chat_url,
|
||||
build_standard_plan_fallback_openai_responses_url, build_standard_provider_request_headers,
|
||||
build_transport_request_url, build_transport_request_url_for_request_body,
|
||||
build_video_create_headers, build_video_create_request_body, build_video_create_upstream_url,
|
||||
@@ -106,12 +108,14 @@ pub(crate) use aether_provider_transport::{
|
||||
GeminiCliRequestAuthUnsupportedReason, GeminiCliRequestEnvelopeSupport,
|
||||
GeminiFilesHeadersInput, GeminiFilesRequestBodyError, GeminiFilesRequestBodyParts,
|
||||
GrokHeaderInput, LocalResolvedOAuthRequestAuth, ProviderOpenAiImageHeadersInput,
|
||||
ProviderVideoCreateFamily, ProviderVideoCreateHeadersInput, SameFormatProviderFamily,
|
||||
SameFormatProviderHeadersInput, SameFormatProviderRequestBehavior,
|
||||
ProviderVideoCreateFamily, ProviderVideoCreateHeadersInput,
|
||||
SameFormatProviderCompatibilityEdit, SameFormatProviderCompatibilityEditAction,
|
||||
SameFormatProviderFamily, SameFormatProviderHeadersInput, SameFormatProviderRequestBehavior,
|
||||
SameFormatProviderRequestBehaviorParams, SameFormatProviderRequestBodyInput,
|
||||
SameFormatProviderUpstreamUrlParams, StandardPlanFallbackAcceptPolicy,
|
||||
StandardPlanFallbackHeadersInput, StandardProviderRequestHeaders,
|
||||
StandardProviderRequestHeadersInput, TransportRequestBodySemanticsError,
|
||||
TransportRequestUrlParams, GEMINI_CLI_USER_AGENT, GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME,
|
||||
GROK_CHAT_PATH, GROK_INTERNAL_HEADER, GROK_RATE_LIMITS_PATH, WINDSURF_ENVELOPE_NAME,
|
||||
SameFormatProviderRequestBodyOutput, SameFormatProviderUpstreamUrlParams,
|
||||
StandardPlanFallbackAcceptPolicy, StandardPlanFallbackHeadersInput,
|
||||
StandardProviderRequestHeaders, StandardProviderRequestHeadersInput,
|
||||
TransportRequestBodySemanticsError, TransportRequestUrlParams, GEMINI_CLI_USER_AGENT,
|
||||
GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME, GROK_CHAT_PATH, GROK_INTERNAL_HEADER,
|
||||
GROK_RATE_LIMITS_PATH, WINDSURF_ENVELOPE_NAME,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user