mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-12 14:10:19 +08:00
Merge commit 'refs/pr/585'
# Conflicts: # apps/aether-gateway/src/ai_serving/planner/passthrough/provider/family/request.rs # apps/aether-gateway/src/tests/ai_execute/stream_provider_gemini/local_cli.rs # apps/aether-gateway/src/tests/ai_execute/sync/gemini/cli.rs # apps/aether-gateway/src/tests/control/admin/provider_query.rs # crates/aether-provider-transport/src/gemini_cli/mod.rs # crates/aether-provider-transport/src/gemini_cli/request.rs # crates/aether-provider-transport/src/gemini_cli/url.rs # crates/aether-provider-transport/src/lib.rs
This commit is contained in:
@@ -3,11 +3,11 @@ use std::sync::Arc;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::ai_serving::transport::gemini_cli::resolve_gemini_cli_project_id;
|
||||
use crate::ai_serving::transport::{
|
||||
build_gemini_cli_v1internal_request, build_standard_provider_request_headers,
|
||||
GatewayProviderTransportSnapshot, GeminiCliRequestEnvelopeSupport,
|
||||
StandardProviderRequestHeaders, StandardProviderRequestHeadersInput, GEMINI_CLI_USER_AGENT,
|
||||
GatewayProviderTransportSnapshot, GeminiCliRequestAuth, GeminiCliRequestAuthSupport,
|
||||
GeminiCliRequestEnvelopeSupport, StandardProviderRequestHeaders,
|
||||
StandardProviderRequestHeadersInput, GEMINI_CLI_USER_AGENT,
|
||||
};
|
||||
use crate::AppState;
|
||||
|
||||
@@ -101,24 +101,33 @@ async fn build_gemini_cli_v1internal_payload(
|
||||
gemini_request_body: &Value,
|
||||
) -> Result<GeminiCliV1InternalPayload, GeminiCliV1InternalRequestError> {
|
||||
let mut resolved_transport = Arc::clone(transport);
|
||||
let project_id = match resolve_gemini_cli_project_id(&resolved_transport) {
|
||||
Some(project_id) => Some(project_id),
|
||||
None => match state
|
||||
let mut auth = match crate::ai_serving::transport::resolve_local_gemini_cli_request_auth(
|
||||
&resolved_transport,
|
||||
) {
|
||||
GeminiCliRequestAuthSupport::Supported(auth) => auth,
|
||||
GeminiCliRequestAuthSupport::Unsupported(_) => {
|
||||
return Err(GeminiCliV1InternalRequestError::ProjectUnavailable);
|
||||
}
|
||||
};
|
||||
if auth.project_id.is_none() {
|
||||
auth = match state
|
||||
.hydrate_gemini_cli_project_metadata_for_transport(&resolved_transport)
|
||||
.await
|
||||
{
|
||||
Some(hydrated) => {
|
||||
let project_id = resolve_gemini_cli_project_id(&hydrated);
|
||||
resolved_transport = Arc::new(hydrated);
|
||||
project_id
|
||||
match crate::ai_serving::transport::resolve_local_gemini_cli_request_auth(
|
||||
&resolved_transport,
|
||||
) {
|
||||
GeminiCliRequestAuthSupport::Supported(auth) => auth,
|
||||
GeminiCliRequestAuthSupport::Unsupported(_) => GeminiCliRequestAuth::default(),
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
},
|
||||
None => GeminiCliRequestAuth::default(),
|
||||
};
|
||||
}
|
||||
.ok_or(GeminiCliV1InternalRequestError::ProjectUnavailable)?;
|
||||
|
||||
let body = match build_gemini_cli_v1internal_request(
|
||||
&project_id,
|
||||
&auth,
|
||||
trace_id,
|
||||
mapped_model,
|
||||
gemini_request_body,
|
||||
|
||||
@@ -91,6 +91,11 @@ pub(crate) async fn maybe_build_local_same_format_provider_decision_payload_for_
|
||||
"envelope_name".to_string(),
|
||||
json!(crate::ai_serving::transport::kiro::KIRO_ENVELOPE_NAME),
|
||||
);
|
||||
} else if resolved.is_gemini_cli {
|
||||
extra_fields.insert(
|
||||
"envelope_name".to_string(),
|
||||
json!(aether_ai_formats::api::GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME),
|
||||
);
|
||||
} else if resolved.is_antigravity {
|
||||
extra_fields.insert(
|
||||
"envelope_name".to_string(),
|
||||
|
||||
@@ -15,11 +15,11 @@ use crate::ai_serving::transport::antigravity::{
|
||||
classify_local_antigravity_request_support, AntigravityEnvelopeRequestType,
|
||||
AntigravityRequestEnvelopeSupport, AntigravityRequestSideSupport,
|
||||
};
|
||||
use crate::ai_serving::transport::gemini_cli::resolve_gemini_cli_project_id;
|
||||
use crate::ai_serving::transport::{
|
||||
build_gemini_cli_v1internal_request, build_grok_browser_headers, build_grok_upstream_url,
|
||||
build_same_format_provider_headers, GeminiCliRequestEnvelopeSupport, GrokHeaderInput,
|
||||
SameFormatProviderHeadersInput, GEMINI_CLI_USER_AGENT, GROK_CHAT_PATH,
|
||||
build_same_format_provider_headers, resolve_local_gemini_cli_request_auth,
|
||||
GeminiCliRequestAuth, GeminiCliRequestAuthSupport, GeminiCliRequestEnvelopeSupport,
|
||||
GrokHeaderInput, SameFormatProviderHeadersInput, GEMINI_CLI_USER_AGENT, GROK_CHAT_PATH,
|
||||
};
|
||||
use crate::ai_serving::{CandidateFailureDiagnostic, GatewayProviderTransportSnapshot};
|
||||
use crate::{AppState, GatewayError};
|
||||
@@ -71,6 +71,7 @@ pub(crate) fn resolve_same_format_provider_transport_unsupported_reason_for_trac
|
||||
);
|
||||
if !behavior.is_antigravity
|
||||
&& !behavior.is_claude_code
|
||||
&& !behavior.is_gemini_cli
|
||||
&& !behavior.is_vertex
|
||||
&& !behavior.is_kiro
|
||||
{
|
||||
@@ -238,35 +239,41 @@ pub(crate) async fn resolve_local_same_format_provider_candidate_payload_parts(
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let gemini_cli_project_id = if prepared.behavior.is_gemini_cli {
|
||||
match resolve_gemini_cli_project_id(&transport) {
|
||||
Some(project_id) => Some(project_id),
|
||||
None => {
|
||||
match state
|
||||
.hydrate_gemini_cli_project_metadata_for_transport(&transport)
|
||||
.await
|
||||
{
|
||||
Some(hydrated) => {
|
||||
let project_id = resolve_gemini_cli_project_id(&hydrated);
|
||||
transport = Arc::new(hydrated);
|
||||
project_id
|
||||
}
|
||||
None => {
|
||||
mark_skipped_local_same_format_provider_candidate(
|
||||
state,
|
||||
input,
|
||||
trace_id,
|
||||
candidate,
|
||||
attempt.candidate_index,
|
||||
&attempt.candidate_id,
|
||||
"transport_auth_unavailable",
|
||||
)
|
||||
.await;
|
||||
return Ok(None);
|
||||
let gemini_cli_auth = if prepared.behavior.is_gemini_cli {
|
||||
let mut auth = match resolve_local_gemini_cli_request_auth(&transport) {
|
||||
GeminiCliRequestAuthSupport::Supported(auth) => auth,
|
||||
GeminiCliRequestAuthSupport::Unsupported(_) => {
|
||||
mark_skipped_local_same_format_provider_candidate(
|
||||
state,
|
||||
input,
|
||||
trace_id,
|
||||
candidate,
|
||||
attempt.candidate_index,
|
||||
&attempt.candidate_id,
|
||||
"transport_auth_unavailable",
|
||||
)
|
||||
.await;
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
if auth.project_id.is_none() {
|
||||
auth = match state
|
||||
.hydrate_gemini_cli_project_metadata_for_transport(&transport)
|
||||
.await
|
||||
{
|
||||
Some(hydrated) => {
|
||||
transport = Arc::new(hydrated);
|
||||
match resolve_local_gemini_cli_request_auth(&transport) {
|
||||
GeminiCliRequestAuthSupport::Supported(auth) => auth,
|
||||
GeminiCliRequestAuthSupport::Unsupported(_) => {
|
||||
GeminiCliRequestAuth::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => GeminiCliRequestAuth::default(),
|
||||
};
|
||||
}
|
||||
Some(auth)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -299,9 +306,9 @@ pub(crate) async fn resolve_local_same_format_provider_candidate_payload_parts(
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
} else if let Some(project_id) = gemini_cli_project_id.as_deref() {
|
||||
} else if let Some(gemini_cli_auth) = gemini_cli_auth.as_ref() {
|
||||
match build_gemini_cli_v1internal_request(
|
||||
project_id,
|
||||
gemini_cli_auth,
|
||||
trace_id,
|
||||
&prepared.mapped_model,
|
||||
&base_provider_request_body,
|
||||
|
||||
+2
@@ -28,6 +28,7 @@ pub(super) struct PreparedSameFormatProviderCandidate {
|
||||
pub(super) behavior: SameFormatProviderRequestBehavior,
|
||||
pub(super) is_antigravity: bool,
|
||||
pub(super) is_claude_code: bool,
|
||||
pub(super) is_gemini_cli: bool,
|
||||
pub(super) is_vertex: bool,
|
||||
pub(super) is_kiro: bool,
|
||||
pub(super) kiro_auth: Option<KiroRequestAuth>,
|
||||
@@ -175,6 +176,7 @@ pub(super) async fn prepare_local_same_format_provider_candidate(
|
||||
behavior,
|
||||
is_antigravity: behavior.is_antigravity,
|
||||
is_claude_code: behavior.is_claude_code,
|
||||
is_gemini_cli: behavior.is_gemini_cli,
|
||||
is_vertex: behavior.is_vertex,
|
||||
is_kiro: behavior.is_kiro,
|
||||
kiro_auth,
|
||||
|
||||
+16
-11
@@ -131,6 +131,7 @@ pub(crate) async fn resolve_local_openai_chat_candidate_payload_parts(
|
||||
.provider_type
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("grok");
|
||||
let is_gemini_cli = is_gemini_cli_provider_transport(transport);
|
||||
|
||||
if is_grok && is_grok_text_provider_api_format(provider_api_format) {
|
||||
let prepared_candidate = match prepare_header_authenticated_candidate(
|
||||
@@ -445,6 +446,8 @@ pub(crate) async fn resolve_local_openai_chat_candidate_payload_parts(
|
||||
};
|
||||
|
||||
let provider_api_format = provider_api_format.trim().to_ascii_lowercase();
|
||||
let normalized_provider_api_format =
|
||||
crate::ai_serving::normalize_api_format_alias(provider_api_format.as_str());
|
||||
if provider_api_format == "openai:image" {
|
||||
return resolve_openai_chat_to_openai_image_payload_parts(
|
||||
state,
|
||||
@@ -479,17 +482,19 @@ pub(crate) async fn resolve_local_openai_chat_candidate_payload_parts(
|
||||
transport,
|
||||
conversion_kind,
|
||||
) {
|
||||
mark_skipped_local_openai_chat_candidate(
|
||||
state,
|
||||
input,
|
||||
trace_id,
|
||||
candidate,
|
||||
candidate_index,
|
||||
candidate_id,
|
||||
skip_reason,
|
||||
)
|
||||
.await;
|
||||
return Ok(None);
|
||||
if !(is_gemini_cli && normalized_provider_api_format == "gemini:generate_content") {
|
||||
mark_skipped_local_openai_chat_candidate(
|
||||
state,
|
||||
input,
|
||||
trace_id,
|
||||
candidate,
|
||||
candidate_index,
|
||||
candidate_id,
|
||||
skip_reason,
|
||||
)
|
||||
.await;
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
let is_kiro_claude_cli =
|
||||
is_kiro_claude_messages_transport(transport, provider_api_format.as_str());
|
||||
|
||||
+25
-18
@@ -116,9 +116,12 @@ pub(crate) async fn resolve_local_openai_responses_candidate_payload_parts(
|
||||
let planner_state = PlannerAppState::new(state);
|
||||
let candidate = &eligible.candidate;
|
||||
let provider_api_format = eligible.provider_api_format.as_str();
|
||||
let normalized_provider_api_format =
|
||||
crate::ai_serving::normalize_api_format_alias(provider_api_format);
|
||||
let transport = &eligible.transport;
|
||||
let transport_profile = crate::ai_serving::transport::resolve_transport_profile(transport);
|
||||
let is_antigravity = is_antigravity_provider_transport(transport);
|
||||
let is_gemini_cli = is_gemini_cli_provider_transport(transport);
|
||||
let is_kiro_claude_cli = is_kiro_claude_messages_transport(transport, provider_api_format);
|
||||
let is_grok = transport
|
||||
.provider
|
||||
@@ -145,25 +148,29 @@ pub(crate) async fn resolve_local_openai_responses_candidate_payload_parts(
|
||||
|
||||
let same_format = api_format_alias_matches(provider_api_format, &client_api_format);
|
||||
let conversion_kind = request_conversion_kind(spec_metadata.api_format, provider_api_format);
|
||||
let transport_unsupported_reason = if is_grok
|
||||
&& is_grok_text_provider_api_format(provider_api_format)
|
||||
{
|
||||
None
|
||||
} else if same_format && is_kiro_claude_cli {
|
||||
local_kiro_request_transport_unsupported_reason_with_network(transport)
|
||||
} else if same_format {
|
||||
local_standard_transport_unsupported_reason_with_network(transport, provider_api_format)
|
||||
} else if is_windsurf_cascade {
|
||||
local_windsurf_request_transport_unsupported_reason_with_network(transport)
|
||||
} else {
|
||||
match conversion_kind {
|
||||
Some(_) if is_antigravity && provider_api_format == "gemini:generate_content" => None,
|
||||
Some(kind) => {
|
||||
crate::ai_serving::request_conversion_transport_unsupported_reason(transport, kind)
|
||||
let transport_unsupported_reason =
|
||||
if is_grok && is_grok_text_provider_api_format(provider_api_format) {
|
||||
None
|
||||
} else if same_format && is_kiro_claude_cli {
|
||||
local_kiro_request_transport_unsupported_reason_with_network(transport)
|
||||
} else if same_format {
|
||||
local_standard_transport_unsupported_reason_with_network(transport, provider_api_format)
|
||||
} else if is_windsurf_cascade {
|
||||
local_windsurf_request_transport_unsupported_reason_with_network(transport)
|
||||
} else {
|
||||
match conversion_kind {
|
||||
Some(_)
|
||||
if (is_antigravity || is_gemini_cli)
|
||||
&& normalized_provider_api_format == "gemini:generate_content" =>
|
||||
{
|
||||
None
|
||||
}
|
||||
Some(kind) => crate::ai_serving::request_conversion_transport_unsupported_reason(
|
||||
transport, kind,
|
||||
),
|
||||
None => Some("transport_api_format_unsupported"),
|
||||
}
|
||||
None => Some("transport_api_format_unsupported"),
|
||||
}
|
||||
};
|
||||
};
|
||||
if let Some(skip_reason) = transport_unsupported_reason {
|
||||
mark_skipped_local_openai_responses_candidate(
|
||||
state,
|
||||
|
||||
@@ -90,25 +90,28 @@ pub(crate) use aether_provider_transport::{
|
||||
openai_image_transport_unsupported_reason, request_conversion_direct_auth,
|
||||
request_conversion_enabled_for_transport, request_conversion_transport_supported,
|
||||
request_conversion_transport_unsupported_reason, request_pair_allowed_for_transport,
|
||||
request_pair_direct_auth, request_pair_transport_unsupported_reason, resolve_gemini_files_auth,
|
||||
resolve_grok_session_auth, resolve_openai_image_auth, resolve_same_format_provider_direct_auth,
|
||||
resolve_transport_execution_timeouts, resolve_transport_profile,
|
||||
resolve_transport_proxy_snapshot, resolve_transport_proxy_snapshot_with_tunnel_affinity,
|
||||
resolve_video_create_auth, same_format_provider_transport_supported,
|
||||
same_format_provider_transport_unsupported_reason, should_skip_upstream_passthrough_header,
|
||||
should_try_same_format_provider_oauth_auth, supports_local_gemini_transport_with_network,
|
||||
request_pair_direct_auth, request_pair_transport_unsupported_reason,
|
||||
resolve_gemini_cli_project_id, resolve_gemini_files_auth, resolve_grok_session_auth,
|
||||
resolve_local_gemini_cli_request_auth, resolve_openai_image_auth,
|
||||
resolve_same_format_provider_direct_auth, resolve_transport_execution_timeouts,
|
||||
resolve_transport_profile, resolve_transport_proxy_snapshot,
|
||||
resolve_transport_proxy_snapshot_with_tunnel_affinity, resolve_video_create_auth,
|
||||
same_format_provider_transport_supported, same_format_provider_transport_unsupported_reason,
|
||||
should_skip_upstream_passthrough_header, should_try_same_format_provider_oauth_auth,
|
||||
supports_local_gemini_transport_with_network,
|
||||
supports_local_generic_oauth_request_auth_resolution,
|
||||
supports_local_oauth_request_auth_resolution, transport_proxy_is_locally_supported,
|
||||
video_create_transport_unsupported_reason, CandidateTransportPolicyFacts,
|
||||
GatewayProviderTransportSnapshot, GeminiCliRequestEnvelopeSupport, GeminiFilesHeadersInput,
|
||||
GeminiFilesRequestBodyError, GeminiFilesRequestBodyParts, GrokHeaderInput,
|
||||
LocalResolvedOAuthRequestAuth, ProviderOpenAiImageHeadersInput, ProviderVideoCreateFamily,
|
||||
ProviderVideoCreateHeadersInput, 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,
|
||||
GatewayProviderTransportSnapshot, GeminiCliRequestAuth, GeminiCliRequestAuthSupport,
|
||||
GeminiCliRequestAuthUnsupportedReason, GeminiCliRequestEnvelopeSupport,
|
||||
GeminiFilesHeadersInput, GeminiFilesRequestBodyError, GeminiFilesRequestBodyParts,
|
||||
GrokHeaderInput, LocalResolvedOAuthRequestAuth, ProviderOpenAiImageHeadersInput,
|
||||
ProviderVideoCreateFamily, ProviderVideoCreateHeadersInput, 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,
|
||||
};
|
||||
|
||||
@@ -2972,28 +2972,41 @@ async fn provider_query_execute_standard_test_candidate(
|
||||
if crate::provider_transport::is_gemini_cli_provider_transport(&transport)
|
||||
&& normalized_provider_api_format == "gemini:generate_content"
|
||||
{
|
||||
let project_id = match crate::provider_transport::resolve_gemini_cli_project_id(&transport)
|
||||
{
|
||||
Some(project_id) => Some(project_id),
|
||||
None => state
|
||||
let mut gemini_cli_auth =
|
||||
match crate::provider_transport::resolve_local_gemini_cli_request_auth(&transport) {
|
||||
crate::provider_transport::GeminiCliRequestAuthSupport::Supported(auth) => auth,
|
||||
crate::provider_transport::GeminiCliRequestAuthSupport::Unsupported(_) => {
|
||||
crate::provider_transport::GeminiCliRequestAuth::default()
|
||||
}
|
||||
};
|
||||
if gemini_cli_auth.project_id.is_none() {
|
||||
gemini_cli_auth = state
|
||||
.app()
|
||||
.hydrate_gemini_cli_project_metadata_for_transport(&transport)
|
||||
.await
|
||||
.and_then(|hydrated| {
|
||||
let project_id =
|
||||
crate::provider_transport::resolve_gemini_cli_project_id(&hydrated);
|
||||
transport = hydrated;
|
||||
project_id
|
||||
}),
|
||||
};
|
||||
let Some(project_id) = project_id else {
|
||||
match crate::provider_transport::resolve_local_gemini_cli_request_auth(
|
||||
&transport,
|
||||
) {
|
||||
crate::provider_transport::GeminiCliRequestAuthSupport::Supported(auth) => {
|
||||
Some(auth)
|
||||
}
|
||||
crate::provider_transport::GeminiCliRequestAuthSupport::Unsupported(_) => {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap_or_default();
|
||||
}
|
||||
if gemini_cli_auth.project_id.is_none() {
|
||||
return Ok(provider_query_skipped_execution_outcome(
|
||||
provider_request_body,
|
||||
"Gemini CLI project_id is unavailable for v1internal request",
|
||||
));
|
||||
};
|
||||
}
|
||||
provider_request_body = match crate::provider_transport::build_gemini_cli_v1internal_request(
|
||||
project_id.as_str(),
|
||||
&gemini_cli_auth,
|
||||
trace_id,
|
||||
request_model,
|
||||
&provider_request_body,
|
||||
|
||||
@@ -452,10 +452,10 @@ async fn gateway_executes_gemini_cli_stream_via_local_decision_gate_after_oauth_
|
||||
struct SeenExecutionRuntimeStreamRequest {
|
||||
trace_id: String,
|
||||
url: String,
|
||||
has_model_field: bool,
|
||||
project: String,
|
||||
outer_model: String,
|
||||
user_prompt_id: String,
|
||||
envelope_model: String,
|
||||
inner_model_present: bool,
|
||||
accept: String,
|
||||
authorization: String,
|
||||
exact_temperature: f64,
|
||||
@@ -587,7 +587,7 @@ async fn gateway_executes_gemini_cli_stream_via_local_decision_gate_after_oauth_
|
||||
{"action":"drop","path":"toolConfig"}
|
||||
])),
|
||||
Some(2),
|
||||
Some("/custom/v1beta/models/gemini-cli-upstream:streamGenerateContent".to_string()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
@@ -733,15 +733,10 @@ async fn gateway_executes_gemini_cli_stream_via_local_decision_gate_after_oauth_
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
has_model_field: payload
|
||||
outer_model: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("model"))
|
||||
.is_some(),
|
||||
project: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("project"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
@@ -752,10 +747,16 @@ async fn gateway_executes_gemini_cli_stream_via_local_decision_gate_after_oauth_
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
envelope_model: payload
|
||||
inner_model_present: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("request"))
|
||||
.and_then(|value| value.get("model"))
|
||||
.is_some(),
|
||||
project: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("project"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
@@ -939,19 +940,19 @@ async fn gateway_executes_gemini_cli_stream_via_local_decision_gate_after_oauth_
|
||||
seen_execution_runtime_request.url,
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse"
|
||||
);
|
||||
assert!(seen_execution_runtime_request.has_model_field);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.project,
|
||||
"gemini-cli-project-1"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.outer_model,
|
||||
"gemini-cli-upstream"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.user_prompt_id,
|
||||
"trace-gemini-cli-oauth-local-stream-123"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.envelope_model,
|
||||
"gemini-cli-upstream"
|
||||
);
|
||||
assert!(!seen_execution_runtime_request.inner_model_present);
|
||||
assert_eq!(seen_execution_runtime_request.accept, "text/event-stream");
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.authorization,
|
||||
|
||||
@@ -2010,7 +2010,9 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_cli_syn
|
||||
struct SeenExecutionRuntimeSyncRequest {
|
||||
trace_id: String,
|
||||
url: String,
|
||||
model: String,
|
||||
outer_model: String,
|
||||
user_prompt_id: String,
|
||||
inner_model_present: bool,
|
||||
auth_header_value: String,
|
||||
endpoint_tag: String,
|
||||
has_contents: bool,
|
||||
@@ -2031,7 +2033,7 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_cli_syn
|
||||
"local".to_string(),
|
||||
true,
|
||||
false,
|
||||
Some(serde_json::json!(["openai", "gemini"])),
|
||||
Some(serde_json::json!(["openai", "gemini", "gemini_cli"])),
|
||||
Some(serde_json::json!(["openai:chat"])),
|
||||
Some(serde_json::json!(["gpt-5"])),
|
||||
api_key_id.to_string(),
|
||||
@@ -2042,7 +2044,7 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_cli_syn
|
||||
Some(60),
|
||||
Some(5),
|
||||
Some(4_102_444_800),
|
||||
Some(serde_json::json!(["openai", "gemini"])),
|
||||
Some(serde_json::json!(["openai", "gemini", "gemini_cli"])),
|
||||
Some(serde_json::json!(["openai:chat"])),
|
||||
Some(serde_json::json!(["gpt-5"])),
|
||||
)
|
||||
@@ -2052,8 +2054,8 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_cli_syn
|
||||
fn sample_candidate_row() -> StoredMinimalCandidateSelectionRow {
|
||||
StoredMinimalCandidateSelectionRow {
|
||||
provider_id: "provider-openai-chat-gemini-cli-local-1".to_string(),
|
||||
provider_name: "gemini".to_string(),
|
||||
provider_type: "custom".to_string(),
|
||||
provider_name: "gemini_cli".to_string(),
|
||||
provider_type: "gemini_cli".to_string(),
|
||||
provider_priority: 10,
|
||||
provider_is_active: true,
|
||||
endpoint_id: "endpoint-openai-chat-gemini-cli-local-1".to_string(),
|
||||
@@ -2062,8 +2064,8 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_cli_syn
|
||||
endpoint_kind: Some("cli".to_string()),
|
||||
endpoint_is_active: true,
|
||||
key_id: "key-openai-chat-gemini-cli-local-1".to_string(),
|
||||
key_name: "prod".to_string(),
|
||||
key_auth_type: "bearer".to_string(),
|
||||
key_name: "oauth".to_string(),
|
||||
key_auth_type: "oauth".to_string(),
|
||||
key_is_active: true,
|
||||
key_api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
key_allowed_models: None,
|
||||
@@ -2091,9 +2093,9 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_cli_syn
|
||||
fn sample_provider_catalog_provider() -> StoredProviderCatalogProvider {
|
||||
StoredProviderCatalogProvider::new(
|
||||
"provider-openai-chat-gemini-cli-local-1".to_string(),
|
||||
"gemini".to_string(),
|
||||
"gemini_cli".to_string(),
|
||||
Some("https://example.com".to_string()),
|
||||
"custom".to_string(),
|
||||
"gemini_cli".to_string(),
|
||||
)
|
||||
.expect("provider should build")
|
||||
.with_transport_fields(
|
||||
@@ -2120,13 +2122,13 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_cli_syn
|
||||
)
|
||||
.expect("endpoint should build")
|
||||
.with_transport_fields(
|
||||
"https://generativelanguage.googleapis.com".to_string(),
|
||||
"https://cloudcode-pa.googleapis.com".to_string(),
|
||||
Some(serde_json::json!([
|
||||
{"action":"set","key":"x-endpoint-tag","value":"openai-chat-gemini-cli-cross-format"}
|
||||
])),
|
||||
None,
|
||||
Some(2),
|
||||
Some("/custom/v1beta/models/gemini-cli-upstream:generateContent".to_string()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
@@ -2138,8 +2140,8 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_cli_syn
|
||||
StoredProviderCatalogKey::new(
|
||||
"key-openai-chat-gemini-cli-local-1".to_string(),
|
||||
"provider-openai-chat-gemini-cli-local-1".to_string(),
|
||||
"prod".to_string(),
|
||||
"bearer".to_string(),
|
||||
"oauth".to_string(),
|
||||
"oauth".to_string(),
|
||||
None,
|
||||
true,
|
||||
)
|
||||
@@ -2241,13 +2243,26 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_cli_syn
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
model: payload
|
||||
outer_model: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("model"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
user_prompt_id: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("user_prompt_id"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
inner_model_present: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("request"))
|
||||
.and_then(|value| value.get("model"))
|
||||
.is_some(),
|
||||
auth_header_value: payload
|
||||
.get("headers")
|
||||
.and_then(|value| value.get("authorization"))
|
||||
@@ -2263,6 +2278,7 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_cli_syn
|
||||
has_contents: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("request"))
|
||||
.and_then(|value| value.get("contents"))
|
||||
.is_some(),
|
||||
});
|
||||
@@ -2336,15 +2352,20 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_cli_syn
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::TOO_MANY_REQUESTS);
|
||||
let response_status = response.status();
|
||||
let execution_path = response
|
||||
.headers()
|
||||
.get(EXECUTION_PATH_HEADER)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.map(str::to_owned);
|
||||
let response_body = response.text().await.expect("body should read");
|
||||
assert_eq!(
|
||||
response
|
||||
.headers()
|
||||
.get(EXECUTION_PATH_HEADER)
|
||||
.and_then(|value| value.to_str().ok()),
|
||||
execution_path.as_deref(),
|
||||
Some(EXECUTION_PATH_EXECUTION_RUNTIME_SYNC)
|
||||
);
|
||||
let response_json: serde_json::Value = response.json().await.expect("body should parse");
|
||||
assert_eq!(response_status, StatusCode::TOO_MANY_REQUESTS);
|
||||
let response_json: serde_json::Value =
|
||||
serde_json::from_str(&response_body).expect("body should parse");
|
||||
assert_eq!(
|
||||
response_json,
|
||||
json!({
|
||||
@@ -2367,9 +2388,17 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_cli_syn
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.url,
|
||||
"https://generativelanguage.googleapis.com/custom/v1beta/models/gemini-cli-upstream:generateContent"
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:generateContent"
|
||||
);
|
||||
assert_eq!(seen_execution_runtime_request.model, "gemini-cli-upstream");
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.outer_model,
|
||||
"gemini-cli-upstream"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.user_prompt_id,
|
||||
"trace-openai-chat-gemini-cli-local-error-123"
|
||||
);
|
||||
assert!(!seen_execution_runtime_request.inner_model_present);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.auth_header_value,
|
||||
"Bearer sk-upstream-openai-chat-gemini-cli"
|
||||
|
||||
@@ -1572,7 +1572,7 @@ async fn gateway_returns_openai_responses_error_for_local_sync_failure() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_sync_failure() {
|
||||
async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_cli_sync_failure() {
|
||||
#[derive(Debug, Clone)]
|
||||
struct SeenExecutionRuntimeSyncRequest {
|
||||
trace_id: String,
|
||||
@@ -1580,7 +1580,9 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_sy
|
||||
authorization: String,
|
||||
endpoint_tag: String,
|
||||
has_contents: bool,
|
||||
model: String,
|
||||
outer_model: String,
|
||||
user_prompt_id: String,
|
||||
inner_model_present: bool,
|
||||
}
|
||||
|
||||
fn hash_api_key(value: &str) -> String {
|
||||
@@ -1598,7 +1600,7 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_sy
|
||||
"local".to_string(),
|
||||
true,
|
||||
false,
|
||||
Some(serde_json::json!(["openai", "gemini"])),
|
||||
Some(serde_json::json!(["openai", "gemini", "gemini_cli"])),
|
||||
Some(serde_json::json!(["openai:responses"])),
|
||||
Some(serde_json::json!(["gpt-5"])),
|
||||
api_key_id.to_string(),
|
||||
@@ -1609,7 +1611,7 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_sy
|
||||
Some(60),
|
||||
Some(5),
|
||||
Some(4_102_444_800_i64),
|
||||
Some(serde_json::json!(["openai", "gemini"])),
|
||||
Some(serde_json::json!(["openai", "gemini", "gemini_cli"])),
|
||||
Some(serde_json::json!(["openai:responses"])),
|
||||
Some(serde_json::json!(["gpt-5"])),
|
||||
)
|
||||
@@ -1619,8 +1621,8 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_sy
|
||||
fn sample_candidate_row() -> StoredMinimalCandidateSelectionRow {
|
||||
StoredMinimalCandidateSelectionRow {
|
||||
provider_id: "provider-openai-cli-gemini-local-1".to_string(),
|
||||
provider_name: "gemini".to_string(),
|
||||
provider_type: "custom".to_string(),
|
||||
provider_name: "gemini_cli".to_string(),
|
||||
provider_type: "gemini_cli".to_string(),
|
||||
provider_priority: 10,
|
||||
provider_is_active: true,
|
||||
endpoint_id: "endpoint-openai-cli-gemini-local-1".to_string(),
|
||||
@@ -1629,8 +1631,8 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_sy
|
||||
endpoint_kind: Some("cli".to_string()),
|
||||
endpoint_is_active: true,
|
||||
key_id: "key-openai-cli-gemini-local-1".to_string(),
|
||||
key_name: "prod".to_string(),
|
||||
key_auth_type: "bearer".to_string(),
|
||||
key_name: "oauth".to_string(),
|
||||
key_auth_type: "oauth".to_string(),
|
||||
key_is_active: true,
|
||||
key_api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
key_allowed_models: None,
|
||||
@@ -1658,9 +1660,9 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_sy
|
||||
fn sample_provider_catalog_provider() -> StoredProviderCatalogProvider {
|
||||
StoredProviderCatalogProvider::new(
|
||||
"provider-openai-cli-gemini-local-1".to_string(),
|
||||
"gemini".to_string(),
|
||||
"gemini_cli".to_string(),
|
||||
Some("https://example.com".to_string()),
|
||||
"custom".to_string(),
|
||||
"gemini_cli".to_string(),
|
||||
)
|
||||
.expect("provider should build")
|
||||
.with_transport_fields(
|
||||
@@ -1687,13 +1689,13 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_sy
|
||||
)
|
||||
.expect("endpoint should build")
|
||||
.with_transport_fields(
|
||||
"https://generativelanguage.googleapis.com".to_string(),
|
||||
"https://cloudcode-pa.googleapis.com".to_string(),
|
||||
Some(serde_json::json!([
|
||||
{"action":"set","key":"x-endpoint-tag","value":"openai-cli-gemini-cross-format"}
|
||||
])),
|
||||
None,
|
||||
Some(2),
|
||||
Some("/custom/v1beta/models/gemini-cli-upstream:generateContent".to_string()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
@@ -1705,8 +1707,8 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_sy
|
||||
StoredProviderCatalogKey::new(
|
||||
"key-openai-cli-gemini-local-1".to_string(),
|
||||
"provider-openai-cli-gemini-local-1".to_string(),
|
||||
"prod".to_string(),
|
||||
"bearer".to_string(),
|
||||
"oauth".to_string(),
|
||||
"oauth".to_string(),
|
||||
None,
|
||||
true,
|
||||
)
|
||||
@@ -1806,15 +1808,29 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_sy
|
||||
has_contents: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("request"))
|
||||
.and_then(|value| value.get("contents"))
|
||||
.is_some(),
|
||||
model: payload
|
||||
outer_model: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("model"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
user_prompt_id: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("user_prompt_id"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
inner_model_present: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("request"))
|
||||
.and_then(|value| value.get("model"))
|
||||
.is_some(),
|
||||
});
|
||||
Json(json!({
|
||||
"request_id": "trace-openai-cli-gemini-local-error-123",
|
||||
@@ -1917,7 +1933,7 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_sy
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.url,
|
||||
"https://generativelanguage.googleapis.com/custom/v1beta/models/gemini-cli-upstream:generateContent"
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:generateContent"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.authorization,
|
||||
@@ -1928,7 +1944,15 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_sy
|
||||
"openai-cli-gemini-cross-format"
|
||||
);
|
||||
assert!(seen_execution_runtime_request.has_contents);
|
||||
assert_eq!(seen_execution_runtime_request.model, "gemini-cli-upstream");
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.outer_model,
|
||||
"gemini-cli-upstream"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.user_prompt_id,
|
||||
"trace-openai-cli-gemini-local-error-123"
|
||||
);
|
||||
assert!(!seen_execution_runtime_request.inner_model_present);
|
||||
|
||||
let stored_candidates = request_candidate_repository
|
||||
.list_by_request_id("trace-openai-cli-gemini-local-error-123")
|
||||
|
||||
@@ -779,10 +779,10 @@ async fn gateway_executes_gemini_cli_sync_via_local_decision_gate_after_oauth_re
|
||||
struct SeenExecutionRuntimeSyncRequest {
|
||||
trace_id: String,
|
||||
url: String,
|
||||
has_model_field: bool,
|
||||
project: String,
|
||||
outer_model: String,
|
||||
user_prompt_id: String,
|
||||
envelope_model: String,
|
||||
inner_model_present: bool,
|
||||
authorization: String,
|
||||
exact_temperature: f64,
|
||||
endpoint_tag: String,
|
||||
@@ -913,7 +913,7 @@ async fn gateway_executes_gemini_cli_sync_via_local_decision_gate_after_oauth_re
|
||||
{"action":"drop","path":"toolConfig"}
|
||||
])),
|
||||
Some(2),
|
||||
Some("/custom/v1beta/models/gemini-cli-upstream:generateContent".to_string()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
@@ -1060,15 +1060,10 @@ async fn gateway_executes_gemini_cli_sync_via_local_decision_gate_after_oauth_re
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
has_model_field: payload
|
||||
outer_model: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("model"))
|
||||
.is_some(),
|
||||
project: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("project"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
@@ -1079,10 +1074,16 @@ async fn gateway_executes_gemini_cli_sync_via_local_decision_gate_after_oauth_re
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
envelope_model: payload
|
||||
inner_model_present: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("request"))
|
||||
.and_then(|value| value.get("model"))
|
||||
.is_some(),
|
||||
project: payload
|
||||
.get("body")
|
||||
.and_then(|value| value.get("json_body"))
|
||||
.and_then(|value| value.get("project"))
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
@@ -1273,21 +1274,21 @@ async fn gateway_executes_gemini_cli_sync_via_local_decision_gate_after_oauth_re
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.url,
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse"
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:generateContent"
|
||||
);
|
||||
assert!(seen_execution_runtime_request.has_model_field);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.project,
|
||||
"gemini-cli-project-1"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.outer_model,
|
||||
"gemini-cli-upstream"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.user_prompt_id,
|
||||
"trace-gemini-cli-oauth-local-sync-123"
|
||||
);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.envelope_model,
|
||||
"gemini-cli-upstream"
|
||||
);
|
||||
assert!(!seen_execution_runtime_request.inner_model_present);
|
||||
assert_eq!(
|
||||
seen_execution_runtime_request.authorization,
|
||||
"Bearer refreshed-gemini-cli-access-token"
|
||||
|
||||
@@ -5027,10 +5027,10 @@ async fn gateway_handles_gemini_cli_test_model_with_oauth_header_fallback() {
|
||||
assert_eq!(plan.endpoint_id, "endpoint-gemini-cli");
|
||||
assert_eq!(plan.key_id, "key-gemini-cli");
|
||||
assert_eq!(plan.provider_api_format, "gemini:generate_content");
|
||||
assert!(plan.stream);
|
||||
assert!(!plan.stream);
|
||||
assert_eq!(
|
||||
plan.url,
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse"
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:generateContent"
|
||||
);
|
||||
assert_eq!(
|
||||
plan.body.json_body.as_ref().unwrap()["project"],
|
||||
@@ -5184,9 +5184,9 @@ async fn gateway_hydrates_gemini_cli_project_id_from_load_code_assist_for_test_m
|
||||
|
||||
assert_eq!(
|
||||
plan.url,
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse"
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:generateContent"
|
||||
);
|
||||
assert!(plan.stream);
|
||||
assert!(!plan.stream);
|
||||
assert_eq!(
|
||||
plan.body.json_body.as_ref().unwrap()["project"],
|
||||
json!("project-from-load-code-assist")
|
||||
@@ -5286,8 +5286,7 @@ async fn gateway_hydrates_gemini_cli_project_id_from_load_code_assist_for_test_m
|
||||
*seen_urls.lock().expect("mutex should lock"),
|
||||
vec![
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist".to_string(),
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse"
|
||||
.to_string(),
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:generateContent".to_string(),
|
||||
]
|
||||
);
|
||||
let reloaded = provider_catalog_repository
|
||||
@@ -5496,10 +5495,10 @@ async fn gateway_unwraps_gemini_cli_v1internal_response_for_failover_model_test(
|
||||
assert_eq!(plan.endpoint_id, "endpoint-gemini-cli");
|
||||
assert_eq!(plan.key_id, "key-gemini-cli");
|
||||
assert_eq!(plan.provider_api_format, "gemini:generate_content");
|
||||
assert!(plan.stream);
|
||||
assert!(!plan.stream);
|
||||
assert_eq!(
|
||||
plan.url,
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse"
|
||||
"https://cloudcode-pa.googleapis.com/v1internal:generateContent"
|
||||
);
|
||||
assert_eq!(
|
||||
plan.body.json_body.as_ref().unwrap()["project"],
|
||||
@@ -5514,15 +5513,30 @@ async fn gateway_unwraps_gemini_cli_v1internal_response_for_failover_model_test(
|
||||
"candidate_id": plan.candidate_id,
|
||||
"status_code": 200,
|
||||
"headers": {
|
||||
"content-type": "text/event-stream"
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"body": {
|
||||
"body_bytes_b64": base64::engine::general_purpose::STANDARD.encode(
|
||||
concat!(
|
||||
"data: {\"response\":{\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"Gemini CLI v1internal failover response\"}],\"role\":\"model\"},\"finishReason\":\"STOP\",\"index\":0}],\"modelVersion\":\"gemini-3-flash-preview\",\"usageMetadata\":{\"promptTokenCount\":2,\"candidatesTokenCount\":5,\"totalTokenCount\":7}},\"remainingCredits\":123,\"consumedCredits\":1,\"traceId\":\"trace-gemini-cli-1\"}\n\n"
|
||||
)
|
||||
.as_bytes()
|
||||
)
|
||||
"json_body": {
|
||||
"response": {
|
||||
"candidates": [{
|
||||
"content": {
|
||||
"parts": [{"text":"Gemini CLI v1internal failover response"}],
|
||||
"role": "model"
|
||||
},
|
||||
"finishReason": "STOP",
|
||||
"index": 0
|
||||
}],
|
||||
"modelVersion": "gemini-3-flash-preview",
|
||||
"usageMetadata": {
|
||||
"promptTokenCount": 2,
|
||||
"candidatesTokenCount": 5,
|
||||
"totalTokenCount": 7
|
||||
}
|
||||
},
|
||||
"remainingCredits": 123,
|
||||
"consumedCredits": 1,
|
||||
"traceId": "trace-gemini-cli-1"
|
||||
}
|
||||
},
|
||||
"telemetry": {
|
||||
"elapsed_ms": 23
|
||||
|
||||
Reference in New Issue
Block a user