删除已失效的跨模型回退分支

移除 generation / edit 重试路径中恒为 false 的 should_fallback_to_gpt_image_2_c 判断与 record_image_model_fallback 调用

删除为计算该判断而额外调用 handle_image_response 的预解析,避免每次重试都白解析响应并下载图片

删除只断言该函数恒为 false 的单元测试,跨模型不切换已由集成测试覆盖
This commit is contained in:
2026-09-19 13:14:23 +08:00
parent 6c33459b90
commit 1c950df116
@@ -99,7 +99,7 @@ pub async fn create_image_generation_with_model(
let request_url = images_generation_url(settings);
let normalized_size = normalize_image_size_for_model(requested_model, size);
let started_at = std::time::Instant::now();
let mut upstream_model = preferred_image_upstream_model(requested_model);
let upstream_model = preferred_image_upstream_model(requested_model);
let mut recovered_failure_audits = Vec::new();
let mut attempt = 1;
loop {
@@ -140,51 +140,6 @@ pub async fn create_image_generation_with_model(
response.body.as_str(),
attempt,
) {
let primary_error = if upstream_model == GPT_IMAGE_2_MODEL {
handle_image_response(
http_client,
request_url.as_str(),
response.status,
response.body.as_str(),
auditable_image_model(upstream_model),
failure_context,
started_at.elapsed().as_millis() as u64,
Some(prompt.chars().count()),
Some(reference_images.len()),
candidate_count,
"vector-engine",
settings.request_deadline,
)
.await
.err()
} else {
None
};
if primary_error.as_ref().is_some_and(|error| {
should_fallback_to_gpt_image_2_c(
requested_model,
upstream_model,
attempt,
error,
settings,
)
}) {
let error = primary_error.expect("primary error checked above");
record_image_model_fallback(
"generation",
request_url.as_str(),
upstream_model,
GPT_IMAGE_2_C_MODEL,
attempt,
&error,
);
if let Some(audit) = error.audit().cloned() {
recovered_failure_audits.push(audit);
}
upstream_model = GPT_IMAGE_2_C_MODEL;
attempt += 1;
continue;
}
if retry_image_upstream_status_after_delay(
"generation",
request_url.as_str(),
@@ -283,29 +238,6 @@ pub async fn create_image_generation_with_model(
.append(&mut recovered_failure_audits);
return Ok(generated);
}
Err(error)
if should_fallback_to_gpt_image_2_c(
requested_model,
upstream_model,
attempt,
&error,
settings,
) =>
{
record_image_model_fallback(
"generation",
request_url.as_str(),
upstream_model,
GPT_IMAGE_2_C_MODEL,
attempt,
&error,
);
if let Some(audit) = error.audit().cloned() {
recovered_failure_audits.push(audit);
}
upstream_model = GPT_IMAGE_2_C_MODEL;
attempt += 1;
}
Err(error) => {
return Err(finish_image_model_fallback_error(
error,
@@ -577,7 +509,7 @@ pub async fn create_image_edit_with_references_and_model(
let reference_image_bytes_total: usize =
reference_images.iter().map(|image| image.bytes.len()).sum();
let started_at = std::time::Instant::now();
let mut upstream_model = preferred_image_upstream_model(requested_model);
let upstream_model = preferred_image_upstream_model(requested_model);
let mut recovered_failure_audits = Vec::new();
let mut attempt = 1;
loop {
@@ -645,51 +577,6 @@ pub async fn create_image_edit_with_references_and_model(
response.body.as_str(),
attempt,
) {
let primary_error = if upstream_model == GPT_IMAGE_2_MODEL {
handle_image_response(
http_client,
request_url.as_str(),
response.status,
response.body.as_str(),
auditable_image_model(upstream_model),
failure_context,
started_at.elapsed().as_millis() as u64,
Some(prompt.chars().count()),
Some(reference_image_count),
candidate_count,
"vector-engine-edit",
settings.request_deadline,
)
.await
.err()
} else {
None
};
if primary_error.as_ref().is_some_and(|error| {
should_fallback_to_gpt_image_2_c(
requested_model,
upstream_model,
attempt,
error,
settings,
)
}) {
let error = primary_error.expect("primary error checked above");
record_image_model_fallback(
"edit",
request_url.as_str(),
upstream_model,
GPT_IMAGE_2_C_MODEL,
attempt,
&error,
);
if let Some(audit) = error.audit().cloned() {
recovered_failure_audits.push(audit);
}
upstream_model = GPT_IMAGE_2_C_MODEL;
attempt += 1;
continue;
}
if retry_image_upstream_status_after_delay(
"edit",
request_url.as_str(),
@@ -790,29 +677,6 @@ pub async fn create_image_edit_with_references_and_model(
.append(&mut recovered_failure_audits);
return Ok(generated);
}
Err(error)
if should_fallback_to_gpt_image_2_c(
requested_model,
upstream_model,
attempt,
&error,
settings,
) =>
{
record_image_model_fallback(
"edit",
request_url.as_str(),
upstream_model,
GPT_IMAGE_2_C_MODEL,
attempt,
&error,
);
if let Some(audit) = error.audit().cloned() {
recovered_failure_audits.push(audit);
}
upstream_model = GPT_IMAGE_2_C_MODEL;
attempt += 1;
}
Err(error) => {
return Err(finish_image_model_fallback_error(
error,
@@ -845,19 +709,6 @@ fn auditable_image_model(model: &str) -> Option<&'static str> {
}
}
fn should_fallback_to_gpt_image_2_c(
requested_model: &str,
upstream_model: &str,
attempt: u32,
error: &PlatformImageError,
settings: &ImageProviderSettings,
) -> bool {
// Compatibility function retained for old call sites/tests. New dispatch
// never crosses models; retries are handled by the same-model loop only.
let _ = (requested_model, upstream_model, attempt, error, settings);
false
}
fn contains_image_provider_content_rejection(message: &str, raw_excerpt: &str) -> bool {
let haystack = format!("{message}\n{raw_excerpt}").to_ascii_lowercase();
[
@@ -883,31 +734,6 @@ fn contains_image_provider_content_rejection(message: &str, raw_excerpt: &str) -
.any(|marker| haystack.contains(marker))
}
fn record_image_model_fallback(
request_kind: &'static str,
request_url: &str,
from_model: &str,
to_model: &str,
attempt: u32,
error: &PlatformImageError,
) {
let audit = error.audit();
tracing::warn!(
provider = VECTOR_ENGINE_PROVIDER,
endpoint = %request_url,
request_kind,
fallback_from_model = from_model,
fallback_to_model = to_model,
attempt,
next_attempt = attempt + 1,
max_attempts = VECTOR_ENGINE_SEND_MAX_ATTEMPTS,
failure_stage = audit.map(|audit| audit.failure_stage).unwrap_or("unknown"),
status = audit.and_then(|audit| audit.status_code).unwrap_or_default(),
error = %error.message(),
"ImageProvider 首选图片模型失败,切换兼容模型"
);
}
fn should_retry_image_curl_send_error(
error: &super::curl_transport::ImageProviderCurlError,
attempt: u32,
@@ -1202,28 +1028,6 @@ mod tests {
assert!(!should_retry_image_upstream_response(502, "", 5));
}
#[test]
fn image_provider_never_falls_back_across_image_models() {
let settings = ImageProviderSettings {
provider: ImageProvider::VectorEngine,
base_url: "https://vector.example/v1".to_string(),
api_key: "test-key".to_string(),
request_timeout_ms: 1_000,
request_deadline: None,
};
let error = PlatformImageError::InvalidRequest {
provider: VECTOR_ENGINE_PROVIDER,
message: "model unavailable".to_string(),
};
assert!(!should_fallback_to_gpt_image_2_c(
GPT_IMAGE_2_MODEL,
GPT_IMAGE_2_MODEL,
1,
&error,
&settings,
));
}
#[test]
fn image_provider_send_retry_delay_uses_exponential_backoff_with_bounded_jitter() {
assert_eq!(image_provider_send_retry_delay_ms(1, 0), 500);