2026-04-14 01:27:04 +08:00
|
|
|
use std::collections::BTreeMap;
|
2026-04-21 16:19:07 +08:00
|
|
|
use std::sync::Arc;
|
2026-04-14 01:27:04 +08:00
|
|
|
|
|
|
|
|
use serde_json::Value;
|
|
|
|
|
|
2026-05-02 13:23:54 +08:00
|
|
|
use crate::ai_serving::planner::candidate_preparation::{
|
|
|
|
|
prepare_header_authenticated_candidate, prepare_header_authenticated_candidate_from_auth,
|
|
|
|
|
OauthPreparationContext,
|
2026-04-14 01:27:04 +08:00
|
|
|
};
|
2026-05-02 13:23:54 +08:00
|
|
|
use crate::ai_serving::planner::common::force_upstream_streaming_for_provider;
|
|
|
|
|
use crate::ai_serving::planner::spec_metadata::local_standard_spec_metadata;
|
|
|
|
|
use crate::ai_serving::planner::standard::{
|
2026-04-26 20:32:55 +08:00
|
|
|
apply_codex_openai_responses_special_headers, request_body_build_failure_extra_data,
|
2026-04-26 01:38:37 +08:00
|
|
|
};
|
2026-05-02 13:23:54 +08:00
|
|
|
use crate::ai_serving::transport::kiro::{
|
|
|
|
|
build_kiro_provider_headers, build_kiro_provider_request_body,
|
|
|
|
|
is_kiro_claude_messages_transport, KiroProviderHeadersInput, KiroRequestAuth,
|
|
|
|
|
KIRO_ENVELOPE_NAME,
|
2026-04-14 01:27:04 +08:00
|
|
|
};
|
2026-05-02 13:23:54 +08:00
|
|
|
use crate::ai_serving::transport::{
|
|
|
|
|
build_kiro_cross_format_upstream_url, build_standard_provider_request_headers,
|
|
|
|
|
StandardProviderRequestHeadersInput,
|
2026-04-24 20:23:02 +08:00
|
|
|
};
|
2026-05-02 13:23:54 +08:00
|
|
|
use crate::ai_serving::{
|
2026-04-26 01:38:37 +08:00
|
|
|
CandidateFailureDiagnostic, GatewayProviderTransportSnapshot, LocalResolvedOAuthRequestAuth,
|
|
|
|
|
};
|
2026-04-14 01:27:04 +08:00
|
|
|
use crate::AppState;
|
|
|
|
|
|
2026-04-26 01:38:37 +08:00
|
|
|
use super::payload::{
|
|
|
|
|
mark_skipped_local_standard_candidate, mark_skipped_local_standard_candidate_with_extra_data,
|
|
|
|
|
mark_skipped_local_standard_candidate_with_failure_diagnostic,
|
|
|
|
|
};
|
2026-04-14 01:27:04 +08:00
|
|
|
use super::{LocalStandardCandidateAttempt, LocalStandardDecisionInput, LocalStandardSpec};
|
|
|
|
|
|
|
|
|
|
pub(crate) struct LocalStandardCandidatePayloadParts {
|
|
|
|
|
pub(super) auth_header: String,
|
|
|
|
|
pub(super) auth_value: String,
|
|
|
|
|
pub(super) mapped_model: String,
|
|
|
|
|
pub(super) provider_api_format: String,
|
|
|
|
|
pub(super) provider_request_body: Value,
|
|
|
|
|
pub(super) provider_request_headers: BTreeMap<String, String>,
|
|
|
|
|
pub(super) upstream_url: String,
|
|
|
|
|
pub(super) upstream_is_stream: bool,
|
2026-04-24 20:23:02 +08:00
|
|
|
pub(super) envelope_name: Option<&'static str>,
|
2026-04-21 16:19:07 +08:00
|
|
|
pub(super) transport: Arc<GatewayProviderTransportSnapshot>,
|
2026-04-14 01:27:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) async fn resolve_local_standard_candidate_payload_parts(
|
|
|
|
|
state: &AppState,
|
|
|
|
|
parts: &http::request::Parts,
|
|
|
|
|
trace_id: &str,
|
|
|
|
|
body_json: &serde_json::Value,
|
|
|
|
|
input: &LocalStandardDecisionInput,
|
|
|
|
|
attempt: &LocalStandardCandidateAttempt,
|
|
|
|
|
spec: LocalStandardSpec,
|
|
|
|
|
) -> Option<LocalStandardCandidatePayloadParts> {
|
|
|
|
|
let spec_metadata = local_standard_spec_metadata(spec);
|
2026-05-02 13:23:54 +08:00
|
|
|
let planner_state = crate::ai_serving::PlannerAppState::new(state);
|
2026-04-14 01:27:04 +08:00
|
|
|
let candidate = &attempt.eligible.candidate;
|
|
|
|
|
let transport = &attempt.eligible.transport;
|
|
|
|
|
let provider_api_format = attempt.eligible.provider_api_format.as_str();
|
2026-05-02 13:23:54 +08:00
|
|
|
let is_kiro_claude_cli = is_kiro_claude_messages_transport(transport, provider_api_format);
|
|
|
|
|
let Some(conversion_kind) =
|
|
|
|
|
crate::ai_serving::request_conversion_kind(spec_metadata.api_format, provider_api_format)
|
|
|
|
|
else {
|
2026-04-14 01:27:04 +08:00
|
|
|
return None;
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-02 13:23:54 +08:00
|
|
|
if let Some(skip_reason) = crate::ai_serving::request_conversion_transport_unsupported_reason(
|
|
|
|
|
transport,
|
|
|
|
|
conversion_kind,
|
|
|
|
|
) {
|
2026-04-14 01:27:04 +08:00
|
|
|
mark_skipped_local_standard_candidate(
|
|
|
|
|
state,
|
|
|
|
|
input,
|
|
|
|
|
trace_id,
|
|
|
|
|
candidate,
|
|
|
|
|
attempt.candidate_index,
|
|
|
|
|
&attempt.candidate_id,
|
2026-04-14 11:50:52 +08:00
|
|
|
skip_reason,
|
2026-04-14 01:27:04 +08:00
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-24 20:23:02 +08:00
|
|
|
let oauth_context = OauthPreparationContext {
|
|
|
|
|
trace_id,
|
|
|
|
|
api_format: provider_api_format,
|
|
|
|
|
operation: "standard_family_cross_format",
|
|
|
|
|
};
|
|
|
|
|
let kiro_auth = if is_kiro_claude_cli {
|
2026-05-02 13:23:54 +08:00
|
|
|
match crate::ai_serving::planner::candidate_preparation::resolve_candidate_oauth_auth(
|
2026-04-24 20:23:02 +08:00
|
|
|
planner_state,
|
|
|
|
|
transport,
|
|
|
|
|
oauth_context,
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
{
|
|
|
|
|
Some(LocalResolvedOAuthRequestAuth::Kiro(auth)) => Some(auth),
|
|
|
|
|
_ => {
|
|
|
|
|
mark_skipped_local_standard_candidate(
|
|
|
|
|
state,
|
|
|
|
|
input,
|
|
|
|
|
trace_id,
|
|
|
|
|
candidate,
|
|
|
|
|
attempt.candidate_index,
|
|
|
|
|
&attempt.candidate_id,
|
|
|
|
|
"transport_auth_unavailable",
|
|
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let prepared_candidate = if let Some(kiro_auth) = kiro_auth.as_ref() {
|
2026-05-02 13:23:54 +08:00
|
|
|
match prepare_header_authenticated_candidate_from_auth(
|
|
|
|
|
candidate,
|
|
|
|
|
kiro_auth.name.to_string(),
|
|
|
|
|
kiro_auth.value.clone(),
|
|
|
|
|
) {
|
|
|
|
|
Ok(prepared) => prepared,
|
|
|
|
|
Err(skip_reason) => {
|
|
|
|
|
mark_skipped_local_standard_candidate(
|
|
|
|
|
state,
|
|
|
|
|
input,
|
|
|
|
|
trace_id,
|
|
|
|
|
candidate,
|
|
|
|
|
attempt.candidate_index,
|
|
|
|
|
&attempt.candidate_id,
|
|
|
|
|
skip_reason,
|
|
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
return None;
|
|
|
|
|
}
|
2026-04-24 20:23:02 +08:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
match prepare_header_authenticated_candidate(
|
|
|
|
|
planner_state,
|
|
|
|
|
transport,
|
|
|
|
|
candidate,
|
2026-05-02 13:23:54 +08:00
|
|
|
crate::ai_serving::request_conversion_direct_auth(transport, conversion_kind),
|
2026-04-24 20:23:02 +08:00
|
|
|
oauth_context,
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
{
|
|
|
|
|
Ok(prepared) => prepared,
|
|
|
|
|
Err(skip_reason) => {
|
|
|
|
|
mark_skipped_local_standard_candidate(
|
|
|
|
|
state,
|
|
|
|
|
input,
|
|
|
|
|
trace_id,
|
|
|
|
|
candidate,
|
|
|
|
|
attempt.candidate_index,
|
|
|
|
|
&attempt.candidate_id,
|
|
|
|
|
skip_reason,
|
|
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
return None;
|
|
|
|
|
}
|
2026-04-14 01:27:04 +08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let upstream_is_stream = spec_metadata.require_streaming
|
|
|
|
|
|| force_upstream_streaming_for_provider(
|
|
|
|
|
transport.provider.provider_type.as_str(),
|
|
|
|
|
provider_api_format,
|
|
|
|
|
);
|
|
|
|
|
let provider_request_body =
|
2026-05-02 13:23:54 +08:00
|
|
|
match crate::ai_serving::planner::standard::build_standard_request_body(
|
2026-04-14 01:27:04 +08:00
|
|
|
body_json,
|
|
|
|
|
spec_metadata.api_format,
|
|
|
|
|
&prepared_candidate.mapped_model,
|
|
|
|
|
transport.provider.provider_type.as_str(),
|
|
|
|
|
provider_api_format,
|
|
|
|
|
parts.uri.path(),
|
|
|
|
|
upstream_is_stream,
|
2026-04-24 20:23:02 +08:00
|
|
|
if is_kiro_claude_cli {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
transport.endpoint.body_rules.as_ref()
|
|
|
|
|
},
|
2026-04-14 01:27:04 +08:00
|
|
|
Some(input.auth_context.api_key_id.as_str()),
|
|
|
|
|
) {
|
|
|
|
|
Some(body) => body,
|
|
|
|
|
None => {
|
2026-04-26 01:38:37 +08:00
|
|
|
mark_skipped_local_standard_candidate_with_extra_data(
|
2026-04-14 01:27:04 +08:00
|
|
|
state,
|
|
|
|
|
input,
|
|
|
|
|
trace_id,
|
|
|
|
|
candidate,
|
|
|
|
|
attempt.candidate_index,
|
|
|
|
|
&attempt.candidate_id,
|
2026-04-26 01:38:37 +08:00
|
|
|
"provider_request_body_build_failed",
|
|
|
|
|
request_body_build_failure_extra_data(
|
|
|
|
|
body_json,
|
|
|
|
|
spec_metadata.api_format,
|
|
|
|
|
provider_api_format,
|
|
|
|
|
),
|
2026-04-14 01:27:04 +08:00
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-24 20:23:02 +08:00
|
|
|
if let Some(kiro_auth) = kiro_auth.as_ref() {
|
|
|
|
|
return build_kiro_cross_format_payload_parts(
|
|
|
|
|
state,
|
|
|
|
|
parts,
|
|
|
|
|
trace_id,
|
|
|
|
|
body_json,
|
|
|
|
|
input,
|
|
|
|
|
attempt,
|
|
|
|
|
transport,
|
|
|
|
|
provider_api_format,
|
|
|
|
|
prepared_candidate.mapped_model,
|
|
|
|
|
prepared_candidate.auth_header,
|
|
|
|
|
prepared_candidate.auth_value,
|
|
|
|
|
provider_request_body,
|
|
|
|
|
upstream_is_stream,
|
|
|
|
|
kiro_auth,
|
|
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-02 13:23:54 +08:00
|
|
|
let upstream_url = match crate::ai_serving::planner::standard::build_standard_upstream_url(
|
2026-04-14 01:27:04 +08:00
|
|
|
parts,
|
|
|
|
|
transport,
|
|
|
|
|
&prepared_candidate.mapped_model,
|
|
|
|
|
provider_api_format,
|
|
|
|
|
upstream_is_stream,
|
|
|
|
|
) {
|
|
|
|
|
Some(url) => url,
|
|
|
|
|
None => {
|
2026-04-26 01:38:37 +08:00
|
|
|
mark_skipped_local_standard_candidate_with_failure_diagnostic(
|
2026-04-14 01:27:04 +08:00
|
|
|
state,
|
|
|
|
|
input,
|
|
|
|
|
trace_id,
|
|
|
|
|
candidate,
|
|
|
|
|
attempt.candidate_index,
|
|
|
|
|
&attempt.candidate_id,
|
|
|
|
|
"upstream_url_missing",
|
2026-04-26 01:38:37 +08:00
|
|
|
CandidateFailureDiagnostic::upstream_url_missing(
|
|
|
|
|
spec_metadata.api_format,
|
|
|
|
|
provider_api_format,
|
|
|
|
|
"standard_family_url",
|
|
|
|
|
),
|
2026-04-14 01:27:04 +08:00
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-05-02 13:23:54 +08:00
|
|
|
let Some(resolved_headers) =
|
|
|
|
|
build_standard_provider_request_headers(StandardProviderRequestHeadersInput {
|
|
|
|
|
transport,
|
|
|
|
|
provider_api_format,
|
|
|
|
|
same_format: false,
|
|
|
|
|
headers: &parts.headers,
|
|
|
|
|
auth_header: &prepared_candidate.auth_header,
|
|
|
|
|
auth_value: &prepared_candidate.auth_value,
|
|
|
|
|
extra_headers: &BTreeMap::new(),
|
|
|
|
|
header_rules: transport.endpoint.header_rules.as_ref(),
|
|
|
|
|
provider_request_body: &provider_request_body,
|
|
|
|
|
original_request_body: body_json,
|
|
|
|
|
upstream_is_stream,
|
|
|
|
|
})
|
|
|
|
|
else {
|
2026-04-26 01:38:37 +08:00
|
|
|
mark_skipped_local_standard_candidate_with_failure_diagnostic(
|
2026-04-14 01:27:04 +08:00
|
|
|
state,
|
|
|
|
|
input,
|
|
|
|
|
trace_id,
|
|
|
|
|
candidate,
|
|
|
|
|
attempt.candidate_index,
|
|
|
|
|
&attempt.candidate_id,
|
|
|
|
|
"transport_header_rules_apply_failed",
|
2026-04-26 01:38:37 +08:00
|
|
|
CandidateFailureDiagnostic::header_rules_apply_failed(
|
|
|
|
|
spec_metadata.api_format,
|
|
|
|
|
provider_api_format,
|
|
|
|
|
"standard_family_headers",
|
|
|
|
|
),
|
2026-04-14 01:27:04 +08:00
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
return None;
|
2026-05-02 13:23:54 +08:00
|
|
|
};
|
|
|
|
|
let mut provider_request_headers = resolved_headers.headers;
|
2026-04-26 20:32:55 +08:00
|
|
|
apply_codex_openai_responses_special_headers(
|
2026-04-14 01:27:04 +08:00
|
|
|
&mut provider_request_headers,
|
|
|
|
|
&provider_request_body,
|
|
|
|
|
&parts.headers,
|
|
|
|
|
transport.provider.provider_type.as_str(),
|
|
|
|
|
provider_api_format,
|
|
|
|
|
Some(trace_id),
|
|
|
|
|
transport.key.decrypted_auth_config.as_deref(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Some(LocalStandardCandidatePayloadParts {
|
2026-05-02 13:23:54 +08:00
|
|
|
auth_header: resolved_headers.auth_header,
|
|
|
|
|
auth_value: resolved_headers.auth_value,
|
2026-04-14 01:27:04 +08:00
|
|
|
mapped_model: prepared_candidate.mapped_model,
|
|
|
|
|
provider_api_format: provider_api_format.to_string(),
|
|
|
|
|
provider_request_body,
|
|
|
|
|
provider_request_headers,
|
|
|
|
|
upstream_url,
|
|
|
|
|
upstream_is_stream,
|
2026-04-24 20:23:02 +08:00
|
|
|
envelope_name: None,
|
|
|
|
|
transport: Arc::clone(transport),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
|
async fn build_kiro_cross_format_payload_parts(
|
|
|
|
|
state: &AppState,
|
|
|
|
|
parts: &http::request::Parts,
|
|
|
|
|
trace_id: &str,
|
|
|
|
|
original_body_json: &serde_json::Value,
|
|
|
|
|
input: &LocalStandardDecisionInput,
|
|
|
|
|
attempt: &LocalStandardCandidateAttempt,
|
|
|
|
|
transport: &Arc<GatewayProviderTransportSnapshot>,
|
|
|
|
|
provider_api_format: &str,
|
|
|
|
|
mapped_model: String,
|
|
|
|
|
auth_header: String,
|
|
|
|
|
auth_value: String,
|
|
|
|
|
claude_request_body: Value,
|
|
|
|
|
upstream_is_stream: bool,
|
|
|
|
|
kiro_auth: &KiroRequestAuth,
|
|
|
|
|
) -> Option<LocalStandardCandidatePayloadParts> {
|
|
|
|
|
let candidate = &attempt.eligible.candidate;
|
|
|
|
|
let provider_request_body = match build_kiro_provider_request_body(
|
|
|
|
|
&claude_request_body,
|
|
|
|
|
&mapped_model,
|
|
|
|
|
&kiro_auth.auth_config,
|
|
|
|
|
transport.endpoint.body_rules.as_ref(),
|
|
|
|
|
) {
|
|
|
|
|
Some(body) => body,
|
|
|
|
|
None => {
|
2026-04-26 01:38:37 +08:00
|
|
|
mark_skipped_local_standard_candidate_with_extra_data(
|
2026-04-24 20:23:02 +08:00
|
|
|
state,
|
|
|
|
|
input,
|
|
|
|
|
trace_id,
|
|
|
|
|
candidate,
|
|
|
|
|
attempt.candidate_index,
|
|
|
|
|
&attempt.candidate_id,
|
2026-04-26 01:38:37 +08:00
|
|
|
"provider_request_body_build_failed",
|
|
|
|
|
request_body_build_failure_extra_data(
|
|
|
|
|
&claude_request_body,
|
|
|
|
|
provider_api_format,
|
|
|
|
|
provider_api_format,
|
|
|
|
|
),
|
2026-04-24 20:23:02 +08:00
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-05-02 13:23:54 +08:00
|
|
|
let upstream_url = match build_kiro_cross_format_upstream_url(
|
2026-04-24 20:23:02 +08:00
|
|
|
transport,
|
2026-05-02 13:23:54 +08:00
|
|
|
&mapped_model,
|
2026-04-24 20:23:02 +08:00
|
|
|
provider_api_format,
|
|
|
|
|
upstream_is_stream,
|
|
|
|
|
parts.uri.query(),
|
2026-05-02 13:23:54 +08:00
|
|
|
kiro_auth.auth_config.effective_api_region(),
|
2026-04-24 20:23:02 +08:00
|
|
|
) {
|
|
|
|
|
Some(url) => url,
|
|
|
|
|
None => {
|
2026-04-26 01:38:37 +08:00
|
|
|
mark_skipped_local_standard_candidate_with_failure_diagnostic(
|
2026-04-24 20:23:02 +08:00
|
|
|
state,
|
|
|
|
|
input,
|
|
|
|
|
trace_id,
|
|
|
|
|
candidate,
|
|
|
|
|
attempt.candidate_index,
|
|
|
|
|
&attempt.candidate_id,
|
|
|
|
|
"upstream_url_missing",
|
2026-04-26 01:38:37 +08:00
|
|
|
CandidateFailureDiagnostic::upstream_url_missing(
|
|
|
|
|
provider_api_format,
|
|
|
|
|
provider_api_format,
|
|
|
|
|
"standard_family_kiro_url",
|
|
|
|
|
),
|
2026-04-24 20:23:02 +08:00
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let provider_request_headers = match build_kiro_provider_headers(KiroProviderHeadersInput {
|
|
|
|
|
headers: &parts.headers,
|
|
|
|
|
provider_request_body: &provider_request_body,
|
|
|
|
|
original_request_body: original_body_json,
|
|
|
|
|
header_rules: transport.endpoint.header_rules.as_ref(),
|
|
|
|
|
auth_header: &auth_header,
|
|
|
|
|
auth_value: &auth_value,
|
|
|
|
|
auth_config: &kiro_auth.auth_config,
|
|
|
|
|
machine_id: kiro_auth.machine_id.as_str(),
|
|
|
|
|
}) {
|
|
|
|
|
Some(headers) => headers,
|
|
|
|
|
None => {
|
2026-04-26 01:38:37 +08:00
|
|
|
mark_skipped_local_standard_candidate_with_failure_diagnostic(
|
2026-04-24 20:23:02 +08:00
|
|
|
state,
|
|
|
|
|
input,
|
|
|
|
|
trace_id,
|
|
|
|
|
candidate,
|
|
|
|
|
attempt.candidate_index,
|
|
|
|
|
&attempt.candidate_id,
|
|
|
|
|
"transport_header_rules_apply_failed",
|
2026-04-26 01:38:37 +08:00
|
|
|
CandidateFailureDiagnostic::header_rules_apply_failed(
|
|
|
|
|
provider_api_format,
|
|
|
|
|
provider_api_format,
|
|
|
|
|
"standard_family_kiro_headers",
|
|
|
|
|
),
|
2026-04-24 20:23:02 +08:00
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Some(LocalStandardCandidatePayloadParts {
|
|
|
|
|
auth_header,
|
|
|
|
|
auth_value,
|
|
|
|
|
mapped_model,
|
|
|
|
|
provider_api_format: provider_api_format.to_string(),
|
|
|
|
|
provider_request_body,
|
|
|
|
|
provider_request_headers,
|
|
|
|
|
upstream_url,
|
|
|
|
|
upstream_is_stream,
|
|
|
|
|
envelope_name: Some(KIRO_ENVELOPE_NAME),
|
2026-04-21 16:19:07 +08:00
|
|
|
transport: Arc::clone(transport),
|
2026-04-14 01:27:04 +08:00
|
|
|
})
|
|
|
|
|
}
|