mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
Normalize canonical API formats
This commit is contained in:
@@ -25,7 +25,6 @@ pub fn provider_private_response_allows_sync_finalize(report_context: &Value) ->
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or_default();
|
||||
provider_adaptation_allows_sync_finalize_envelope(envelope_name, provider_api_format)
|
||||
|| matches!(envelope_name, "claude:cli")
|
||||
}
|
||||
|
||||
pub fn normalize_provider_private_report_context(report_context: Option<&Value>) -> Option<Value> {
|
||||
@@ -63,7 +62,7 @@ pub fn normalize_provider_private_response_value(
|
||||
return Some(data);
|
||||
}
|
||||
let mut unwrapped = match report_context.get("envelope_name").and_then(Value::as_str) {
|
||||
Some("claude:cli") | Some(KIRO_ENVELOPE_NAME) => data,
|
||||
Some(KIRO_ENVELOPE_NAME) => data,
|
||||
Some(GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME) => {
|
||||
if let Some(response) = data
|
||||
.get("response")
|
||||
@@ -345,7 +344,7 @@ mod tests {
|
||||
let report_context = json!({
|
||||
"has_envelope": true,
|
||||
"envelope_name": "antigravity:v1internal",
|
||||
"provider_api_format": "gemini:cli",
|
||||
"provider_api_format": "gemini:generate_content",
|
||||
});
|
||||
let normalized = normalize_provider_private_report_context(Some(&report_context))
|
||||
.expect("context should normalize");
|
||||
@@ -357,7 +356,7 @@ mod tests {
|
||||
fn unwraps_antigravity_sync_response_and_injects_ids() {
|
||||
let report_context = json!({
|
||||
"has_envelope": true,
|
||||
"provider_api_format": "gemini:cli",
|
||||
"provider_api_format": "gemini:generate_content",
|
||||
"envelope_name": "antigravity:v1internal",
|
||||
"mapped_model": "claude-sonnet-4-5",
|
||||
});
|
||||
@@ -390,8 +389,8 @@ mod tests {
|
||||
fn unwraps_antigravity_stream_line_and_injects_ids() {
|
||||
let report_context = json!({
|
||||
"has_envelope": true,
|
||||
"provider_api_format": "gemini:cli",
|
||||
"client_api_format": "gemini:cli",
|
||||
"provider_api_format": "gemini:generate_content",
|
||||
"client_api_format": "gemini:generate_content",
|
||||
"envelope_name": "antigravity:v1internal",
|
||||
"mapped_model": "claude-sonnet-4-5",
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@ const PROVIDER_ADAPTATION_SURFACES: &[ProviderAdaptationDescriptor] = &[
|
||||
surface: ProviderAdaptationSurface::AntigravityGeminiChat,
|
||||
provider_type: Some(ANTIGRAVITY_PROVIDER_TYPE),
|
||||
envelope_name: ANTIGRAVITY_V1INTERNAL_ENVELOPE_NAME,
|
||||
anchor_api_format: "gemini:chat",
|
||||
anchor_api_format: "gemini:generate_content",
|
||||
supports_request_bridge: true,
|
||||
supports_sync_finalize_bridge: true,
|
||||
supports_stream_bridge: true,
|
||||
@@ -41,7 +41,7 @@ const PROVIDER_ADAPTATION_SURFACES: &[ProviderAdaptationDescriptor] = &[
|
||||
surface: ProviderAdaptationSurface::AntigravityGeminiCli,
|
||||
provider_type: Some(ANTIGRAVITY_PROVIDER_TYPE),
|
||||
envelope_name: ANTIGRAVITY_V1INTERNAL_ENVELOPE_NAME,
|
||||
anchor_api_format: "gemini:cli",
|
||||
anchor_api_format: "gemini:generate_content",
|
||||
supports_request_bridge: true,
|
||||
supports_sync_finalize_bridge: true,
|
||||
supports_stream_bridge: true,
|
||||
@@ -52,7 +52,7 @@ const PROVIDER_ADAPTATION_SURFACES: &[ProviderAdaptationDescriptor] = &[
|
||||
surface: ProviderAdaptationSurface::GeminiCliV1Internal,
|
||||
provider_type: None,
|
||||
envelope_name: GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME,
|
||||
anchor_api_format: "gemini:cli",
|
||||
anchor_api_format: "gemini:generate_content",
|
||||
supports_request_bridge: false,
|
||||
supports_sync_finalize_bridge: true,
|
||||
supports_stream_bridge: true,
|
||||
@@ -63,7 +63,7 @@ const PROVIDER_ADAPTATION_SURFACES: &[ProviderAdaptationDescriptor] = &[
|
||||
surface: ProviderAdaptationSurface::KiroClaudeCli,
|
||||
provider_type: Some(KIRO_PROVIDER_TYPE),
|
||||
envelope_name: KIRO_ENVELOPE_NAME,
|
||||
anchor_api_format: "claude:cli",
|
||||
anchor_api_format: "claude:messages",
|
||||
supports_request_bridge: true,
|
||||
supports_sync_finalize_bridge: true,
|
||||
supports_stream_bridge: true,
|
||||
@@ -149,20 +149,20 @@ mod tests {
|
||||
assert_eq!(
|
||||
provider_adaptation_anchor_api_format(
|
||||
ANTIGRAVITY_V1INTERNAL_ENVELOPE_NAME,
|
||||
"gemini:cli"
|
||||
"gemini:generate_content"
|
||||
),
|
||||
Some("gemini:cli")
|
||||
Some("gemini:generate_content")
|
||||
);
|
||||
assert_eq!(
|
||||
provider_adaptation_anchor_api_format(
|
||||
GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME,
|
||||
"gemini:cli"
|
||||
"gemini:generate_content"
|
||||
),
|
||||
Some("gemini:cli")
|
||||
Some("gemini:generate_content")
|
||||
);
|
||||
assert_eq!(
|
||||
provider_adaptation_anchor_api_format(KIRO_ENVELOPE_NAME, "claude:cli"),
|
||||
Some("claude:cli")
|
||||
provider_adaptation_anchor_api_format(KIRO_ENVELOPE_NAME, "claude:messages"),
|
||||
Some("claude:messages")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -170,19 +170,19 @@ mod tests {
|
||||
fn exposes_private_surface_capabilities() {
|
||||
assert!(provider_adaptation_allows_sync_finalize_envelope(
|
||||
ANTIGRAVITY_V1INTERNAL_ENVELOPE_NAME,
|
||||
"gemini:chat"
|
||||
"gemini:generate_content"
|
||||
));
|
||||
assert!(provider_adaptation_should_unwrap_stream_envelope(
|
||||
GEMINI_CLI_V1INTERNAL_ENVELOPE_NAME,
|
||||
"gemini:cli"
|
||||
"gemini:generate_content"
|
||||
));
|
||||
assert!(provider_adaptation_requires_eventstream_accept(
|
||||
Some(KIRO_ENVELOPE_NAME),
|
||||
"claude:cli"
|
||||
"claude:messages"
|
||||
));
|
||||
assert!(!provider_adaptation_requires_eventstream_accept(
|
||||
Some(ANTIGRAVITY_V1INTERNAL_ENVELOPE_NAME),
|
||||
"gemini:cli"
|
||||
"gemini:generate_content"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +190,6 @@ pub use crate::planner::standard::{
|
||||
CODEX_OPENAI_IMAGE_INTERNAL_MODEL,
|
||||
};
|
||||
pub use aether_ai_formats::{
|
||||
is_openai_responses_compact_format, is_openai_responses_family_format,
|
||||
is_openai_responses_format, legacy_openai_format_alias_matches,
|
||||
normalize_legacy_openai_format_alias, openai_format_storage_aliases,
|
||||
api_format_alias_matches, api_format_storage_aliases, is_openai_responses_compact_format,
|
||||
is_openai_responses_family_format, is_openai_responses_format, normalize_api_format_alias,
|
||||
};
|
||||
|
||||
@@ -67,15 +67,15 @@ pub fn implicit_sync_finalize_report_kind(plan_kind: &str) -> Option<&'static st
|
||||
pub fn core_error_default_client_api_format(report_kind: &str) -> Option<&'static str> {
|
||||
match report_kind {
|
||||
OPENAI_CHAT_SYNC_FINALIZE_REPORT_KIND => Some("openai:chat"),
|
||||
CLAUDE_CHAT_SYNC_FINALIZE_REPORT_KIND => Some("claude:chat"),
|
||||
GEMINI_CHAT_SYNC_FINALIZE_REPORT_KIND => Some("gemini:chat"),
|
||||
CLAUDE_CHAT_SYNC_FINALIZE_REPORT_KIND => Some("claude:messages"),
|
||||
GEMINI_CHAT_SYNC_FINALIZE_REPORT_KIND => Some("gemini:generate_content"),
|
||||
OPENAI_RESPONSES_SYNC_FINALIZE_REPORT_KIND => Some("openai:responses"),
|
||||
OPENAI_RESPONSES_COMPACT_SYNC_FINALIZE_REPORT_KIND => Some("openai:responses:compact"),
|
||||
LEGACY_OPENAI_CLI_SYNC_FINALIZE_REPORT_KIND => Some("openai:responses"),
|
||||
LEGACY_OPENAI_COMPACT_SYNC_FINALIZE_REPORT_KIND => Some("openai:responses:compact"),
|
||||
OPENAI_IMAGE_SYNC_FINALIZE_REPORT_KIND => Some("openai:image"),
|
||||
CLAUDE_CLI_SYNC_FINALIZE_REPORT_KIND => Some("claude:cli"),
|
||||
GEMINI_CLI_SYNC_FINALIZE_REPORT_KIND => Some("gemini:cli"),
|
||||
CLAUDE_CLI_SYNC_FINALIZE_REPORT_KIND => Some("claude:messages"),
|
||||
GEMINI_CLI_SYNC_FINALIZE_REPORT_KIND => Some("gemini:generate_content"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ pub fn build_core_error_body_for_client_format(
|
||||
let mut error_object = Map::new();
|
||||
error_object.insert("message".to_string(), Value::String(message.to_string()));
|
||||
|
||||
match aether_ai_formats::normalize_legacy_openai_format_alias(client_api_format).as_str() {
|
||||
match aether_ai_formats::normalize_api_format_alias(client_api_format).as_str() {
|
||||
"openai:chat" | "openai:responses" | "openai:responses:compact" => {
|
||||
error_object.insert(
|
||||
"type".to_string(),
|
||||
@@ -51,7 +51,7 @@ pub fn build_core_error_body_for_client_format(
|
||||
Value::Object(error_object),
|
||||
)])))
|
||||
}
|
||||
"claude:chat" | "claude:cli" => {
|
||||
"claude:messages" => {
|
||||
error_object.insert(
|
||||
"type".to_string(),
|
||||
Value::String(map_local_sync_error_kind_to_claude_type(kind).to_string()),
|
||||
@@ -64,7 +64,7 @@ pub fn build_core_error_body_for_client_format(
|
||||
("error".to_string(), Value::Object(error_object)),
|
||||
])))
|
||||
}
|
||||
"gemini:chat" | "gemini:cli" => Some(Value::Object(Map::from_iter([(
|
||||
"gemini:generate_content" => Some(Value::Object(Map::from_iter([(
|
||||
"error".to_string(),
|
||||
Value::Object(Map::from_iter([
|
||||
(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use aether_ai_formats::normalize_legacy_openai_format_alias;
|
||||
use aether_ai_formats::normalize_api_format_alias;
|
||||
use aether_provider_transport::auth::{
|
||||
resolve_local_gemini_auth, resolve_local_openai_bearer_auth, resolve_local_standard_auth,
|
||||
};
|
||||
@@ -80,8 +80,8 @@ pub fn request_conversion_enabled_for_transport(
|
||||
client_api_format: &str,
|
||||
provider_api_format: &str,
|
||||
) -> bool {
|
||||
let client_api_format = normalize_legacy_openai_format_alias(client_api_format);
|
||||
let provider_api_format = normalize_legacy_openai_format_alias(provider_api_format);
|
||||
let client_api_format = normalize_api_format_alias(client_api_format);
|
||||
let provider_api_format = normalize_api_format_alias(provider_api_format);
|
||||
if client_api_format == provider_api_format {
|
||||
return true;
|
||||
}
|
||||
@@ -103,8 +103,8 @@ pub fn request_pair_allowed_for_transport(
|
||||
client_api_format: &str,
|
||||
provider_api_format: &str,
|
||||
) -> bool {
|
||||
let client_api_format = normalize_legacy_openai_format_alias(client_api_format);
|
||||
let provider_api_format = normalize_legacy_openai_format_alias(provider_api_format);
|
||||
let client_api_format = normalize_api_format_alias(client_api_format);
|
||||
let provider_api_format = normalize_api_format_alias(provider_api_format);
|
||||
if client_api_format == provider_api_format {
|
||||
return true;
|
||||
}
|
||||
@@ -116,7 +116,7 @@ pub fn request_pair_allowed_for_transport(
|
||||
.provider_type
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("kiro")
|
||||
&& provider_api_format.eq_ignore_ascii_case("claude:cli")
|
||||
&& aether_ai_formats::api_format_alias_matches(&provider_api_format, "claude:messages")
|
||||
{
|
||||
return request_conversion_enabled_for_transport(
|
||||
transport,
|
||||
@@ -148,16 +148,15 @@ pub fn request_conversion_transport_unsupported_reason(
|
||||
.provider_type
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("kiro")
|
||||
&& transport
|
||||
.endpoint
|
||||
.api_format
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("claude:cli")
|
||||
&& aether_ai_formats::api_format_alias_matches(
|
||||
&transport.endpoint.api_format,
|
||||
"claude:messages",
|
||||
)
|
||||
{
|
||||
return local_kiro_request_transport_unsupported_reason_with_network(transport);
|
||||
}
|
||||
|
||||
match normalize_legacy_openai_format_alias(&transport.endpoint.api_format).as_str() {
|
||||
match normalize_api_format_alias(&transport.endpoint.api_format).as_str() {
|
||||
"openai:chat" => local_openai_chat_transport_unsupported_reason(transport),
|
||||
"openai:responses" | "openai:responses:compact" => {
|
||||
local_standard_transport_unsupported_reason_with_network(
|
||||
@@ -165,21 +164,16 @@ pub fn request_conversion_transport_unsupported_reason(
|
||||
transport.endpoint.api_format.trim(),
|
||||
)
|
||||
}
|
||||
"claude:chat" => {
|
||||
local_standard_transport_unsupported_reason_with_network(transport, "claude:chat")
|
||||
"claude:messages" => {
|
||||
local_standard_transport_unsupported_reason_with_network(transport, "claude:messages")
|
||||
}
|
||||
"claude:cli" => {
|
||||
local_standard_transport_unsupported_reason_with_network(transport, "claude:cli")
|
||||
}
|
||||
"gemini:chat" | "gemini:cli" if is_vertex_api_key_transport_context(transport) => {
|
||||
"gemini:generate_content" if is_vertex_api_key_transport_context(transport) => {
|
||||
local_vertex_api_key_gemini_transport_unsupported_reason_with_network(transport)
|
||||
}
|
||||
"gemini:chat" => {
|
||||
local_gemini_transport_unsupported_reason_with_network(transport, "gemini:chat")
|
||||
}
|
||||
"gemini:cli" => {
|
||||
local_gemini_transport_unsupported_reason_with_network(transport, "gemini:cli")
|
||||
}
|
||||
"gemini:generate_content" => local_gemini_transport_unsupported_reason_with_network(
|
||||
transport,
|
||||
"gemini:generate_content",
|
||||
),
|
||||
_ => Some("transport_api_format_unsupported"),
|
||||
}
|
||||
}
|
||||
@@ -188,11 +182,11 @@ pub fn request_conversion_direct_auth(
|
||||
transport: &GatewayProviderTransportSnapshot,
|
||||
_kind: RequestConversionKind,
|
||||
) -> Option<(String, String)> {
|
||||
match normalize_legacy_openai_format_alias(&transport.endpoint.api_format).as_str() {
|
||||
match normalize_api_format_alias(&transport.endpoint.api_format).as_str() {
|
||||
"openai:chat" | "openai:responses" | "openai:responses:compact" => {
|
||||
resolve_local_openai_bearer_auth(transport)
|
||||
}
|
||||
"gemini:chat" | "gemini:cli" => {
|
||||
"gemini:generate_content" => {
|
||||
if is_vertex_api_key_transport_context(transport) {
|
||||
resolve_local_vertex_api_key_query_auth(transport)
|
||||
.map(|auth| (VERTEX_API_KEY_QUERY_PARAM.to_string(), auth.value))
|
||||
@@ -200,7 +194,7 @@ pub fn request_conversion_direct_auth(
|
||||
resolve_local_gemini_auth(transport)
|
||||
}
|
||||
}
|
||||
"claude:chat" | "claude:cli" => resolve_local_standard_auth(transport),
|
||||
"claude:messages" => resolve_local_standard_auth(transport),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -243,8 +237,9 @@ fn json_format_list_contains(value: &serde_json::Value, api_format: &str) -> boo
|
||||
return false;
|
||||
};
|
||||
items.iter().any(|item| {
|
||||
item.as_str()
|
||||
.is_some_and(|candidate| candidate.trim().eq_ignore_ascii_case(api_format))
|
||||
item.as_str().is_some_and(|candidate| {
|
||||
aether_ai_formats::api_format_alias_matches(candidate, api_format)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -266,18 +261,16 @@ mod tests {
|
||||
const STANDARD_SURFACES: &[&str] = &[
|
||||
"openai:chat",
|
||||
"openai:responses",
|
||||
"claude:chat",
|
||||
"claude:cli",
|
||||
"gemini:chat",
|
||||
"gemini:cli",
|
||||
"claude:messages",
|
||||
"gemini:generate_content",
|
||||
];
|
||||
|
||||
fn expected_request_conversion_kind(provider_api_format: &str) -> RequestConversionKind {
|
||||
match provider_api_format {
|
||||
"openai:chat" => RequestConversionKind::ToOpenAIChat,
|
||||
"openai:responses" => RequestConversionKind::ToOpenAiResponses,
|
||||
"claude:chat" | "claude:cli" => RequestConversionKind::ToClaudeStandard,
|
||||
"gemini:chat" | "gemini:cli" => RequestConversionKind::ToGeminiStandard,
|
||||
"claude:messages" => RequestConversionKind::ToClaudeStandard,
|
||||
"gemini:generate_content" => RequestConversionKind::ToGeminiStandard,
|
||||
other => panic!("unexpected provider api format: {other}"),
|
||||
}
|
||||
}
|
||||
@@ -289,7 +282,7 @@ mod tests {
|
||||
Some(RequestConversionKind::ToOpenAiResponses)
|
||||
);
|
||||
assert_eq!(
|
||||
request_conversion_kind("openai:chat", "claude:cli"),
|
||||
request_conversion_kind("openai:chat", "claude:messages"),
|
||||
Some(RequestConversionKind::ToClaudeStandard)
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -297,11 +290,11 @@ mod tests {
|
||||
Some(RequestConversionKind::ToOpenAIChat)
|
||||
);
|
||||
assert_eq!(
|
||||
request_conversion_kind("openai:responses:compact", "gemini:cli"),
|
||||
request_conversion_kind("openai:responses:compact", "gemini:generate_content"),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
request_conversion_kind("gemini:cli", "openai:responses:compact"),
|
||||
request_conversion_kind("gemini:generate_content", "openai:responses:compact"),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -317,14 +310,14 @@ mod tests {
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
request_conversion_kind("gemini:chat", "claude:chat"),
|
||||
request_conversion_kind("gemini:generate_content", "claude:messages"),
|
||||
Some(RequestConversionKind::ToClaudeStandard)
|
||||
);
|
||||
assert_eq!(request_conversion_kind("claude:chat", "claude:cli"), None);
|
||||
assert_eq!(
|
||||
request_conversion_kind("claude:chat", "claude:cli"),
|
||||
Some(RequestConversionKind::ToClaudeStandard)
|
||||
request_conversion_kind("claude:messages", "claude:messages"),
|
||||
None
|
||||
);
|
||||
assert_eq!(request_conversion_kind("claude:chat", "claude:chat"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -351,35 +344,35 @@ mod tests {
|
||||
#[test]
|
||||
fn sync_response_conversion_registry_supports_bidirectional_standard_matrix() {
|
||||
assert_eq!(
|
||||
sync_chat_response_conversion_kind("openai:chat", "claude:chat"),
|
||||
sync_chat_response_conversion_kind("openai:chat", "claude:messages"),
|
||||
Some(SyncChatResponseConversionKind::ToClaudeChat)
|
||||
);
|
||||
assert_eq!(
|
||||
sync_chat_response_conversion_kind("claude:chat", "gemini:chat"),
|
||||
sync_chat_response_conversion_kind("claude:messages", "gemini:generate_content"),
|
||||
Some(SyncChatResponseConversionKind::ToGeminiChat)
|
||||
);
|
||||
assert_eq!(
|
||||
sync_chat_response_conversion_kind("gemini:chat", "openai:chat"),
|
||||
sync_chat_response_conversion_kind("gemini:generate_content", "openai:chat"),
|
||||
Some(SyncChatResponseConversionKind::ToOpenAIChat)
|
||||
);
|
||||
assert_eq!(
|
||||
sync_cli_response_conversion_kind("openai:responses", "gemini:cli"),
|
||||
sync_cli_response_conversion_kind("openai:responses", "gemini:generate_content"),
|
||||
Some(SyncCliResponseConversionKind::ToGeminiCli)
|
||||
);
|
||||
assert_eq!(
|
||||
sync_cli_response_conversion_kind("claude:chat", "openai:responses"),
|
||||
sync_cli_response_conversion_kind("claude:messages", "openai:responses"),
|
||||
Some(SyncCliResponseConversionKind::ToOpenAiResponses)
|
||||
);
|
||||
assert_eq!(
|
||||
sync_cli_response_conversion_kind("claude:cli", "openai:responses:compact"),
|
||||
sync_cli_response_conversion_kind("claude:messages", "openai:responses:compact"),
|
||||
Some(SyncCliResponseConversionKind::ToOpenAiResponses)
|
||||
);
|
||||
assert_eq!(
|
||||
sync_cli_response_conversion_kind("openai:responses:compact", "claude:cli"),
|
||||
sync_cli_response_conversion_kind("openai:responses:compact", "claude:messages"),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
sync_cli_response_conversion_kind("gemini:cli", "claude:cli"),
|
||||
sync_cli_response_conversion_kind("gemini:generate_content", "claude:messages"),
|
||||
Some(SyncCliResponseConversionKind::ToClaudeCli)
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -395,7 +388,7 @@ mod tests {
|
||||
#[test]
|
||||
fn sync_response_conversion_registry_covers_all_standard_surface_pairs() {
|
||||
for provider_api_format in STANDARD_SURFACES {
|
||||
for client_api_format in ["openai:chat", "claude:chat", "gemini:chat"] {
|
||||
for client_api_format in ["openai:chat", "claude:messages", "gemini:generate_content"] {
|
||||
let actual =
|
||||
sync_chat_response_conversion_kind(provider_api_format, client_api_format);
|
||||
if *provider_api_format == client_api_format {
|
||||
@@ -406,8 +399,8 @@ mod tests {
|
||||
} else {
|
||||
let expected = match client_api_format {
|
||||
"openai:chat" => SyncChatResponseConversionKind::ToOpenAIChat,
|
||||
"claude:chat" => SyncChatResponseConversionKind::ToClaudeChat,
|
||||
"gemini:chat" => SyncChatResponseConversionKind::ToGeminiChat,
|
||||
"claude:messages" => SyncChatResponseConversionKind::ToClaudeChat,
|
||||
"gemini:generate_content" => SyncChatResponseConversionKind::ToGeminiChat,
|
||||
other => panic!("unexpected chat client api format: {other}"),
|
||||
};
|
||||
assert_eq!(
|
||||
@@ -418,7 +411,11 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
for client_api_format in ["openai:responses", "claude:cli", "gemini:cli"] {
|
||||
for client_api_format in [
|
||||
"openai:responses",
|
||||
"claude:messages",
|
||||
"gemini:generate_content",
|
||||
] {
|
||||
let actual =
|
||||
sync_cli_response_conversion_kind(provider_api_format, client_api_format);
|
||||
if *provider_api_format == client_api_format {
|
||||
@@ -429,8 +426,8 @@ mod tests {
|
||||
} else {
|
||||
let expected = match client_api_format {
|
||||
"openai:responses" => SyncCliResponseConversionKind::ToOpenAiResponses,
|
||||
"claude:cli" => SyncCliResponseConversionKind::ToClaudeCli,
|
||||
"gemini:cli" => SyncCliResponseConversionKind::ToGeminiCli,
|
||||
"claude:messages" => SyncCliResponseConversionKind::ToClaudeCli,
|
||||
"gemini:generate_content" => SyncCliResponseConversionKind::ToGeminiCli,
|
||||
other => panic!("unexpected cli client api format: {other}"),
|
||||
};
|
||||
assert_eq!(
|
||||
@@ -449,49 +446,31 @@ mod tests {
|
||||
request_candidate_api_formats("openai:chat", false),
|
||||
vec![
|
||||
"openai:chat",
|
||||
"claude:chat",
|
||||
"gemini:chat",
|
||||
"openai:responses",
|
||||
"claude:cli",
|
||||
"gemini:cli",
|
||||
"claude:messages",
|
||||
"gemini:generate_content",
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
request_candidate_api_formats("openai:responses", false),
|
||||
vec![
|
||||
"openai:responses",
|
||||
"claude:cli",
|
||||
"gemini:cli",
|
||||
"openai:chat",
|
||||
"claude:chat",
|
||||
"gemini:chat",
|
||||
"claude:messages",
|
||||
"gemini:generate_content",
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
request_candidate_api_formats("openai:cli", false),
|
||||
vec![
|
||||
"openai:responses",
|
||||
"claude:cli",
|
||||
"gemini:cli",
|
||||
"openai:chat",
|
||||
"claude:chat",
|
||||
"gemini:chat",
|
||||
]
|
||||
Vec::<&'static str>::new()
|
||||
);
|
||||
assert_eq!(
|
||||
request_candidate_api_formats("claude:cli", false),
|
||||
vec![
|
||||
"claude:cli",
|
||||
"openai:responses",
|
||||
"gemini:cli",
|
||||
"claude:chat",
|
||||
"openai:chat",
|
||||
"gemini:chat",
|
||||
]
|
||||
Vec::<&'static str>::new()
|
||||
);
|
||||
assert_eq!(
|
||||
request_candidate_api_formats("openai:compact", false),
|
||||
vec!["openai:responses:compact"]
|
||||
Vec::<&'static str>::new()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -499,21 +478,25 @@ mod tests {
|
||||
fn request_candidate_registry_prefers_same_kind_before_same_family_fallbacks() {
|
||||
assert_eq!(
|
||||
request_candidate_api_format_preference("claude:cli", "openai:responses"),
|
||||
Some((1, 0))
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
request_candidate_api_format_preference("claude:cli", "claude:chat"),
|
||||
Some((2, 1))
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
request_candidate_api_format_preference("claude:cli", "openai:chat"),
|
||||
Some((3, 0))
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_conversion_enable_flag_only_applies_to_real_data_format_conversions() {
|
||||
assert!(!request_conversion_requires_enable_flag(
|
||||
"claude:messages",
|
||||
"claude:messages"
|
||||
));
|
||||
assert!(request_conversion_requires_enable_flag(
|
||||
"claude:chat",
|
||||
"claude:cli"
|
||||
));
|
||||
@@ -527,7 +510,7 @@ mod tests {
|
||||
));
|
||||
assert!(request_conversion_requires_enable_flag(
|
||||
"openai:chat",
|
||||
"gemini:chat"
|
||||
"gemini:generate_content"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -627,7 +610,7 @@ mod tests {
|
||||
config: None,
|
||||
format_acceptance_config: Some(serde_json::json!({
|
||||
"enabled": true,
|
||||
"accept_formats": ["claude:cli"],
|
||||
"accept_formats": ["claude:messages"],
|
||||
})),
|
||||
proxy: None,
|
||||
},
|
||||
@@ -652,17 +635,17 @@ mod tests {
|
||||
|
||||
assert!(request_conversion_enabled_for_transport(
|
||||
&transport,
|
||||
"claude:cli",
|
||||
"claude:messages",
|
||||
"openai:responses"
|
||||
));
|
||||
assert!(request_pair_allowed_for_transport(
|
||||
&transport,
|
||||
"claude:cli",
|
||||
"claude:messages",
|
||||
"openai:responses"
|
||||
));
|
||||
assert!(!request_pair_allowed_for_transport(
|
||||
&transport,
|
||||
"gemini:cli",
|
||||
"gemini:generate_content",
|
||||
"openai:responses"
|
||||
));
|
||||
}
|
||||
@@ -700,7 +683,7 @@ mod tests {
|
||||
config: None,
|
||||
format_acceptance_config: Some(serde_json::json!({
|
||||
"enabled": true,
|
||||
"reject_formats": ["claude:cli"],
|
||||
"reject_formats": ["claude:messages"],
|
||||
})),
|
||||
proxy: None,
|
||||
},
|
||||
@@ -725,7 +708,7 @@ mod tests {
|
||||
|
||||
assert!(!request_conversion_enabled_for_transport(
|
||||
&transport,
|
||||
"claude:cli",
|
||||
"claude:messages",
|
||||
"openai:responses"
|
||||
));
|
||||
}
|
||||
@@ -751,9 +734,9 @@ mod tests {
|
||||
endpoint: GatewayProviderTransportEndpoint {
|
||||
id: "endpoint-vertex".to_string(),
|
||||
provider_id: "provider-vertex".to_string(),
|
||||
api_format: "gemini:chat".to_string(),
|
||||
api_format: "gemini:generate_content".to_string(),
|
||||
api_family: Some("gemini".to_string()),
|
||||
endpoint_kind: Some("chat".to_string()),
|
||||
endpoint_kind: Some("generate_content".to_string()),
|
||||
is_active: true,
|
||||
base_url: "https://aiplatform.googleapis.com".to_string(),
|
||||
header_rules: None,
|
||||
@@ -770,7 +753,7 @@ mod tests {
|
||||
name: "key".to_string(),
|
||||
auth_type: "api_key".to_string(),
|
||||
is_active: true,
|
||||
api_formats: Some(vec!["gemini:chat".to_string()]),
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
@@ -794,7 +777,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kiro_claude_cli_transport_supports_cross_format_conversion_via_envelope() {
|
||||
fn kiro_claude_messages_transport_supports_cross_format_conversion_via_envelope() {
|
||||
let transport = GatewayProviderTransportSnapshot {
|
||||
provider: GatewayProviderTransportProvider {
|
||||
id: "provider-kiro".to_string(),
|
||||
@@ -814,9 +797,9 @@ mod tests {
|
||||
endpoint: GatewayProviderTransportEndpoint {
|
||||
id: "endpoint-kiro".to_string(),
|
||||
provider_id: "provider-kiro".to_string(),
|
||||
api_format: "claude:cli".to_string(),
|
||||
api_format: "claude:messages".to_string(),
|
||||
api_family: Some("claude".to_string()),
|
||||
endpoint_kind: Some("cli".to_string()),
|
||||
endpoint_kind: Some("messages".to_string()),
|
||||
is_active: true,
|
||||
base_url: "https://q.{region}.amazonaws.com".to_string(),
|
||||
header_rules: None,
|
||||
@@ -833,7 +816,7 @@ mod tests {
|
||||
name: "key".to_string(),
|
||||
auth_type: "bearer".to_string(),
|
||||
is_active: true,
|
||||
api_formats: Some(vec!["claude:cli".to_string()]),
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
@@ -849,7 +832,7 @@ mod tests {
|
||||
assert!(request_pair_allowed_for_transport(
|
||||
&transport,
|
||||
"openai:chat",
|
||||
"claude:cli"
|
||||
"claude:messages"
|
||||
));
|
||||
assert!(request_conversion_transport_supported(
|
||||
&transport,
|
||||
|
||||
@@ -459,7 +459,7 @@ mod tests {
|
||||
"\"code\":\"invalid_request\"",
|
||||
),
|
||||
(
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
data_line(json!({
|
||||
"type": "error",
|
||||
"error": {
|
||||
@@ -473,7 +473,7 @@ mod tests {
|
||||
"\"code\":\"rate_limit\"",
|
||||
),
|
||||
(
|
||||
"gemini:cli",
|
||||
"gemini:generate_content",
|
||||
data_line(json!({
|
||||
"error": {
|
||||
"code": 429,
|
||||
@@ -524,7 +524,7 @@ mod tests {
|
||||
"\"code\":\"invalid_request\"",
|
||||
),
|
||||
(
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
data_line(json!({
|
||||
"type": "error",
|
||||
"error": {
|
||||
@@ -538,7 +538,7 @@ mod tests {
|
||||
"\"code\":\"rate_limit\"",
|
||||
),
|
||||
(
|
||||
"gemini:cli",
|
||||
"gemini:generate_content",
|
||||
data_line(json!({
|
||||
"error": {
|
||||
"code": 429,
|
||||
@@ -553,7 +553,7 @@ mod tests {
|
||||
];
|
||||
|
||||
for (provider_api_format, line, message, err_type, code) in cases {
|
||||
let report_context = report_context(provider_api_format, "claude:chat");
|
||||
let report_context = report_context(provider_api_format, "claude:messages");
|
||||
let mut matrix = StreamingStandardFormatMatrix::default();
|
||||
let output = matrix
|
||||
.transform_line(&report_context, line)
|
||||
@@ -591,7 +591,7 @@ mod tests {
|
||||
"\"status\":\"INVALID_ARGUMENT\"",
|
||||
),
|
||||
(
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
data_line(json!({
|
||||
"type": "error",
|
||||
"error": {
|
||||
@@ -605,7 +605,7 @@ mod tests {
|
||||
"\"status\":\"RESOURCE_EXHAUSTED\"",
|
||||
),
|
||||
(
|
||||
"gemini:cli",
|
||||
"gemini:generate_content",
|
||||
data_line(json!({
|
||||
"error": {
|
||||
"code": 429,
|
||||
@@ -620,7 +620,7 @@ mod tests {
|
||||
];
|
||||
|
||||
for (provider_api_format, line, message, code, status) in cases {
|
||||
let report_context = report_context(provider_api_format, "gemini:chat");
|
||||
let report_context = report_context(provider_api_format, "gemini:generate_content");
|
||||
let mut matrix = StreamingStandardFormatMatrix::default();
|
||||
let output = matrix
|
||||
.transform_line(&report_context, line)
|
||||
@@ -656,7 +656,7 @@ mod tests {
|
||||
"\"code\":\"invalid_request\"",
|
||||
),
|
||||
(
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
data_line(json!({
|
||||
"type": "error",
|
||||
"error": {
|
||||
@@ -670,7 +670,7 @@ mod tests {
|
||||
"\"code\":\"rate_limit\"",
|
||||
),
|
||||
(
|
||||
"gemini:cli",
|
||||
"gemini:generate_content",
|
||||
data_line(json!({
|
||||
"error": {
|
||||
"code": 429,
|
||||
@@ -706,7 +706,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn rewrites_gemini_inline_image_streams_to_claude_image_blocks() {
|
||||
let report_context = report_context("gemini:chat", "claude:chat");
|
||||
let report_context = report_context("gemini:generate_content", "claude:messages");
|
||||
let mut matrix = StreamingStandardFormatMatrix::default();
|
||||
let output = matrix
|
||||
.transform_line(
|
||||
@@ -735,7 +735,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn rewrites_claude_image_blocks_to_gemini_inline_image_streams() {
|
||||
let report_context = report_context("claude:chat", "gemini:chat");
|
||||
let report_context = report_context("claude:messages", "gemini:generate_content");
|
||||
let mut matrix = StreamingStandardFormatMatrix::default();
|
||||
let output = matrix
|
||||
.transform_line(
|
||||
@@ -763,7 +763,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn terminal_observer_preserves_claude_cache_usage() {
|
||||
let report_context = report_context("claude:chat", "openai:chat");
|
||||
let report_context = report_context("claude:messages", "openai:chat");
|
||||
let mut observer = StreamingStandardTerminalObserver::default();
|
||||
|
||||
observer
|
||||
|
||||
@@ -41,7 +41,7 @@ pub fn resolve_finalize_stream_rewrite_mode(
|
||||
|
||||
if needs_conversion
|
||||
&& envelope_name.eq_ignore_ascii_case(KIRO_ENVELOPE_NAME)
|
||||
&& provider_api_format == "claude:cli"
|
||||
&& provider_api_format == "claude:messages"
|
||||
{
|
||||
return supports_standard_stream_rewrite(
|
||||
provider_api_format.as_str(),
|
||||
@@ -63,7 +63,8 @@ pub fn resolve_finalize_stream_rewrite_mode(
|
||||
}
|
||||
|
||||
if envelope_name.eq_ignore_ascii_case(KIRO_ENVELOPE_NAME) {
|
||||
return (provider_api_format == "claude:cli" && client_api_format == "claude:cli")
|
||||
return (provider_api_format == "claude:messages"
|
||||
&& client_api_format == "claude:messages")
|
||||
.then_some(FinalizeStreamRewriteMode::KiroToClaudeCli);
|
||||
}
|
||||
|
||||
@@ -83,25 +84,29 @@ fn supports_standard_stream_rewrite(provider_api_format: &str, client_api_format
|
||||
|
||||
fn is_standard_provider_api_format(api_format: &str) -> bool {
|
||||
matches!(
|
||||
aether_ai_formats::normalize_legacy_openai_format_alias(api_format).as_str(),
|
||||
aether_ai_formats::normalize_api_format_alias(api_format).as_str(),
|
||||
"openai:chat"
|
||||
| "openai:responses"
|
||||
| "openai:responses:compact"
|
||||
| "claude:chat"
|
||||
| "claude:cli"
|
||||
| "gemini:chat"
|
||||
| "gemini:cli"
|
||||
| "claude:messages"
|
||||
| "gemini:generate_content"
|
||||
)
|
||||
}
|
||||
|
||||
fn is_standard_chat_client_api_format(api_format: &str) -> bool {
|
||||
matches!(api_format, "openai:chat" | "claude:chat" | "gemini:chat")
|
||||
matches!(
|
||||
api_format,
|
||||
"openai:chat" | "claude:messages" | "gemini:generate_content"
|
||||
)
|
||||
}
|
||||
|
||||
fn is_standard_cli_client_api_format(api_format: &str) -> bool {
|
||||
matches!(
|
||||
aether_ai_formats::normalize_legacy_openai_format_alias(api_format).as_str(),
|
||||
"openai:responses" | "openai:responses:compact" | "claude:cli" | "gemini:cli"
|
||||
aether_ai_formats::normalize_api_format_alias(api_format).as_str(),
|
||||
"openai:responses"
|
||||
| "openai:responses:compact"
|
||||
| "claude:messages"
|
||||
| "gemini:generate_content"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -114,7 +119,7 @@ mod tests {
|
||||
#[test]
|
||||
fn resolves_standard_mode_for_cross_format_standard_streams() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "claude:chat",
|
||||
"provider_api_format": "claude:messages",
|
||||
"client_api_format": "openai:chat",
|
||||
"needs_conversion": true,
|
||||
});
|
||||
@@ -127,8 +132,8 @@ mod tests {
|
||||
#[test]
|
||||
fn resolves_envelope_unwrap_for_same_format_private_envelopes() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "gemini:cli",
|
||||
"client_api_format": "gemini:cli",
|
||||
"provider_api_format": "gemini:generate_content",
|
||||
"client_api_format": "gemini:generate_content",
|
||||
"envelope_name": "antigravity:v1internal",
|
||||
"needs_conversion": false,
|
||||
});
|
||||
@@ -141,8 +146,8 @@ mod tests {
|
||||
#[test]
|
||||
fn resolves_kiro_same_format_streams_to_kiro_mode() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "claude:cli",
|
||||
"client_api_format": "claude:cli",
|
||||
"provider_api_format": "claude:messages",
|
||||
"client_api_format": "claude:messages",
|
||||
"envelope_name": "kiro:generateAssistantResponse",
|
||||
"needs_conversion": false,
|
||||
});
|
||||
|
||||
@@ -176,10 +176,10 @@ pub fn maybe_build_openai_chat_cross_format_sync_product_from_normalized_payload
|
||||
};
|
||||
|
||||
let Some(client_body_json) = (match provider_api_format.as_str() {
|
||||
"claude:chat" | "claude:cli" => {
|
||||
"claude:messages" => {
|
||||
convert_claude_canonical_response_to_openai_chat(&provider_body_json, report_context)
|
||||
}
|
||||
"gemini:chat" | "gemini:cli" => {
|
||||
"gemini:generate_content" => {
|
||||
convert_gemini_canonical_response_to_openai_chat(&provider_body_json, report_context)
|
||||
}
|
||||
"openai:responses" => {
|
||||
@@ -248,10 +248,10 @@ pub fn maybe_build_openai_responses_cross_format_sync_product_from_normalized_pa
|
||||
normalize_openai_responses_family_api_format(&provider_api_format);
|
||||
let Some(client_body_json) = (match normalized_provider_api_format.as_str() {
|
||||
"openai:responses" | "openai:responses:compact" => Some(provider_body_json.clone()),
|
||||
"claude:chat" | "claude:cli" => {
|
||||
"claude:messages" => {
|
||||
convert_claude_response_to_openai_responses(&provider_body_json, report_context)
|
||||
}
|
||||
"gemini:chat" | "gemini:cli" => convert_gemini_canonical_response_to_openai_responses(
|
||||
"gemini:generate_content" => convert_gemini_canonical_response_to_openai_responses(
|
||||
&provider_body_json,
|
||||
report_context,
|
||||
normalized_client_api_format == "openai:responses:compact",
|
||||
@@ -541,8 +541,8 @@ fn maybe_build_openai_cross_format_provider_body_from_normalized_payload(
|
||||
let normalized_provider_api_format =
|
||||
normalize_openai_responses_family_api_format(provider_api_format);
|
||||
match normalized_provider_api_format.as_str() {
|
||||
"claude:chat" | "claude:cli" => aggregate_claude_stream_sync_response(&body_bytes),
|
||||
"gemini:chat" | "gemini:cli" => aggregate_gemini_stream_sync_response(&body_bytes),
|
||||
"claude:messages" => aggregate_claude_stream_sync_response(&body_bytes),
|
||||
"gemini:generate_content" => aggregate_gemini_stream_sync_response(&body_bytes),
|
||||
"openai:responses" | "openai:responses:compact" => {
|
||||
aggregate_openai_responses_stream_sync_response(&body_bytes)
|
||||
}
|
||||
@@ -622,13 +622,13 @@ pub fn aggregate_standard_chat_stream_sync_response(
|
||||
body: &[u8],
|
||||
provider_api_format: &str,
|
||||
) -> Option<Value> {
|
||||
match aether_ai_formats::normalize_legacy_openai_format_alias(provider_api_format).as_str() {
|
||||
match aether_ai_formats::normalize_api_format_alias(provider_api_format).as_str() {
|
||||
"openai:chat" => aggregate_openai_chat_stream_sync_response(body),
|
||||
"openai:responses" | "openai:responses:compact" => {
|
||||
aggregate_openai_responses_stream_sync_response(body)
|
||||
}
|
||||
"claude:chat" | "claude:cli" => aggregate_claude_stream_sync_response(body),
|
||||
"gemini:chat" | "gemini:cli" => aggregate_gemini_stream_sync_response(body),
|
||||
"claude:messages" => aggregate_claude_stream_sync_response(body),
|
||||
"gemini:generate_content" => aggregate_gemini_stream_sync_response(body),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -660,7 +660,7 @@ pub fn convert_standard_chat_response(
|
||||
}
|
||||
if matches!(
|
||||
provider_api_format.trim().to_ascii_lowercase().as_str(),
|
||||
"claude:chat" | "claude:cli"
|
||||
"claude:messages"
|
||||
) {
|
||||
return convert_claude_canonical_chat_response(
|
||||
body_json,
|
||||
@@ -670,7 +670,7 @@ pub fn convert_standard_chat_response(
|
||||
}
|
||||
if matches!(
|
||||
provider_api_format.trim().to_ascii_lowercase().as_str(),
|
||||
"gemini:chat" | "gemini:cli"
|
||||
"gemini:generate_content"
|
||||
) {
|
||||
return convert_gemini_canonical_chat_response(
|
||||
body_json,
|
||||
@@ -725,13 +725,13 @@ pub fn convert_standard_cli_response(
|
||||
}
|
||||
if matches!(
|
||||
provider_api_format.trim().to_ascii_lowercase().as_str(),
|
||||
"claude:chat" | "claude:cli"
|
||||
"claude:messages"
|
||||
) {
|
||||
return convert_claude_canonical_cli_response(body_json, client_api_format, report_context);
|
||||
}
|
||||
if matches!(
|
||||
provider_api_format.trim().to_ascii_lowercase().as_str(),
|
||||
"gemini:chat" | "gemini:cli"
|
||||
"gemini:generate_content"
|
||||
) {
|
||||
return convert_gemini_canonical_cli_response(body_json, client_api_format, report_context);
|
||||
}
|
||||
@@ -760,8 +760,12 @@ pub fn convert_standard_cli_response(
|
||||
"openai:responses:compact" => {
|
||||
convert_openai_chat_response_to_openai_responses(&canonical, report_context, true)
|
||||
}
|
||||
"claude:cli" => convert_openai_chat_response_to_claude_chat(&canonical, report_context),
|
||||
"gemini:cli" => convert_openai_chat_response_to_gemini_chat(&canonical, report_context),
|
||||
"claude:messages" => {
|
||||
convert_openai_chat_response_to_claude_chat(&canonical, report_context)
|
||||
}
|
||||
"gemini:generate_content" => {
|
||||
convert_openai_chat_response_to_gemini_chat(&canonical, report_context)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -787,19 +791,23 @@ fn convert_openai_chat_canonical_chat_response(
|
||||
if !openai_chat_response_can_use_single_response_canonical(body_json) {
|
||||
return match client_api_format.trim().to_ascii_lowercase().as_str() {
|
||||
"openai:chat" => Some(body_json.clone()),
|
||||
"claude:chat" => convert_openai_chat_response_to_claude_chat(body_json, report_context),
|
||||
"gemini:chat" => convert_openai_chat_response_to_gemini_chat(body_json, report_context),
|
||||
"claude:messages" => {
|
||||
convert_openai_chat_response_to_claude_chat(body_json, report_context)
|
||||
}
|
||||
"gemini:generate_content" => {
|
||||
convert_openai_chat_response_to_gemini_chat(body_json, report_context)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
match client_api_format.trim().to_ascii_lowercase().as_str() {
|
||||
"openai:chat" => Some(body_json.clone()),
|
||||
"claude:chat" => {
|
||||
"claude:messages" => {
|
||||
let openai_chat =
|
||||
convert_openai_chat_canonical_response_to_openai_chat(body_json, report_context)?;
|
||||
convert_openai_chat_response_to_claude_chat(&openai_chat, report_context)
|
||||
}
|
||||
"gemini:chat" => {
|
||||
"gemini:generate_content" => {
|
||||
let openai_chat =
|
||||
convert_openai_chat_canonical_response_to_openai_chat(body_json, report_context)?;
|
||||
convert_openai_chat_response_to_gemini_chat(&openai_chat, report_context)
|
||||
@@ -821,8 +829,12 @@ fn convert_openai_chat_canonical_cli_response(
|
||||
"openai:responses:compact" => {
|
||||
convert_openai_chat_response_to_openai_responses(body_json, report_context, true)
|
||||
}
|
||||
"claude:cli" => convert_openai_chat_response_to_claude_chat(body_json, report_context),
|
||||
"gemini:cli" => convert_openai_chat_response_to_gemini_chat(body_json, report_context),
|
||||
"claude:messages" => {
|
||||
convert_openai_chat_response_to_claude_chat(body_json, report_context)
|
||||
}
|
||||
"gemini:generate_content" => {
|
||||
convert_openai_chat_response_to_gemini_chat(body_json, report_context)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@@ -835,8 +847,12 @@ fn convert_openai_chat_canonical_cli_response(
|
||||
"openai:responses:compact" => {
|
||||
convert_openai_chat_response_to_openai_responses(&openai_chat, report_context, true)
|
||||
}
|
||||
"claude:cli" => convert_openai_chat_response_to_claude_chat(&openai_chat, report_context),
|
||||
"gemini:cli" => convert_openai_chat_response_to_gemini_chat(&openai_chat, report_context),
|
||||
"claude:messages" => {
|
||||
convert_openai_chat_response_to_claude_chat(&openai_chat, report_context)
|
||||
}
|
||||
"gemini:generate_content" => {
|
||||
convert_openai_chat_response_to_gemini_chat(&openai_chat, report_context)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -873,7 +889,7 @@ fn convert_openai_responses_canonical_chat_response(
|
||||
.or_else(|| {
|
||||
convert_openai_responses_response_to_openai_chat(body_json, report_context)
|
||||
}),
|
||||
"claude:chat" => canonical
|
||||
"claude:messages" => canonical
|
||||
.as_ref()
|
||||
.map(canonical_to_claude_response)
|
||||
.or_else(|| {
|
||||
@@ -881,7 +897,7 @@ fn convert_openai_responses_canonical_chat_response(
|
||||
convert_openai_responses_response_to_openai_chat(body_json, report_context)?;
|
||||
convert_openai_chat_response_to_claude_chat(&openai_chat, report_context)
|
||||
}),
|
||||
"gemini:chat" => canonical
|
||||
"gemini:generate_content" => canonical
|
||||
.as_ref()
|
||||
.and_then(|canonical| canonical_to_gemini_response(canonical, report_context))
|
||||
.or_else(|| {
|
||||
@@ -921,7 +937,7 @@ fn convert_openai_responses_canonical_responses_response(
|
||||
convert_openai_chat_response_to_openai_responses(&openai_chat, report_context, true)
|
||||
})
|
||||
}
|
||||
"claude:cli" => {
|
||||
"claude:messages" => {
|
||||
let canonical =
|
||||
from_openai_responses_to_canonical_response(body_json).map(|mut canonical| {
|
||||
apply_report_context_model_fallback(&mut canonical.model, report_context);
|
||||
@@ -938,7 +954,7 @@ fn convert_openai_responses_canonical_responses_response(
|
||||
convert_openai_chat_response_to_claude_chat(&openai_chat, report_context)
|
||||
})
|
||||
}
|
||||
"gemini:cli" => {
|
||||
"gemini:generate_content" => {
|
||||
let canonical =
|
||||
from_openai_responses_to_canonical_response(body_json).map(|mut canonical| {
|
||||
apply_report_context_model_fallback(&mut canonical.model, report_context);
|
||||
@@ -982,8 +998,8 @@ fn convert_claude_canonical_chat_response(
|
||||
"openai:chat" => {
|
||||
convert_claude_canonical_response_to_openai_chat(body_json, report_context)
|
||||
}
|
||||
"claude:chat" => convert_claude_canonical_response_to_claude(body_json, report_context),
|
||||
"gemini:chat" => {
|
||||
"claude:messages" => convert_claude_canonical_response_to_claude(body_json, report_context),
|
||||
"gemini:generate_content" => {
|
||||
let openai_chat =
|
||||
convert_claude_canonical_response_to_openai_chat(body_json, report_context)?;
|
||||
convert_openai_chat_response_to_gemini_chat(&openai_chat, report_context)
|
||||
@@ -1008,8 +1024,8 @@ fn convert_claude_canonical_cli_response(
|
||||
convert_claude_canonical_response_to_openai_chat(body_json, report_context)?;
|
||||
convert_openai_chat_response_to_openai_responses(&openai_chat, report_context, true)
|
||||
}
|
||||
"claude:cli" => convert_claude_canonical_response_to_claude(body_json, report_context),
|
||||
"gemini:cli" => {
|
||||
"claude:messages" => convert_claude_canonical_response_to_claude(body_json, report_context),
|
||||
"gemini:generate_content" => {
|
||||
let openai_chat =
|
||||
convert_claude_canonical_response_to_openai_chat(body_json, report_context)?;
|
||||
convert_openai_chat_response_to_gemini_chat(&openai_chat, report_context)
|
||||
@@ -1054,10 +1070,10 @@ fn convert_gemini_canonical_chat_response(
|
||||
let openai_chat = convert_gemini_chat_response_to_openai_chat(body_json, report_context)?;
|
||||
return match client_api_format.trim().to_ascii_lowercase().as_str() {
|
||||
"openai:chat" => Some(openai_chat),
|
||||
"claude:chat" => {
|
||||
"claude:messages" => {
|
||||
convert_openai_chat_response_to_claude_chat(&openai_chat, report_context)
|
||||
}
|
||||
"gemini:chat" => {
|
||||
"gemini:generate_content" => {
|
||||
convert_openai_chat_response_to_gemini_chat(&openai_chat, report_context)
|
||||
}
|
||||
_ => None,
|
||||
@@ -1067,12 +1083,14 @@ fn convert_gemini_canonical_chat_response(
|
||||
"openai:chat" => {
|
||||
convert_gemini_canonical_response_to_openai_chat(body_json, report_context)
|
||||
}
|
||||
"claude:chat" => {
|
||||
"claude:messages" => {
|
||||
let openai_chat =
|
||||
convert_gemini_canonical_response_to_openai_chat(body_json, report_context)?;
|
||||
convert_openai_chat_response_to_claude_chat(&openai_chat, report_context)
|
||||
}
|
||||
"gemini:chat" => convert_gemini_canonical_response_to_gemini(body_json, report_context),
|
||||
"gemini:generate_content" => {
|
||||
convert_gemini_canonical_response_to_gemini(body_json, report_context)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1093,10 +1111,10 @@ fn convert_gemini_canonical_cli_response(
|
||||
"openai:responses:compact" => {
|
||||
convert_openai_chat_response_to_openai_responses(&openai_chat, report_context, true)
|
||||
}
|
||||
"claude:cli" => {
|
||||
"claude:messages" => {
|
||||
convert_openai_chat_response_to_claude_chat(&openai_chat, report_context)
|
||||
}
|
||||
"gemini:cli" => {
|
||||
"gemini:generate_content" => {
|
||||
convert_openai_chat_response_to_gemini_chat(&openai_chat, report_context)
|
||||
}
|
||||
_ => None,
|
||||
@@ -1109,12 +1127,14 @@ fn convert_gemini_canonical_cli_response(
|
||||
"openai:responses:compact" => {
|
||||
convert_gemini_canonical_response_to_openai_responses(body_json, report_context, true)
|
||||
}
|
||||
"claude:cli" => {
|
||||
"claude:messages" => {
|
||||
let openai_chat =
|
||||
convert_gemini_canonical_response_to_openai_chat(body_json, report_context)?;
|
||||
convert_openai_chat_response_to_claude_chat(&openai_chat, report_context)
|
||||
}
|
||||
"gemini:cli" => convert_gemini_canonical_response_to_gemini(body_json, report_context),
|
||||
"gemini:generate_content" => {
|
||||
convert_gemini_canonical_response_to_gemini(body_json, report_context)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1230,10 +1250,10 @@ fn is_openai_responses_finalize_kind(report_kind: &str) -> bool {
|
||||
fn standard_same_format_api_format(report_kind: &str) -> Option<&'static str> {
|
||||
match report_kind {
|
||||
"openai_chat_sync_finalize" => Some("openai:chat"),
|
||||
"claude_chat_sync_finalize" => Some("claude:chat"),
|
||||
"gemini_chat_sync_finalize" => Some("gemini:chat"),
|
||||
"claude_cli_sync_finalize" => Some("claude:cli"),
|
||||
"gemini_cli_sync_finalize" => Some("gemini:cli"),
|
||||
"claude_chat_sync_finalize" => Some("claude:messages"),
|
||||
"gemini_chat_sync_finalize" => Some("gemini:generate_content"),
|
||||
"claude_cli_sync_finalize" => Some("claude:messages"),
|
||||
"gemini_cli_sync_finalize" => Some("gemini:generate_content"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1241,8 +1261,8 @@ fn standard_same_format_api_format(report_kind: &str) -> Option<&'static str> {
|
||||
fn aggregate_same_format_stream_sync_response(api_format: &str, body: &[u8]) -> Option<Value> {
|
||||
match api_format {
|
||||
"openai:chat" => aggregate_openai_chat_stream_sync_response(body),
|
||||
"claude:chat" | "claude:cli" => aggregate_claude_stream_sync_response(body),
|
||||
"gemini:chat" | "gemini:cli" => aggregate_gemini_stream_sync_response(body),
|
||||
"claude:messages" => aggregate_claude_stream_sync_response(body),
|
||||
"gemini:generate_content" => aggregate_gemini_stream_sync_response(body),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1255,7 +1275,7 @@ fn is_openai_responses_family_api_format(api_format: &str) -> bool {
|
||||
}
|
||||
|
||||
fn normalize_openai_responses_family_api_format(api_format: &str) -> String {
|
||||
aether_ai_formats::normalize_legacy_openai_format_alias(api_format)
|
||||
aether_ai_formats::normalize_api_format_alias(api_format)
|
||||
}
|
||||
|
||||
fn parse_stream_json_events(body: &[u8]) -> Option<Vec<Value>> {
|
||||
@@ -2945,7 +2965,7 @@ mod tests {
|
||||
"data: {\"responseId\":\"resp_gem_stream_456\",\"modelVersion\":\"gemini-2.5-pro\",\"candidates\":[{\"index\":0,\"content\":{\"role\":\"model\",\"parts\":[]},\"finishReason\":\"STOP\"}],\"usageMetadata\":{\"promptTokenCount\":4,\"candidatesTokenCount\":2,\"totalTokenCount\":6}}\n\n",
|
||||
);
|
||||
let report_context = json!({
|
||||
"provider_api_format": "gemini:chat",
|
||||
"provider_api_format": "gemini:generate_content",
|
||||
"client_api_format": "openai:chat",
|
||||
"mapped_model": "gemini-2.5-pro",
|
||||
});
|
||||
@@ -2992,7 +3012,7 @@ mod tests {
|
||||
);
|
||||
let report_context = json!({
|
||||
"provider_api_format": "openai:chat",
|
||||
"client_api_format": "claude:chat",
|
||||
"client_api_format": "claude:messages",
|
||||
});
|
||||
|
||||
let product = maybe_build_standard_cross_format_sync_product_from_normalized_payload(
|
||||
@@ -3049,7 +3069,7 @@ mod tests {
|
||||
fn falls_back_to_body_json_when_stream_aggregation_returns_none() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "openai:chat",
|
||||
"client_api_format": "claude:chat",
|
||||
"client_api_format": "claude:messages",
|
||||
});
|
||||
let provider_body_json = json!({
|
||||
"id": "chatcmpl_123",
|
||||
@@ -3103,8 +3123,8 @@ mod tests {
|
||||
"data: {\"type\":\"message_stop\"}\n\n",
|
||||
);
|
||||
let report_context = json!({
|
||||
"provider_api_format": "claude:chat",
|
||||
"client_api_format": "claude:chat",
|
||||
"provider_api_format": "claude:messages",
|
||||
"client_api_format": "claude:messages",
|
||||
"needs_conversion": false,
|
||||
});
|
||||
|
||||
@@ -3129,8 +3149,8 @@ mod tests {
|
||||
#[test]
|
||||
fn falls_back_to_body_json_for_standard_same_format_sync_payload() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "gemini:cli",
|
||||
"client_api_format": "gemini:cli",
|
||||
"provider_api_format": "gemini:generate_content",
|
||||
"client_api_format": "gemini:generate_content",
|
||||
"needs_conversion": false,
|
||||
});
|
||||
let provider_body_json = json!({
|
||||
@@ -3181,8 +3201,8 @@ mod tests {
|
||||
#[test]
|
||||
fn rejects_standard_same_format_error_body_json() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "claude:chat",
|
||||
"client_api_format": "claude:chat",
|
||||
"provider_api_format": "claude:messages",
|
||||
"client_api_format": "claude:messages",
|
||||
"needs_conversion": false,
|
||||
});
|
||||
let provider_body_json = json!({
|
||||
@@ -3476,7 +3496,7 @@ mod tests {
|
||||
#[test]
|
||||
fn builds_openai_chat_cross_format_sync_product_from_claude_body_json() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "claude:chat",
|
||||
"provider_api_format": "claude:messages",
|
||||
"client_api_format": "openai:chat",
|
||||
"model": "gpt-5",
|
||||
"mapped_model": "claude-sonnet-4",
|
||||
@@ -3514,7 +3534,7 @@ mod tests {
|
||||
#[test]
|
||||
fn claude_canonical_response_route_matches_legacy_outputs_for_existing_clients() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "claude:chat",
|
||||
"provider_api_format": "claude:messages",
|
||||
"client_api_format": "openai:chat",
|
||||
"model": "gpt-5",
|
||||
"mapped_model": "claude-sonnet-4",
|
||||
@@ -3551,7 +3571,7 @@ mod tests {
|
||||
.expect("legacy claude -> openai chat");
|
||||
let converted_openai = convert_standard_chat_response(
|
||||
&provider_body_json,
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
"openai:chat",
|
||||
&report_context,
|
||||
)
|
||||
@@ -3563,8 +3583,8 @@ mod tests {
|
||||
.expect("legacy claude -> openai chat -> claude");
|
||||
let converted_claude = convert_standard_chat_response(
|
||||
&provider_body_json,
|
||||
"claude:chat",
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
"claude:messages",
|
||||
&report_context,
|
||||
)
|
||||
.expect("canonical claude -> claude");
|
||||
@@ -3578,7 +3598,7 @@ mod tests {
|
||||
.expect("legacy claude -> openai responses");
|
||||
let converted_openai_responses = convert_standard_cli_response(
|
||||
&provider_body_json,
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
"openai:responses",
|
||||
&report_context,
|
||||
)
|
||||
@@ -3589,7 +3609,7 @@ mod tests {
|
||||
#[test]
|
||||
fn gemini_canonical_response_route_matches_legacy_outputs_for_single_candidate() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "gemini:chat",
|
||||
"provider_api_format": "gemini:generate_content",
|
||||
"client_api_format": "openai:chat",
|
||||
"model": "gpt-5",
|
||||
"mapped_model": "gemini-2.5-pro",
|
||||
@@ -3628,7 +3648,7 @@ mod tests {
|
||||
.expect("legacy gemini -> openai chat");
|
||||
let converted_openai = convert_standard_chat_response(
|
||||
&provider_body_json,
|
||||
"gemini:chat",
|
||||
"gemini:generate_content",
|
||||
"openai:chat",
|
||||
&report_context,
|
||||
)
|
||||
@@ -3640,8 +3660,8 @@ mod tests {
|
||||
.expect("legacy gemini -> openai chat -> claude");
|
||||
let converted_claude = convert_standard_chat_response(
|
||||
&provider_body_json,
|
||||
"gemini:chat",
|
||||
"claude:chat",
|
||||
"gemini:generate_content",
|
||||
"claude:messages",
|
||||
&report_context,
|
||||
)
|
||||
.expect("canonical gemini -> claude");
|
||||
@@ -3655,7 +3675,7 @@ mod tests {
|
||||
.expect("legacy gemini -> openai responses");
|
||||
let converted_openai_responses = convert_standard_cli_response(
|
||||
&provider_body_json,
|
||||
"gemini:chat",
|
||||
"gemini:generate_content",
|
||||
"openai:responses",
|
||||
&report_context,
|
||||
)
|
||||
@@ -3667,7 +3687,7 @@ mod tests {
|
||||
fn openai_chat_canonical_response_route_matches_legacy_outputs_for_single_choice() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "openai:chat",
|
||||
"client_api_format": "claude:chat",
|
||||
"client_api_format": "claude:messages",
|
||||
"model": "gpt-5",
|
||||
"mapped_model": "gpt-5",
|
||||
});
|
||||
@@ -3712,7 +3732,7 @@ mod tests {
|
||||
let converted_claude = convert_standard_chat_response(
|
||||
&provider_body_json,
|
||||
"openai:chat",
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
&report_context,
|
||||
)
|
||||
.expect("canonical openai chat -> claude");
|
||||
@@ -3724,7 +3744,7 @@ mod tests {
|
||||
let converted_gemini = convert_standard_chat_response(
|
||||
&provider_body_json,
|
||||
"openai:chat",
|
||||
"gemini:chat",
|
||||
"gemini:generate_content",
|
||||
&report_context,
|
||||
)
|
||||
.expect("canonical openai chat -> gemini");
|
||||
@@ -3818,7 +3838,7 @@ mod tests {
|
||||
let converted_claude = convert_standard_chat_response(
|
||||
&provider_body_json,
|
||||
"openai:responses",
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
&report_context,
|
||||
)
|
||||
.expect("canonical openai responses -> claude");
|
||||
@@ -3832,7 +3852,7 @@ mod tests {
|
||||
let converted_gemini = convert_standard_chat_response(
|
||||
&provider_body_json,
|
||||
"openai:responses",
|
||||
"gemini:chat",
|
||||
"gemini:generate_content",
|
||||
&report_context,
|
||||
)
|
||||
.expect("canonical openai responses -> gemini");
|
||||
@@ -3866,7 +3886,7 @@ mod tests {
|
||||
#[test]
|
||||
fn builds_openai_chat_cross_format_sync_product_from_gemini_body_json() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "gemini:chat",
|
||||
"provider_api_format": "gemini:generate_content",
|
||||
"client_api_format": "openai:chat",
|
||||
"model": "gpt-5",
|
||||
"mapped_model": "gemini-2.5-pro",
|
||||
@@ -3957,7 +3977,7 @@ mod tests {
|
||||
"data: {\"responseId\":\"resp_cli_stream_123\",\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"Hello Gemini CLI\"}],\"role\":\"model\"},\"finishReason\":\"STOP\",\"index\":0}],\"modelVersion\":\"gemini-2.5-pro-upstream\",\"usageMetadata\":{\"promptTokenCount\":2,\"candidatesTokenCount\":3,\"totalTokenCount\":5}}\n\n",
|
||||
);
|
||||
let report_context = json!({
|
||||
"provider_api_format": "gemini:cli",
|
||||
"provider_api_format": "gemini:generate_content",
|
||||
"client_api_format": "openai:responses",
|
||||
"model": "gpt-5",
|
||||
"mapped_model": "gemini-2.5-pro-upstream",
|
||||
@@ -4155,7 +4175,7 @@ mod tests {
|
||||
#[test]
|
||||
fn standard_sync_finalize_product_handles_openai_chat_cross_format() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "claude:chat",
|
||||
"provider_api_format": "claude:messages",
|
||||
"client_api_format": "openai:chat",
|
||||
"model": "gpt-5",
|
||||
"mapped_model": "claude-sonnet-4",
|
||||
@@ -4192,7 +4212,7 @@ mod tests {
|
||||
"data: {\"responseId\":\"resp_cli_stream_123\",\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"Hello Gemini CLI\"}],\"role\":\"model\"},\"finishReason\":\"STOP\",\"index\":0}],\"modelVersion\":\"gemini-2.5-pro-upstream\",\"usageMetadata\":{\"promptTokenCount\":2,\"candidatesTokenCount\":3,\"totalTokenCount\":5}}\n\n",
|
||||
);
|
||||
let report_context = json!({
|
||||
"provider_api_format": "gemini:cli",
|
||||
"provider_api_format": "gemini:generate_content",
|
||||
"client_api_format": "openai:responses",
|
||||
"model": "gpt-5",
|
||||
"mapped_model": "gemini-2.5-pro-upstream",
|
||||
@@ -4218,7 +4238,7 @@ mod tests {
|
||||
fn standard_sync_finalize_product_falls_back_to_generic_standard_cross_format() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "openai:chat",
|
||||
"client_api_format": "claude:chat",
|
||||
"client_api_format": "claude:messages",
|
||||
});
|
||||
let provider_body_json = json!({
|
||||
"id": "chatcmpl_123",
|
||||
@@ -4255,7 +4275,7 @@ mod tests {
|
||||
fn multi_choice_openai_chat_response_keeps_legacy_fallback_route() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "openai:chat",
|
||||
"client_api_format": "claude:chat",
|
||||
"client_api_format": "claude:messages",
|
||||
"model": "gpt-5",
|
||||
"mapped_model": "gpt-5-upstream",
|
||||
});
|
||||
@@ -4288,7 +4308,7 @@ mod tests {
|
||||
let converted = convert_standard_chat_response(
|
||||
&provider_body_json,
|
||||
"openai:chat",
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
&report_context,
|
||||
)
|
||||
.expect("fallback route");
|
||||
@@ -4298,7 +4318,7 @@ mod tests {
|
||||
#[test]
|
||||
fn multi_candidate_gemini_response_keeps_legacy_fallback_route() {
|
||||
let report_context = json!({
|
||||
"provider_api_format": "gemini:chat",
|
||||
"provider_api_format": "gemini:generate_content",
|
||||
"client_api_format": "openai:chat",
|
||||
"model": "gemini-2.5-pro",
|
||||
"mapped_model": "gemini-upstream",
|
||||
@@ -4330,7 +4350,7 @@ mod tests {
|
||||
.expect("legacy multi candidate route");
|
||||
let converted = convert_standard_chat_response(
|
||||
&provider_body_json,
|
||||
"gemini:chat",
|
||||
"gemini:generate_content",
|
||||
"openai:chat",
|
||||
&report_context,
|
||||
)
|
||||
|
||||
@@ -60,18 +60,21 @@ mod tests {
|
||||
"codex",
|
||||
"openai:responses"
|
||||
));
|
||||
assert!(force_upstream_streaming_for_provider("codex", "openai:cli"));
|
||||
assert!(!force_upstream_streaming_for_provider(
|
||||
"codex",
|
||||
"openai:responses:compact"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_force_streaming_for_compact_or_other_provider_types() {
|
||||
assert!(!force_upstream_streaming_for_provider(
|
||||
"codex",
|
||||
"openai:compact"
|
||||
"openai:responses:compact"
|
||||
));
|
||||
assert!(!force_upstream_streaming_for_provider(
|
||||
"openai",
|
||||
"openai:cli"
|
||||
"openai:responses"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,28 +22,28 @@ pub struct LocalSameFormatProviderSpec {
|
||||
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalSameFormatProviderSpec> {
|
||||
match plan_kind {
|
||||
CLAUDE_CHAT_SYNC_PLAN_KIND => Some(LocalSameFormatProviderSpec {
|
||||
api_format: "claude:chat",
|
||||
api_format: "claude:messages",
|
||||
decision_kind: CLAUDE_CHAT_SYNC_PLAN_KIND,
|
||||
report_kind: "claude_chat_sync_success",
|
||||
family: LocalSameFormatProviderFamily::Standard,
|
||||
require_streaming: false,
|
||||
}),
|
||||
CLAUDE_CLI_SYNC_PLAN_KIND => Some(LocalSameFormatProviderSpec {
|
||||
api_format: "claude:cli",
|
||||
api_format: "claude:messages",
|
||||
decision_kind: CLAUDE_CLI_SYNC_PLAN_KIND,
|
||||
report_kind: "claude_cli_sync_success",
|
||||
family: LocalSameFormatProviderFamily::Standard,
|
||||
require_streaming: false,
|
||||
}),
|
||||
GEMINI_CHAT_SYNC_PLAN_KIND => Some(LocalSameFormatProviderSpec {
|
||||
api_format: "gemini:chat",
|
||||
api_format: "gemini:generate_content",
|
||||
decision_kind: GEMINI_CHAT_SYNC_PLAN_KIND,
|
||||
report_kind: "gemini_chat_sync_success",
|
||||
family: LocalSameFormatProviderFamily::Gemini,
|
||||
require_streaming: false,
|
||||
}),
|
||||
GEMINI_CLI_SYNC_PLAN_KIND => Some(LocalSameFormatProviderSpec {
|
||||
api_format: "gemini:cli",
|
||||
api_format: "gemini:generate_content",
|
||||
decision_kind: GEMINI_CLI_SYNC_PLAN_KIND,
|
||||
report_kind: "gemini_cli_sync_success",
|
||||
family: LocalSameFormatProviderFamily::Gemini,
|
||||
@@ -56,28 +56,28 @@ pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalSameFormatProviderSpec>
|
||||
pub fn resolve_stream_spec(plan_kind: &str) -> Option<LocalSameFormatProviderSpec> {
|
||||
match plan_kind {
|
||||
CLAUDE_CHAT_STREAM_PLAN_KIND => Some(LocalSameFormatProviderSpec {
|
||||
api_format: "claude:chat",
|
||||
api_format: "claude:messages",
|
||||
decision_kind: CLAUDE_CHAT_STREAM_PLAN_KIND,
|
||||
report_kind: "claude_chat_stream_success",
|
||||
family: LocalSameFormatProviderFamily::Standard,
|
||||
require_streaming: true,
|
||||
}),
|
||||
CLAUDE_CLI_STREAM_PLAN_KIND => Some(LocalSameFormatProviderSpec {
|
||||
api_format: "claude:cli",
|
||||
api_format: "claude:messages",
|
||||
decision_kind: CLAUDE_CLI_STREAM_PLAN_KIND,
|
||||
report_kind: "claude_cli_stream_success",
|
||||
family: LocalSameFormatProviderFamily::Standard,
|
||||
require_streaming: true,
|
||||
}),
|
||||
GEMINI_CHAT_STREAM_PLAN_KIND => Some(LocalSameFormatProviderSpec {
|
||||
api_format: "gemini:chat",
|
||||
api_format: "gemini:generate_content",
|
||||
decision_kind: GEMINI_CHAT_STREAM_PLAN_KIND,
|
||||
report_kind: "gemini_chat_stream_success",
|
||||
family: LocalSameFormatProviderFamily::Gemini,
|
||||
require_streaming: true,
|
||||
}),
|
||||
GEMINI_CLI_STREAM_PLAN_KIND => Some(LocalSameFormatProviderSpec {
|
||||
api_format: "gemini:cli",
|
||||
api_format: "gemini:generate_content",
|
||||
decision_kind: GEMINI_CLI_STREAM_PLAN_KIND,
|
||||
report_kind: "gemini_cli_stream_success",
|
||||
family: LocalSameFormatProviderFamily::Gemini,
|
||||
@@ -94,7 +94,7 @@ mod tests {
|
||||
#[test]
|
||||
fn resolves_claude_sync_same_format_spec() {
|
||||
let spec = resolve_sync_spec("claude_chat_sync").expect("spec");
|
||||
assert_eq!(spec.api_format, "claude:chat");
|
||||
assert_eq!(spec.api_format, "claude:messages");
|
||||
assert_eq!(spec.report_kind, "claude_chat_sync_success");
|
||||
assert!(!spec.require_streaming);
|
||||
}
|
||||
@@ -102,7 +102,7 @@ mod tests {
|
||||
#[test]
|
||||
fn resolves_gemini_stream_same_format_spec() {
|
||||
let spec = resolve_stream_spec("gemini_cli_stream").expect("spec");
|
||||
assert_eq!(spec.api_format, "gemini:cli");
|
||||
assert_eq!(spec.api_format, "gemini:generate_content");
|
||||
assert_eq!(spec.report_kind, "gemini_cli_stream_success");
|
||||
assert!(spec.require_streaming);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ pub fn resolve_execution_runtime_stream_plan_kind(
|
||||
}
|
||||
|
||||
if route_family == Some("claude")
|
||||
&& route_kind == Some("chat")
|
||||
&& is_claude_messages_route_kind(route_kind)
|
||||
&& *method == Method::POST
|
||||
&& path == "/v1/messages"
|
||||
{
|
||||
@@ -59,7 +59,7 @@ pub fn resolve_execution_runtime_stream_plan_kind(
|
||||
}
|
||||
|
||||
if route_family == Some("gemini")
|
||||
&& route_kind == Some("chat")
|
||||
&& is_gemini_generate_content_route_kind(route_kind)
|
||||
&& *method == Method::POST
|
||||
&& path.ends_with(":streamGenerateContent")
|
||||
{
|
||||
@@ -206,7 +206,7 @@ pub fn resolve_execution_runtime_sync_plan_kind(
|
||||
}
|
||||
|
||||
if route_family == Some("claude")
|
||||
&& route_kind == Some("chat")
|
||||
&& is_claude_messages_route_kind(route_kind)
|
||||
&& *method == Method::POST
|
||||
&& path == "/v1/messages"
|
||||
{
|
||||
@@ -222,7 +222,7 @@ pub fn resolve_execution_runtime_sync_plan_kind(
|
||||
}
|
||||
|
||||
if route_family == Some("gemini")
|
||||
&& route_kind == Some("chat")
|
||||
&& is_gemini_generate_content_route_kind(route_kind)
|
||||
&& *method == Method::POST
|
||||
&& path.ends_with(":generateContent")
|
||||
{
|
||||
@@ -269,6 +269,14 @@ fn is_openai_responses_compact_route_kind(route_kind: Option<&str>) -> bool {
|
||||
matches!(route_kind, Some("responses:compact") | Some("compact"))
|
||||
}
|
||||
|
||||
fn is_claude_messages_route_kind(route_kind: Option<&str>) -> bool {
|
||||
matches!(route_kind, Some("messages") | Some("chat"))
|
||||
}
|
||||
|
||||
fn is_gemini_generate_content_route_kind(route_kind: Option<&str>) -> bool {
|
||||
matches!(route_kind, Some("generate_content") | Some("chat"))
|
||||
}
|
||||
|
||||
pub fn is_matching_stream_request(
|
||||
plan_kind: &str,
|
||||
path: &str,
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::planner::standard::family::{
|
||||
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||
match plan_kind {
|
||||
CLAUDE_CHAT_SYNC_PLAN_KIND => Some(LocalStandardSpec {
|
||||
api_format: "claude:chat",
|
||||
api_format: "claude:messages",
|
||||
decision_kind: CLAUDE_CHAT_SYNC_PLAN_KIND,
|
||||
report_kind: "claude_chat_sync_finalize",
|
||||
family: LocalStandardSourceFamily::Standard,
|
||||
@@ -20,7 +20,7 @@ pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||
pub fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||
match plan_kind {
|
||||
CLAUDE_CHAT_STREAM_PLAN_KIND => Some(LocalStandardSpec {
|
||||
api_format: "claude:chat",
|
||||
api_format: "claude:messages",
|
||||
decision_kind: CLAUDE_CHAT_STREAM_PLAN_KIND,
|
||||
report_kind: "claude_chat_stream_success",
|
||||
family: LocalStandardSourceFamily::Standard,
|
||||
@@ -38,7 +38,7 @@ mod tests {
|
||||
#[test]
|
||||
fn resolves_claude_chat_sync_spec() {
|
||||
let spec = resolve_sync_spec("claude_chat_sync").expect("spec");
|
||||
assert_eq!(spec.api_format, "claude:chat");
|
||||
assert_eq!(spec.api_format, "claude:messages");
|
||||
assert_eq!(spec.report_kind, "claude_chat_sync_finalize");
|
||||
assert!(!spec.require_streaming);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ mod tests {
|
||||
#[test]
|
||||
fn resolves_claude_chat_stream_spec() {
|
||||
let spec = resolve_stream_spec("claude_chat_stream").expect("spec");
|
||||
assert_eq!(spec.api_format, "claude:chat");
|
||||
assert_eq!(spec.api_format, "claude:messages");
|
||||
assert_eq!(spec.report_kind, "claude_chat_stream_success");
|
||||
assert!(spec.require_streaming);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::planner::standard::family::{
|
||||
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||
match plan_kind {
|
||||
CLAUDE_CLI_SYNC_PLAN_KIND => Some(LocalStandardSpec {
|
||||
api_format: "claude:cli",
|
||||
api_format: "claude:messages",
|
||||
decision_kind: CLAUDE_CLI_SYNC_PLAN_KIND,
|
||||
report_kind: "claude_cli_sync_finalize",
|
||||
family: LocalStandardSourceFamily::Standard,
|
||||
@@ -20,7 +20,7 @@ pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||
pub fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||
match plan_kind {
|
||||
CLAUDE_CLI_STREAM_PLAN_KIND => Some(LocalStandardSpec {
|
||||
api_format: "claude:cli",
|
||||
api_format: "claude:messages",
|
||||
decision_kind: CLAUDE_CLI_STREAM_PLAN_KIND,
|
||||
report_kind: "claude_cli_stream_success",
|
||||
family: LocalStandardSourceFamily::Standard,
|
||||
@@ -38,7 +38,7 @@ mod tests {
|
||||
#[test]
|
||||
fn resolves_claude_cli_sync_spec() {
|
||||
let spec = resolve_sync_spec("claude_cli_sync").expect("spec");
|
||||
assert_eq!(spec.api_format, "claude:cli");
|
||||
assert_eq!(spec.api_format, "claude:messages");
|
||||
assert_eq!(spec.report_kind, "claude_cli_sync_finalize");
|
||||
assert!(!spec.require_streaming);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ mod tests {
|
||||
#[test]
|
||||
fn resolves_claude_cli_stream_spec() {
|
||||
let spec = resolve_stream_spec("claude_cli_stream").expect("spec");
|
||||
assert_eq!(spec.api_format, "claude:cli");
|
||||
assert_eq!(spec.api_format, "claude:messages");
|
||||
assert_eq!(spec.report_kind, "claude_cli_stream_success");
|
||||
assert!(spec.require_streaming);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::planner::standard::family::{
|
||||
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||
match plan_kind {
|
||||
GEMINI_CHAT_SYNC_PLAN_KIND => Some(LocalStandardSpec {
|
||||
api_format: "gemini:chat",
|
||||
api_format: "gemini:generate_content",
|
||||
decision_kind: GEMINI_CHAT_SYNC_PLAN_KIND,
|
||||
report_kind: "gemini_chat_sync_finalize",
|
||||
family: LocalStandardSourceFamily::Gemini,
|
||||
@@ -20,7 +20,7 @@ pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||
pub fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||
match plan_kind {
|
||||
GEMINI_CHAT_STREAM_PLAN_KIND => Some(LocalStandardSpec {
|
||||
api_format: "gemini:chat",
|
||||
api_format: "gemini:generate_content",
|
||||
decision_kind: GEMINI_CHAT_STREAM_PLAN_KIND,
|
||||
report_kind: "gemini_chat_stream_success",
|
||||
family: LocalStandardSourceFamily::Gemini,
|
||||
@@ -38,7 +38,7 @@ mod tests {
|
||||
#[test]
|
||||
fn resolves_gemini_chat_sync_spec() {
|
||||
let spec = resolve_sync_spec("gemini_chat_sync").expect("spec");
|
||||
assert_eq!(spec.api_format, "gemini:chat");
|
||||
assert_eq!(spec.api_format, "gemini:generate_content");
|
||||
assert_eq!(spec.report_kind, "gemini_chat_sync_finalize");
|
||||
assert!(!spec.require_streaming);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ mod tests {
|
||||
#[test]
|
||||
fn resolves_gemini_chat_stream_spec() {
|
||||
let spec = resolve_stream_spec("gemini_chat_stream").expect("spec");
|
||||
assert_eq!(spec.api_format, "gemini:chat");
|
||||
assert_eq!(spec.api_format, "gemini:generate_content");
|
||||
assert_eq!(spec.report_kind, "gemini_chat_stream_success");
|
||||
assert!(spec.require_streaming);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::planner::standard::family::{
|
||||
pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||
match plan_kind {
|
||||
GEMINI_CLI_SYNC_PLAN_KIND => Some(LocalStandardSpec {
|
||||
api_format: "gemini:cli",
|
||||
api_format: "gemini:generate_content",
|
||||
decision_kind: GEMINI_CLI_SYNC_PLAN_KIND,
|
||||
report_kind: "gemini_cli_sync_finalize",
|
||||
family: LocalStandardSourceFamily::Gemini,
|
||||
@@ -20,7 +20,7 @@ pub fn resolve_sync_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||
pub fn resolve_stream_spec(plan_kind: &str) -> Option<LocalStandardSpec> {
|
||||
match plan_kind {
|
||||
GEMINI_CLI_STREAM_PLAN_KIND => Some(LocalStandardSpec {
|
||||
api_format: "gemini:cli",
|
||||
api_format: "gemini:generate_content",
|
||||
decision_kind: GEMINI_CLI_STREAM_PLAN_KIND,
|
||||
report_kind: "gemini_cli_stream_success",
|
||||
family: LocalStandardSourceFamily::Gemini,
|
||||
@@ -38,7 +38,7 @@ mod tests {
|
||||
#[test]
|
||||
fn resolves_gemini_cli_sync_spec() {
|
||||
let spec = resolve_sync_spec("gemini_cli_sync").expect("spec");
|
||||
assert_eq!(spec.api_format, "gemini:cli");
|
||||
assert_eq!(spec.api_format, "gemini:generate_content");
|
||||
assert_eq!(spec.report_kind, "gemini_cli_sync_finalize");
|
||||
assert!(!spec.require_streaming);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ mod tests {
|
||||
#[test]
|
||||
fn resolves_gemini_cli_stream_spec() {
|
||||
let spec = resolve_stream_spec("gemini_cli_stream").expect("spec");
|
||||
assert_eq!(spec.api_format, "gemini:cli");
|
||||
assert_eq!(spec.api_format, "gemini:generate_content");
|
||||
assert_eq!(spec.report_kind, "gemini_cli_stream_success");
|
||||
assert!(spec.require_streaming);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ pub fn build_standard_request_body_from_canonical(
|
||||
provider_api_format: &str,
|
||||
upstream_is_stream: bool,
|
||||
) -> Option<Value> {
|
||||
match aether_ai_formats::normalize_legacy_openai_format_alias(provider_api_format).as_str() {
|
||||
match aether_ai_formats::normalize_api_format_alias(provider_api_format).as_str() {
|
||||
"openai:chat" => build_local_openai_chat_request_body(
|
||||
canonical_request,
|
||||
mapped_model,
|
||||
@@ -85,12 +85,12 @@ pub fn build_standard_request_body_from_canonical(
|
||||
false,
|
||||
true,
|
||||
),
|
||||
"claude:chat" | "claude:cli" => convert_openai_chat_request_to_claude_request(
|
||||
"claude:messages" => convert_openai_chat_request_to_claude_request(
|
||||
canonical_request,
|
||||
mapped_model,
|
||||
upstream_is_stream,
|
||||
),
|
||||
"gemini:chat" | "gemini:cli" => convert_openai_chat_request_to_gemini_request(
|
||||
"gemini:generate_content" => convert_openai_chat_request_to_gemini_request(
|
||||
canonical_request,
|
||||
mapped_model,
|
||||
upstream_is_stream,
|
||||
@@ -117,15 +117,15 @@ fn normalize_standard_request_to_openai_chat_request_cow<'a>(
|
||||
client_api_format: &str,
|
||||
request_path: &str,
|
||||
) -> Option<Cow<'a, Value>> {
|
||||
match aether_ai_formats::normalize_legacy_openai_format_alias(client_api_format).as_str() {
|
||||
match aether_ai_formats::normalize_api_format_alias(client_api_format).as_str() {
|
||||
"openai:chat" => Some(Cow::Borrowed(body_json)),
|
||||
"openai:responses" | "openai:responses:compact" => {
|
||||
normalize_openai_responses_request_to_openai_chat_request(body_json).map(Cow::Owned)
|
||||
}
|
||||
"claude:chat" | "claude:cli" => {
|
||||
"claude:messages" => {
|
||||
normalize_claude_request_to_openai_chat_request(body_json).map(Cow::Owned)
|
||||
}
|
||||
"gemini:chat" | "gemini:cli" => {
|
||||
"gemini:generate_content" => {
|
||||
normalize_gemini_request_to_openai_chat_request(body_json, request_path).map(Cow::Owned)
|
||||
}
|
||||
_ => None,
|
||||
@@ -162,10 +162,8 @@ mod tests {
|
||||
const STANDARD_SURFACES: &[&str] = &[
|
||||
"openai:chat",
|
||||
"openai:responses",
|
||||
"claude:chat",
|
||||
"claude:cli",
|
||||
"gemini:chat",
|
||||
"gemini:cli",
|
||||
"claude:messages",
|
||||
"gemini:generate_content",
|
||||
];
|
||||
|
||||
fn sample_request_for(api_format: &str) -> (Value, &'static str) {
|
||||
@@ -190,7 +188,7 @@ mod tests {
|
||||
}),
|
||||
"/v1/responses",
|
||||
),
|
||||
"claude:chat" | "claude:cli" => (
|
||||
"claude:messages" => (
|
||||
json!({
|
||||
"model": "source-model",
|
||||
"system": "Be concise.",
|
||||
@@ -202,7 +200,7 @@ mod tests {
|
||||
}),
|
||||
"/v1/messages",
|
||||
),
|
||||
"gemini:chat" | "gemini:cli" => (
|
||||
"gemini:generate_content" => (
|
||||
json!({
|
||||
"systemInstruction": {
|
||||
"parts": [{"text": "Be concise."}]
|
||||
@@ -223,7 +221,7 @@ mod tests {
|
||||
|
||||
fn assert_stream_flag(provider_api_format: &str, upstream_is_stream: bool, converted: &Value) {
|
||||
match provider_api_format {
|
||||
"openai:chat" | "openai:responses" | "claude:chat" | "claude:cli" => {
|
||||
"openai:chat" | "openai:responses" | "claude:messages" => {
|
||||
assert_eq!(
|
||||
converted
|
||||
.get("stream")
|
||||
@@ -233,7 +231,7 @@ mod tests {
|
||||
"{provider_api_format} stream flag should follow upstream_is_stream"
|
||||
);
|
||||
}
|
||||
"gemini:chat" | "gemini:cli" => {
|
||||
"gemini:generate_content" => {
|
||||
assert!(
|
||||
converted.get("stream").is_none(),
|
||||
"gemini streaming is represented by endpoint URL, not request body"
|
||||
@@ -299,7 +297,7 @@ mod tests {
|
||||
) -> Value {
|
||||
let chat_canonical = normalize_standard_request_to_openai_chat_request(
|
||||
request,
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
"/v1/messages",
|
||||
)
|
||||
.expect("legacy claude normalization should succeed");
|
||||
@@ -319,7 +317,7 @@ mod tests {
|
||||
) -> Value {
|
||||
let chat_canonical = normalize_standard_request_to_openai_chat_request(
|
||||
request,
|
||||
"gemini:chat",
|
||||
"gemini:generate_content",
|
||||
"/v1beta/models/source-model:generateContent",
|
||||
)
|
||||
.expect("legacy gemini normalization should succeed");
|
||||
@@ -607,13 +605,12 @@ mod tests {
|
||||
"openai:chat",
|
||||
"openai:responses",
|
||||
"openai:responses:compact",
|
||||
"gemini:chat",
|
||||
"gemini:cli",
|
||||
"gemini:generate_content",
|
||||
] {
|
||||
for upstream_is_stream in [false, true] {
|
||||
let converted = build_standard_request_body(
|
||||
&request,
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
"mapped-model",
|
||||
"custom",
|
||||
provider_api_format,
|
||||
@@ -627,7 +624,7 @@ mod tests {
|
||||
legacy_claude_request_body(&request, provider_api_format, upstream_is_stream);
|
||||
assert_eq!(
|
||||
converted, legacy,
|
||||
"typed canonical claude:chat -> {provider_api_format} changed payload with upstream_is_stream={upstream_is_stream}"
|
||||
"typed canonical claude:messages -> {provider_api_format} changed payload with upstream_is_stream={upstream_is_stream}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -689,13 +686,12 @@ mod tests {
|
||||
"openai:chat",
|
||||
"openai:responses",
|
||||
"openai:responses:compact",
|
||||
"claude:chat",
|
||||
"claude:cli",
|
||||
"claude:messages",
|
||||
] {
|
||||
for upstream_is_stream in [false, true] {
|
||||
let converted = build_standard_request_body(
|
||||
&request,
|
||||
"gemini:chat",
|
||||
"gemini:generate_content",
|
||||
"mapped-model",
|
||||
"custom",
|
||||
provider_api_format,
|
||||
@@ -709,7 +705,7 @@ mod tests {
|
||||
legacy_gemini_request_body(&request, provider_api_format, upstream_is_stream);
|
||||
assert_eq!(
|
||||
converted, legacy,
|
||||
"typed canonical gemini:chat -> {provider_api_format} changed payload with upstream_is_stream={upstream_is_stream}"
|
||||
"typed canonical gemini:generate_content -> {provider_api_format} changed payload with upstream_is_stream={upstream_is_stream}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -770,7 +766,7 @@ mod tests {
|
||||
|
||||
let converted = build_standard_request_body(
|
||||
&request,
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
"gpt-5",
|
||||
"openai",
|
||||
"openai:chat",
|
||||
@@ -801,7 +797,7 @@ mod tests {
|
||||
|
||||
let converted = build_standard_request_body(
|
||||
&request,
|
||||
"gemini:chat",
|
||||
"gemini:generate_content",
|
||||
"gpt-5",
|
||||
"openai",
|
||||
"openai:chat",
|
||||
@@ -835,10 +831,10 @@ mod tests {
|
||||
|
||||
let converted = build_standard_request_body(
|
||||
&request,
|
||||
"gemini:chat",
|
||||
"gemini:generate_content",
|
||||
"claude-sonnet-4-5",
|
||||
"anthropic",
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
"/v1beta/models/gemini-2.5-pro:generateContent",
|
||||
false,
|
||||
None,
|
||||
@@ -871,10 +867,10 @@ mod tests {
|
||||
|
||||
let converted = build_standard_request_body(
|
||||
&request,
|
||||
"claude:cli",
|
||||
"claude:messages",
|
||||
"gemini-2.5-pro",
|
||||
"google",
|
||||
"gemini:cli",
|
||||
"gemini:generate_content",
|
||||
"/v1/messages",
|
||||
false,
|
||||
None,
|
||||
@@ -1000,7 +996,7 @@ mod tests {
|
||||
"openai:chat",
|
||||
"gemini-2.5-pro",
|
||||
"google",
|
||||
"gemini:chat",
|
||||
"gemini:generate_content",
|
||||
"/v1/chat/completions",
|
||||
false,
|
||||
None,
|
||||
@@ -1052,7 +1048,7 @@ mod tests {
|
||||
"openai:chat",
|
||||
"claude-sonnet-4-5",
|
||||
"anthropic",
|
||||
"claude:chat",
|
||||
"claude:messages",
|
||||
"/v1/chat/completions",
|
||||
false,
|
||||
None,
|
||||
@@ -1073,7 +1069,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn openai_responses_nested_tools_survive_claude_cli_and_kiro_envelope_conversion() {
|
||||
fn openai_responses_nested_tools_survive_claude_messages_and_kiro_envelope_conversion() {
|
||||
let request = json!({
|
||||
"model": "gpt-5",
|
||||
"input": "Use the weather tool for Shanghai.",
|
||||
@@ -1102,13 +1098,13 @@ mod tests {
|
||||
"openai:responses",
|
||||
"claude-sonnet-4.6",
|
||||
"kiro",
|
||||
"claude:cli",
|
||||
"claude:messages",
|
||||
"/v1/responses",
|
||||
true,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.expect("openai responses should convert to claude cli");
|
||||
.expect("openai responses should convert to claude messages");
|
||||
assert_eq!(claude["tools"][0]["name"], "get_weather");
|
||||
assert_eq!(claude["tool_choice"]["name"], "get_weather");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user