fix(gateway): normalize Gemini Vertex embedding transport

This commit is contained in:
MMEXA
2026-05-18 15:39:29 +00:00
parent 66f154a251
commit b480f3aaff
24 changed files with 1046 additions and 107 deletions
@@ -2,7 +2,8 @@ use aether_contracts::{ExecutionPlan, ExecutionResult};
use serde_json::Value;
use crate::orchestration::{
resolve_local_failover_analysis_for_attempt, LocalFailoverAnalysis, LocalFailoverDecision,
resolve_local_failover_analysis_for_attempt, LocalFailoverAnalysis,
LocalFailoverClassification, LocalFailoverDecision,
};
use crate::AppState;
@@ -48,6 +49,15 @@ pub(crate) async fn analyze_local_candidate_failover_sync(
return LocalFailoverAnalysis::use_default();
}
if let Some(error) = result.error.as_ref() {
if !error.retryable && !error.failover_recommended {
return LocalFailoverAnalysis {
classification: LocalFailoverClassification::StopExecutionError,
decision: LocalFailoverDecision::StopLocalFailover,
};
}
}
resolve_local_failover_analysis_for_attempt(
state,
plan,
@@ -326,14 +336,14 @@ pub(crate) fn resolve_core_stream_direct_finalize_report_kind(plan_kind: &str) -
mod tests {
use std::collections::BTreeSet;
use aether_contracts::ExecutionResult;
use aether_contracts::{ExecutionError, ExecutionErrorKind, ExecutionPhase, ExecutionResult};
use aether_data::repository::provider_catalog::InMemoryProviderCatalogReadRepository;
use aether_data_contracts::repository::provider_catalog::{
StoredProviderCatalogEndpoint, StoredProviderCatalogKey, StoredProviderCatalogProvider,
};
use super::{
resolve_core_stream_error_finalize_report_kind,
analyze_local_candidate_failover_sync, resolve_core_stream_error_finalize_report_kind,
resolve_core_sync_error_finalize_report_kind, should_fallback_to_control_stream,
should_fallback_to_control_sync, should_retry_next_local_candidate_stream,
should_retry_next_local_candidate_sync, should_stop_local_candidate_failover_stream,
@@ -607,6 +617,69 @@ mod tests {
);
}
#[tokio::test]
async fn sync_failover_honors_non_retryable_execution_error() {
let result = ExecutionResult {
request_id: "req-1".to_string(),
candidate_id: None,
status_code: 502,
headers: Default::default(),
body: None,
telemetry: None,
error: Some(ExecutionError {
kind: ExecutionErrorKind::Upstream5xx,
phase: ExecutionPhase::Finalize,
message: "provider returned HTTP 200 without visible model output".to_string(),
upstream_status: Some(200),
retryable: false,
failover_recommended: false,
}),
};
let local_report_context = serde_json::json!({
"candidate_index": 0,
"retry_index": 0,
});
let state = build_state_with_provider_config(None);
let plan = sample_plan();
let analysis = analyze_local_candidate_failover_sync(
&state,
&plan,
"openai_chat_sync",
Some(&local_report_context),
&result,
Some("provider returned HTTP 200 without visible model output"),
)
.await;
assert_eq!(
analysis.decision,
crate::orchestration::LocalFailoverDecision::StopLocalFailover
);
assert!(
!should_retry_next_local_candidate_sync(
&state,
&plan,
"openai_chat_sync",
Some(&local_report_context),
&result,
Some("provider returned HTTP 200 without visible model output"),
)
.await
);
assert!(
should_stop_local_candidate_failover_sync(
&state,
&plan,
"openai_chat_sync",
Some(&local_report_context),
&result,
Some("provider returned HTTP 200 without visible model output"),
)
.await
);
}
#[tokio::test]
async fn sync_retry_next_candidate_skips_video_follow_up_plan_kinds() {
let result = ExecutionResult {