mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
migrate ai format conversion to responses adapters
This commit is contained in:
@@ -148,12 +148,7 @@ fn local_same_format_transport_unsupported_reason(
|
||||
Some("key_inactive")
|
||||
};
|
||||
}
|
||||
if !transport
|
||||
.endpoint
|
||||
.api_format
|
||||
.trim()
|
||||
.eq_ignore_ascii_case(api_format.trim())
|
||||
{
|
||||
if !same_api_format(&transport.endpoint.api_format, api_format) {
|
||||
return Some("transport_api_format_mismatch");
|
||||
}
|
||||
if !header_rules_are_locally_supported(transport.endpoint.header_rules.as_ref()) {
|
||||
@@ -203,3 +198,15 @@ fn local_same_format_transport_unsupported_reason(
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn same_api_format(left: &str, right: &str) -> bool {
|
||||
normalize_api_format_alias(left) == normalize_api_format_alias(right)
|
||||
}
|
||||
|
||||
fn normalize_api_format_alias(value: &str) -> String {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"openai:cli" => "openai:responses".to_string(),
|
||||
"openai:compact" => "openai:responses:compact".to_string(),
|
||||
other => other.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,14 +75,14 @@ const CODEX_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTempla
|
||||
base_url: "https://chatgpt.com/backend-api/codex",
|
||||
endpoints: &[
|
||||
FixedProviderEndpointTemplate {
|
||||
item_key: "openai:cli",
|
||||
api_format: "openai:cli",
|
||||
item_key: "openai:responses",
|
||||
api_format: "openai:responses",
|
||||
custom_path: None,
|
||||
config_defaults: FORCE_STREAM_ENDPOINT_CONFIG_DEFAULTS,
|
||||
},
|
||||
FixedProviderEndpointTemplate {
|
||||
item_key: "openai:compact",
|
||||
api_format: "openai:compact",
|
||||
item_key: "openai:responses:compact",
|
||||
api_format: "openai:responses:compact",
|
||||
custom_path: None,
|
||||
config_defaults: EMPTY_ENDPOINT_CONFIG_DEFAULTS,
|
||||
},
|
||||
@@ -181,7 +181,11 @@ pub fn fixed_provider_endpoint_template_by_api_format(
|
||||
provider_type: &str,
|
||||
api_format: &str,
|
||||
) -> Option<&'static FixedProviderEndpointTemplate> {
|
||||
let normalized = api_format.trim();
|
||||
let normalized = match api_format.trim().to_ascii_lowercase().as_str() {
|
||||
"openai:cli" => "openai:responses",
|
||||
"openai:compact" => "openai:responses:compact",
|
||||
_ => api_format.trim(),
|
||||
};
|
||||
fixed_provider_template(provider_type)?
|
||||
.endpoints
|
||||
.iter()
|
||||
@@ -299,7 +303,11 @@ mod tests {
|
||||
.iter()
|
||||
.map(|item| item.api_format)
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["openai:cli", "openai:compact", "openai:image"]
|
||||
vec![
|
||||
"openai:responses",
|
||||
"openai:responses:compact",
|
||||
"openai:image"
|
||||
]
|
||||
);
|
||||
|
||||
let image_template =
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::claude_code::build_claude_code_messages_url;
|
||||
use crate::snapshot::GatewayProviderTransportSnapshot;
|
||||
use crate::url::{
|
||||
build_claude_messages_url, build_gemini_content_url, build_openai_chat_url,
|
||||
build_openai_cli_url, build_passthrough_path_url,
|
||||
build_openai_responses_url, build_passthrough_path_url,
|
||||
};
|
||||
use crate::vertex::{
|
||||
build_vertex_api_key_gemini_content_url, resolve_local_vertex_api_key_query_auth,
|
||||
@@ -65,12 +65,12 @@ pub fn build_transport_request_url(
|
||||
&transport.endpoint.base_url,
|
||||
params.request_query,
|
||||
)),
|
||||
"openai:cli" => Some(build_openai_cli_url(
|
||||
"openai:responses" | "openai:cli" => Some(build_openai_responses_url(
|
||||
&transport.endpoint.base_url,
|
||||
params.request_query,
|
||||
false,
|
||||
)),
|
||||
"openai:compact" => Some(build_openai_cli_url(
|
||||
"openai:responses:compact" | "openai:compact" => Some(build_openai_responses_url(
|
||||
&transport.endpoint.base_url,
|
||||
params.request_query,
|
||||
true,
|
||||
@@ -346,6 +346,30 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_openai_responses_url_for_formal_format_name() {
|
||||
let transport = sample_transport(
|
||||
"openai",
|
||||
"openai:responses",
|
||||
"https://api.openai.example/v1",
|
||||
None,
|
||||
);
|
||||
|
||||
let url = build_transport_request_url(
|
||||
&transport,
|
||||
TransportRequestUrlParams {
|
||||
provider_api_format: "openai:responses",
|
||||
mapped_model: None,
|
||||
upstream_is_stream: false,
|
||||
request_query: Some("tenant=demo"),
|
||||
kiro_api_region: None,
|
||||
},
|
||||
)
|
||||
.expect("openai responses url");
|
||||
|
||||
assert_eq!(url, "https://api.openai.example/v1/responses?tenant=demo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expands_custom_path_templates_when_hook_does_not_apply() {
|
||||
let transport = sample_transport(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -308,7 +308,7 @@ mod tests {
|
||||
)
|
||||
.expect("key should build")
|
||||
.with_transport_fields(
|
||||
Some(serde_json::json!(["openai:chat", "openai:cli"])),
|
||||
Some(serde_json::json!(["openai:chat", "openai:responses"])),
|
||||
encrypted_api_key,
|
||||
Some(encrypted_auth_config),
|
||||
Some(serde_json::json!({"openai:chat": 0.8})),
|
||||
@@ -382,7 +382,10 @@ mod tests {
|
||||
name: "prod-key".to_string(),
|
||||
auth_type: "api_key".to_string(),
|
||||
is_active: true,
|
||||
api_formats: Some(vec!["openai:chat".to_string(), "openai:cli".to_string(),]),
|
||||
api_formats: Some(vec![
|
||||
"openai:chat".to_string(),
|
||||
"openai:responses".to_string(),
|
||||
]),
|
||||
allowed_models: Some(vec!["gpt-4.1".to_string(), "gpt-4.1-mini".to_string(),]),
|
||||
capabilities: Some(serde_json::json!({"cache_1h": true})),
|
||||
rate_multipliers: Some(serde_json::json!({"openai:chat": 0.8})),
|
||||
@@ -672,9 +675,9 @@ mod tests {
|
||||
let endpoint = StoredProviderCatalogEndpoint::new(
|
||||
"endpoint-safe-2".to_string(),
|
||||
"provider-1".to_string(),
|
||||
"openai:cli".to_string(),
|
||||
"openai:responses".to_string(),
|
||||
Some("openai".to_string()),
|
||||
Some("cli".to_string()),
|
||||
Some("responses".to_string()),
|
||||
true,
|
||||
)
|
||||
.expect("endpoint should build")
|
||||
@@ -707,7 +710,7 @@ mod tests {
|
||||
)
|
||||
.expect("key should build")
|
||||
.with_transport_fields(
|
||||
Some(serde_json::json!(["openai:cli"])),
|
||||
Some(serde_json::json!(["openai:responses"])),
|
||||
encrypted_api_key,
|
||||
Some(encrypted_auth_config),
|
||||
None,
|
||||
@@ -737,7 +740,7 @@ mod tests {
|
||||
);
|
||||
assert!(!supports_local_standard_transport_with_network(
|
||||
&snapshot,
|
||||
"openai:cli"
|
||||
"openai:responses"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -820,9 +823,9 @@ mod tests {
|
||||
let endpoint = StoredProviderCatalogEndpoint::new(
|
||||
"endpoint-safe-4".to_string(),
|
||||
"provider-1".to_string(),
|
||||
"openai:cli".to_string(),
|
||||
"openai:responses".to_string(),
|
||||
Some("openai".to_string()),
|
||||
Some("cli".to_string()),
|
||||
Some("responses".to_string()),
|
||||
true,
|
||||
)
|
||||
.expect("endpoint should build")
|
||||
@@ -862,7 +865,7 @@ mod tests {
|
||||
)
|
||||
.expect("key should build")
|
||||
.with_transport_fields(
|
||||
Some(serde_json::json!(["openai:cli"])),
|
||||
Some(serde_json::json!(["openai:responses"])),
|
||||
encrypted_api_key,
|
||||
Some(encrypted_auth_config),
|
||||
None,
|
||||
@@ -900,7 +903,7 @@ mod tests {
|
||||
);
|
||||
assert!(supports_local_standard_transport_with_network(
|
||||
&snapshot,
|
||||
"openai:cli"
|
||||
"openai:responses"
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,11 @@ pub fn build_openai_chat_url(upstream_base_url: &str, query: Option<&str>) -> St
|
||||
url
|
||||
}
|
||||
|
||||
pub fn build_openai_cli_url(upstream_base_url: &str, query: Option<&str>, compact: bool) -> String {
|
||||
pub fn build_openai_responses_url(
|
||||
upstream_base_url: &str,
|
||||
query: Option<&str>,
|
||||
compact: bool,
|
||||
) -> String {
|
||||
let (trimmed, base_query) = split_base_url_query(upstream_base_url);
|
||||
let trimmed = trimmed.trim_end_matches('/');
|
||||
let suffix = if compact {
|
||||
@@ -242,8 +246,8 @@ fn merge_query_string(
|
||||
mod tests {
|
||||
use super::{
|
||||
build_gemini_content_url, build_gemini_files_passthrough_url,
|
||||
build_gemini_video_predict_long_running_url, build_openai_chat_url, build_openai_cli_url,
|
||||
build_passthrough_path_url,
|
||||
build_gemini_video_predict_long_running_url, build_openai_chat_url,
|
||||
build_openai_responses_url, build_passthrough_path_url,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -258,13 +262,13 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn openai_cli_url_preserves_codex_path_prefix() {
|
||||
fn openai_responses_url_preserves_codex_path_prefix() {
|
||||
assert_eq!(
|
||||
build_openai_cli_url("https://tiger.bookapi.cc/codex", None, false),
|
||||
build_openai_responses_url("https://tiger.bookapi.cc/codex", None, false),
|
||||
"https://tiger.bookapi.cc/codex/responses"
|
||||
);
|
||||
assert_eq!(
|
||||
build_openai_cli_url("https://tiger.bookapi.cc/codex?tenant=demo", None, true),
|
||||
build_openai_responses_url("https://tiger.bookapi.cc/codex?tenant=demo", None, true),
|
||||
"https://tiger.bookapi.cc/codex/responses/compact?tenant=demo"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user