Merge remote-tracking branch 'origin/pr/591'

This commit is contained in:
fawney19
2026-05-28 15:23:26 +08:00
6 changed files with 127 additions and 10 deletions
@@ -31,6 +31,10 @@ pub(super) fn openai_chat_upstream_is_stream_for_candidate(
crate::ai_serving::transport::kiro::is_kiro_claude_messages_transport(
transport,
provider_api_format,
) || openai_chat_gemini_cli_client_stream_requires_upstream_streaming(
transport,
provider_api_format,
client_is_stream,
);
resolve_upstream_is_stream_for_provider(
transport.endpoint.config.as_ref(),
@@ -41,6 +45,18 @@ pub(super) fn openai_chat_upstream_is_stream_for_candidate(
)
}
fn openai_chat_gemini_cli_client_stream_requires_upstream_streaming(
transport: &GatewayProviderTransportSnapshot,
provider_api_format: &str,
client_is_stream: bool,
) -> bool {
crate::ai_serving::transport::gemini_cli::is_gemini_cli_provider_transport(transport)
&& crate::ai_serving::transport::gemini_cli::gemini_cli_v1internal_requires_upstream_streaming(
provider_api_format,
client_is_stream,
)
}
#[cfg(test)]
mod tests {
use super::openai_chat_upstream_is_stream_for_candidate;
@@ -165,4 +181,24 @@ mod tests {
false,
));
}
#[test]
fn openai_chat_policy_resolver_preserves_gemini_cli_streaming_requests() {
let gemini_cli = sample_transport(
"gemini_cli",
"gemini:generate_content",
Some(json!({"upstream_stream_policy": "force_non_stream"})),
);
assert!(openai_chat_upstream_is_stream_for_candidate(
&gemini_cli,
"gemini:generate_content",
true,
));
assert!(!openai_chat_upstream_is_stream_for_candidate(
&gemini_cli,
"gemini:generate_content",
false,
));
}
}
@@ -1,4 +1,5 @@
mod auth;
mod policy;
mod request;
mod url;
@@ -7,6 +8,7 @@ pub use auth::{
resolve_local_gemini_cli_request_auth, GeminiCliRequestAuth, GeminiCliRequestAuthSupport,
GeminiCliRequestAuthUnsupportedReason, GEMINI_CLI_PROVIDER_TYPE,
};
pub use policy::gemini_cli_v1internal_requires_upstream_streaming;
pub use request::{
build_gemini_cli_v1internal_request, classify_gemini_cli_v1internal_request_body,
GeminiCliRequestEnvelopeSupport, GeminiCliRequestEnvelopeUnsupportedReason,
@@ -0,0 +1,29 @@
pub fn gemini_cli_v1internal_requires_upstream_streaming(
provider_api_format: &str,
client_requires_streaming: bool,
) -> bool {
client_requires_streaming
&& aether_ai_formats::normalize_api_format_alias(provider_api_format)
== "gemini:generate_content"
}
#[cfg(test)]
mod tests {
use super::gemini_cli_v1internal_requires_upstream_streaming;
#[test]
fn v1internal_generate_content_requires_upstream_streaming_for_stream_clients() {
assert!(gemini_cli_v1internal_requires_upstream_streaming(
"gemini:generate_content",
true,
));
assert!(!gemini_cli_v1internal_requires_upstream_streaming(
"gemini:generate_content",
false,
));
assert!(!gemini_cli_v1internal_requires_upstream_streaming(
"openai:chat",
true,
));
}
}
+7 -7
View File
@@ -43,13 +43,13 @@ pub use diagnostics::{
};
pub use gemini_cli::{
build_gemini_cli_v1internal_request, build_gemini_cli_v1internal_url,
classify_gemini_cli_v1internal_request_body, is_gemini_cli_provider_transport,
resolve_gemini_cli_project_id, resolve_local_gemini_cli_request_auth, GeminiCliRequestAuth,
GeminiCliRequestAuthSupport, GeminiCliRequestAuthUnsupportedReason,
GeminiCliRequestEnvelopeSupport, GeminiCliRequestEnvelopeUnsupportedReason,
GeminiCliRequestUrlAction, GEMINI_CLI_PROVIDER_TYPE, GEMINI_CLI_RETRIEVE_USER_QUOTA_PATH,
GEMINI_CLI_USER_AGENT, GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME,
GEMINI_CLI_V1INTERNAL_PATH_TEMPLATE,
classify_gemini_cli_v1internal_request_body, gemini_cli_v1internal_requires_upstream_streaming,
is_gemini_cli_provider_transport, resolve_gemini_cli_project_id,
resolve_local_gemini_cli_request_auth, GeminiCliRequestAuth, GeminiCliRequestAuthSupport,
GeminiCliRequestAuthUnsupportedReason, GeminiCliRequestEnvelopeSupport,
GeminiCliRequestEnvelopeUnsupportedReason, GeminiCliRequestUrlAction, GEMINI_CLI_PROVIDER_TYPE,
GEMINI_CLI_RETRIEVE_USER_QUOTA_PATH, GEMINI_CLI_USER_AGENT,
GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME, GEMINI_CLI_V1INTERNAL_PATH_TEMPLATE,
};
pub use gemini_files::{
build_gemini_files_headers, build_gemini_files_request_body, build_gemini_files_upstream_url,
@@ -165,6 +165,11 @@ const FORCE_STREAM_ENDPOINT_CONFIG_DEFAULTS: &[FixedProviderEndpointConfigDefaul
key: "upstream_stream_policy",
value: FixedProviderEndpointConfigValue::String("force_stream"),
}];
const AUTO_STREAM_ENDPOINT_CONFIG_DEFAULTS: &[FixedProviderEndpointConfigDefault] =
&[FixedProviderEndpointConfigDefault {
key: "upstream_stream_policy",
value: FixedProviderEndpointConfigValue::String("auto"),
}];
const STANDARD_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy::standard();
const CUSTOM_RUNTIME_POLICY: ProviderRuntimePolicy = ProviderRuntimePolicy {
@@ -332,13 +337,13 @@ const KIRO_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTemplat
const GEMINI_CLI_FIXED_PROVIDER_TEMPLATE: FixedProviderTemplate = FixedProviderTemplate {
provider_type: "gemini_cli",
version: 2,
version: 3,
base_url: "https://cloudcode-pa.googleapis.com",
endpoints: &[FixedProviderEndpointTemplate {
item_key: "gemini:generate_content",
api_format: "gemini:generate_content",
custom_path: Some("/v1internal:{action}"),
config_defaults: EMPTY_ENDPOINT_CONFIG_DEFAULTS,
config_defaults: AUTO_STREAM_ENDPOINT_CONFIG_DEFAULTS,
}],
runtime_policy: GEMINI_CLI_RUNTIME_POLICY,
};
@@ -718,12 +723,23 @@ mod tests {
let template =
fixed_provider_template("gemini_cli").expect("gemini_cli template should exist");
assert_eq!(template.base_url, "https://cloudcode-pa.googleapis.com");
assert_eq!(template.version, 2);
assert_eq!(template.version, 3);
let endpoint =
fixed_provider_endpoint_template_by_api_format("gemini_cli", "gemini:generate_content")
.expect("gemini_cli generateContent endpoint should exist");
assert_eq!(endpoint.custom_path, Some("/v1internal:{action}"));
assert_eq!(
endpoint
.config_defaults
.iter()
.map(|item| (item.key, item.value))
.collect::<Vec<_>>(),
vec![(
"upstream_stream_policy",
FixedProviderEndpointConfigValue::String("auto")
)]
);
}
#[test]
@@ -113,11 +113,17 @@ pub fn classify_same_format_provider_request_behavior(
.eq_ignore_ascii_case("claude_code");
let is_vertex = is_vertex_transport_context(transport);
let is_kiro = is_kiro_provider_transport(transport);
let gemini_cli_requires_upstream_streaming = is_gemini_cli
&& crate::gemini_cli::gemini_cli_v1internal_requires_upstream_streaming(
params.provider_api_format,
params.require_streaming,
);
let upstream_is_stream = aether_ai_formats::resolve_upstream_is_stream_from_endpoint_config(
transport.endpoint.config.as_ref(),
params.require_streaming,
is_kiro
|| is_antigravity
|| gemini_cli_requires_upstream_streaming
|| aether_ai_formats::api::force_upstream_streaming_for_provider(
transport.provider.provider_type.as_str(),
params.provider_api_format,
@@ -647,6 +653,34 @@ mod tests {
assert!(behavior.upstream_is_stream);
}
#[test]
fn same_format_behavior_preserves_gemini_cli_streaming_requests() {
let mut gemini_cli = sample_transport("gemini_cli");
gemini_cli.endpoint.config = Some(json!({
"upstream_stream_policy": "force_non_stream"
}));
let stream_behavior = classify_same_format_provider_request_behavior(
&gemini_cli,
SameFormatProviderRequestBehaviorParams {
require_streaming: true,
provider_api_format: "gemini:generate_content",
report_kind: "gemini_cli_stream_success",
},
);
assert!(stream_behavior.upstream_is_stream);
let sync_behavior = classify_same_format_provider_request_behavior(
&gemini_cli,
SameFormatProviderRequestBehaviorParams {
require_streaming: false,
provider_api_format: "gemini:generate_content",
report_kind: "gemini_cli_sync_success",
},
);
assert!(!sync_behavior.upstream_is_stream);
}
#[test]
fn same_format_policy_resolution_drives_standard_body_stream_field() {
for (endpoint_config, client_is_stream, expected_stream) in [