mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
fix(gateway): route openai image streams with images surface
This commit is contained in:
@@ -7,7 +7,7 @@ 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;
|
||||
use crate::url::build_openai_responses_url;
|
||||
use crate::url::{build_openai_image_url, build_openai_responses_url};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ProviderOpenAiImageHeadersInput<'a> {
|
||||
@@ -56,9 +56,18 @@ pub fn resolve_openai_image_auth(
|
||||
|
||||
pub fn build_openai_image_upstream_url(
|
||||
transport: &GatewayProviderTransportSnapshot,
|
||||
request_path: Option<&str>,
|
||||
request_query: Option<&str>,
|
||||
) -> String {
|
||||
build_openai_responses_url(&transport.endpoint.base_url, request_query, false)
|
||||
if transport
|
||||
.provider
|
||||
.provider_type
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("codex")
|
||||
{
|
||||
return build_openai_responses_url(&transport.endpoint.base_url, request_query, false);
|
||||
}
|
||||
build_openai_image_url(&transport.endpoint.base_url, request_path, request_query)
|
||||
}
|
||||
|
||||
pub fn build_openai_image_headers(
|
||||
@@ -155,12 +164,30 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_openai_image_url_on_responses_surface() {
|
||||
let url = build_openai_image_upstream_url(&sample_transport(), Some("trace=1"));
|
||||
fn codex_openai_image_url_stays_on_responses_surface() {
|
||||
let url = build_openai_image_upstream_url(
|
||||
&sample_transport(),
|
||||
Some("/v1/images/generations"),
|
||||
Some("trace=1"),
|
||||
);
|
||||
|
||||
assert_eq!(url, "https://api.openai.com/v1/responses?trace=1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standard_openai_image_url_uses_images_surface() {
|
||||
let mut transport = sample_transport();
|
||||
transport.provider.provider_type = "openai".to_string();
|
||||
|
||||
let url = build_openai_image_upstream_url(
|
||||
&transport,
|
||||
Some("/v1/images/generations"),
|
||||
Some("trace=1"),
|
||||
);
|
||||
|
||||
assert_eq!(url, "https://api.openai.com/v1/images/generations?trace=1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chatgpt_web_is_supported_by_dedicated_openai_image_transport_policy() {
|
||||
let mut transport = sample_transport();
|
||||
|
||||
@@ -41,6 +41,43 @@ pub fn build_openai_responses_url(
|
||||
url
|
||||
}
|
||||
|
||||
pub fn build_openai_image_url(
|
||||
upstream_base_url: &str,
|
||||
request_path: Option<&str>,
|
||||
query: Option<&str>,
|
||||
) -> String {
|
||||
let (trimmed, base_query) = split_base_url_query(upstream_base_url);
|
||||
let trimmed = trimmed.trim_end_matches('/');
|
||||
let suffix = openai_image_path_suffix(request_path);
|
||||
let mut url = if openai_image_base_includes_operation_path(trimmed) {
|
||||
trimmed.to_string()
|
||||
} else if trimmed.ends_with("/v1") || google_openai_compat_base_includes_api_root(trimmed) {
|
||||
format!("{trimmed}{suffix}")
|
||||
} else {
|
||||
format!("{trimmed}/v1{suffix}")
|
||||
};
|
||||
append_merged_query(&mut url, base_query, None, query, &[]);
|
||||
url
|
||||
}
|
||||
|
||||
fn openai_image_path_suffix(request_path: Option<&str>) -> &'static str {
|
||||
match request_path
|
||||
.map(str::trim)
|
||||
.map(|value| value.trim_end_matches('/'))
|
||||
{
|
||||
Some("/v1/images/edits") | Some("/images/edits") => "/images/edits",
|
||||
_ => "/images/generations",
|
||||
}
|
||||
}
|
||||
|
||||
fn openai_image_base_includes_operation_path(base_url: &str) -> bool {
|
||||
let path = Url::parse(base_url)
|
||||
.ok()
|
||||
.map(|url| url.path().trim_end_matches('/').to_string())
|
||||
.unwrap_or_else(|| base_url.trim_end_matches('/').to_string());
|
||||
path.ends_with("/images/generations") || path.ends_with("/images/edits")
|
||||
}
|
||||
|
||||
pub fn build_claude_messages_url(upstream_base_url: &str, query: Option<&str>) -> String {
|
||||
let (trimmed, base_query) = split_base_url_query(upstream_base_url);
|
||||
let trimmed = trimmed.trim_end_matches('/');
|
||||
@@ -307,7 +344,7 @@ 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_gemini_video_predict_long_running_url, build_openai_chat_url, build_openai_image_url,
|
||||
build_openai_responses_url, build_passthrough_path_url,
|
||||
normalize_gemini_content_action_path,
|
||||
};
|
||||
@@ -353,6 +390,22 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn openai_image_url_uses_images_surface() {
|
||||
assert_eq!(
|
||||
build_openai_image_url(
|
||||
"https://api.openai.example/v1?tenant=demo",
|
||||
Some("/v1/images/generations"),
|
||||
Some("trace=1")
|
||||
),
|
||||
"https://api.openai.example/v1/images/generations?tenant=demo&trace=1"
|
||||
);
|
||||
assert_eq!(
|
||||
build_openai_image_url("https://api.openai.example", Some("/v1/images/edits"), None),
|
||||
"https://api.openai.example/v1/images/edits"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merges_base_url_query_for_dynamic_gemini_content_urls() {
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user