mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
feat: configure auth channel mismatch formats
This commit is contained in:
@@ -69,6 +69,7 @@ struct GatewayLocalCandidateMaterializationPort<'a, F, G> {
|
||||
auth_snapshot: Option<&'a GatewayAuthApiKeySnapshot>,
|
||||
required_capabilities: Option<&'a Value>,
|
||||
sticky_session_token: Option<&'a str>,
|
||||
request_auth_channel: Option<&'a str>,
|
||||
persistence_policy: LocalCandidatePersistencePolicy<'a>,
|
||||
resolution_mode: LocalCandidateResolutionMode,
|
||||
build_available_extra_data: F,
|
||||
@@ -123,6 +124,7 @@ where
|
||||
self.auth_snapshot,
|
||||
self.required_capabilities,
|
||||
self.sticky_session_token,
|
||||
self.request_auth_channel,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -135,6 +137,7 @@ where
|
||||
self.auth_snapshot,
|
||||
self.required_capabilities,
|
||||
self.sticky_session_token,
|
||||
self.request_auth_channel,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -314,6 +317,7 @@ pub(crate) async fn materialize_local_execution_candidates_with_serving<F, G>(
|
||||
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
|
||||
required_capabilities: Option<&Value>,
|
||||
sticky_session_token: Option<&str>,
|
||||
request_auth_channel: Option<&str>,
|
||||
persistence_policy: LocalCandidatePersistencePolicy<'_>,
|
||||
candidates: Vec<SchedulerMinimalCandidateSelectionCandidate>,
|
||||
preselection_skipped: Vec<SkippedLocalExecutionCandidate>,
|
||||
@@ -333,6 +337,7 @@ where
|
||||
auth_snapshot,
|
||||
required_capabilities,
|
||||
sticky_session_token,
|
||||
request_auth_channel,
|
||||
persistence_policy,
|
||||
resolution_mode,
|
||||
build_available_extra_data,
|
||||
@@ -682,6 +687,7 @@ mod tests {
|
||||
is_active: true,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
auth_type_by_format: None,
|
||||
allow_auth_channel_mismatch_formats: None,
|
||||
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
|
||||
@@ -182,6 +182,7 @@ mod tests {
|
||||
is_active: true,
|
||||
api_formats: None,
|
||||
auth_type_by_format: None,
|
||||
allow_auth_channel_mismatch_formats: None,
|
||||
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
@@ -240,6 +241,7 @@ mod tests {
|
||||
is_active: true,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
auth_type_by_format: None,
|
||||
allow_auth_channel_mismatch_formats: None,
|
||||
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
|
||||
@@ -136,6 +136,7 @@ mod tests {
|
||||
is_active: true,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
auth_type_by_format: None,
|
||||
allow_auth_channel_mismatch_formats: None,
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
|
||||
@@ -1048,6 +1048,7 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1125,6 +1126,7 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1198,6 +1200,7 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1262,6 +1265,7 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1342,6 +1346,7 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1413,6 +1418,7 @@ mod tests {
|
||||
Some(&auth_snapshot),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1488,6 +1494,7 @@ mod tests {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1580,6 +1587,7 @@ mod tests {
|
||||
Some(&auth_snapshot),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1680,6 +1688,7 @@ mod tests {
|
||||
Some(&auth_snapshot),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1781,6 +1790,7 @@ mod tests {
|
||||
Some(&auth_snapshot),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1882,6 +1892,7 @@ mod tests {
|
||||
Some(&auth_snapshot),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ struct GatewayLocalCandidateResolutionPort<'a> {
|
||||
auth_snapshot: Option<&'a GatewayAuthApiKeySnapshot>,
|
||||
required_capabilities: Option<&'a serde_json::Value>,
|
||||
sticky_session_token: Option<&'a str>,
|
||||
request_auth_channel: Option<&'a str>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -86,6 +87,11 @@ impl AiCandidateResolutionPort for GatewayLocalCandidateResolutionPort<'_> {
|
||||
transport: &Self::Transport,
|
||||
requested_model: Option<&str>,
|
||||
) -> Option<&'static str> {
|
||||
if let Some(skip_reason) =
|
||||
candidate_auth_channel_skip_reason(transport, self.request_auth_channel)
|
||||
{
|
||||
return Some(skip_reason);
|
||||
}
|
||||
candidate_common_transport_skip_reason(
|
||||
transport,
|
||||
candidate_transport_policy_facts(candidate),
|
||||
@@ -169,6 +175,7 @@ pub(crate) async fn resolve_and_rank_local_execution_candidates(
|
||||
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
|
||||
required_capabilities: Option<&serde_json::Value>,
|
||||
sticky_session_token: Option<&str>,
|
||||
request_auth_channel: Option<&str>,
|
||||
) -> (
|
||||
Vec<EligibleLocalExecutionCandidate>,
|
||||
Vec<SkippedLocalExecutionCandidate>,
|
||||
@@ -182,6 +189,7 @@ pub(crate) async fn resolve_and_rank_local_execution_candidates(
|
||||
auth_snapshot,
|
||||
required_capabilities,
|
||||
sticky_session_token,
|
||||
request_auth_channel,
|
||||
AiCandidateResolutionMode::Standard,
|
||||
)
|
||||
.await
|
||||
@@ -195,6 +203,7 @@ pub(crate) async fn resolve_and_rank_local_execution_candidates_without_transpor
|
||||
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
|
||||
required_capabilities: Option<&serde_json::Value>,
|
||||
sticky_session_token: Option<&str>,
|
||||
request_auth_channel: Option<&str>,
|
||||
) -> (
|
||||
Vec<EligibleLocalExecutionCandidate>,
|
||||
Vec<SkippedLocalExecutionCandidate>,
|
||||
@@ -208,6 +217,7 @@ pub(crate) async fn resolve_and_rank_local_execution_candidates_without_transpor
|
||||
auth_snapshot,
|
||||
required_capabilities,
|
||||
sticky_session_token,
|
||||
request_auth_channel,
|
||||
AiCandidateResolutionMode::WithoutTransportPairGate,
|
||||
)
|
||||
.await
|
||||
@@ -221,6 +231,7 @@ async fn resolve_and_rank_local_execution_candidates_with_mode(
|
||||
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
|
||||
required_capabilities: Option<&serde_json::Value>,
|
||||
sticky_session_token: Option<&str>,
|
||||
request_auth_channel: Option<&str>,
|
||||
mode: AiCandidateResolutionMode,
|
||||
) -> (
|
||||
Vec<EligibleLocalExecutionCandidate>,
|
||||
@@ -232,6 +243,7 @@ async fn resolve_and_rank_local_execution_candidates_with_mode(
|
||||
auth_snapshot,
|
||||
required_capabilities,
|
||||
sticky_session_token,
|
||||
request_auth_channel,
|
||||
};
|
||||
|
||||
let request = AiCandidateResolutionRequest {
|
||||
@@ -257,6 +269,87 @@ fn candidate_transport_policy_facts(
|
||||
}
|
||||
}
|
||||
|
||||
fn candidate_auth_channel_skip_reason(
|
||||
transport: &GatewayProviderTransportSnapshot,
|
||||
request_auth_channel: Option<&str>,
|
||||
) -> Option<&'static str> {
|
||||
let request_auth_channel = normalize_request_auth_channel(request_auth_channel?)?;
|
||||
let upstream_auth_channel = resolve_transport_request_auth_channel(transport)?;
|
||||
if request_auth_channel == upstream_auth_channel
|
||||
|| allow_auth_channel_mismatch_for_format(transport)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some("auth_channel_mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_request_auth_channel(value: &str) -> Option<&'static str> {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"api_key" | "api-key" | "apikey" => Some("api_key"),
|
||||
"bearer_like" | "bearer-like" | "bearer" | "oauth" => Some("bearer_like"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_transport_request_auth_channel(
|
||||
transport: &GatewayProviderTransportSnapshot,
|
||||
) -> Option<&'static str> {
|
||||
let auth_type = resolve_transport_auth_type_for_endpoint_format(transport);
|
||||
match auth_type.as_str() {
|
||||
"api_key" => Some("api_key"),
|
||||
"bearer" => Some("bearer_like"),
|
||||
"oauth" if provider_uses_bearer_like_oauth(&transport.provider.provider_type) => {
|
||||
Some("bearer_like")
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_transport_auth_type_for_endpoint_format(
|
||||
transport: &GatewayProviderTransportSnapshot,
|
||||
) -> String {
|
||||
let default_auth_type = transport.key.auth_type.trim().to_ascii_lowercase();
|
||||
let api_format = crate::ai_serving::normalize_api_format_alias(&transport.endpoint.api_format);
|
||||
transport
|
||||
.key
|
||||
.auth_type_by_format
|
||||
.as_ref()
|
||||
.and_then(serde_json::Value::as_object)
|
||||
.and_then(|overrides| {
|
||||
overrides
|
||||
.get(&api_format)
|
||||
.or_else(|| overrides.get(transport.endpoint.api_format.trim()))
|
||||
})
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.map(str::trim)
|
||||
.map(str::to_ascii_lowercase)
|
||||
.filter(|value| matches!(value.as_str(), "api_key" | "bearer"))
|
||||
.unwrap_or(default_auth_type)
|
||||
}
|
||||
|
||||
fn provider_uses_bearer_like_oauth(provider_type: &str) -> bool {
|
||||
matches!(
|
||||
provider_type.trim().to_ascii_lowercase().as_str(),
|
||||
"claude_code" | "gemini_cli" | "antigravity" | "kiro"
|
||||
)
|
||||
}
|
||||
|
||||
fn allow_auth_channel_mismatch_for_format(transport: &GatewayProviderTransportSnapshot) -> bool {
|
||||
let api_format = crate::ai_serving::normalize_api_format_alias(&transport.endpoint.api_format);
|
||||
transport
|
||||
.key
|
||||
.allow_auth_channel_mismatch_formats
|
||||
.as_ref()
|
||||
.and_then(serde_json::Value::as_array)
|
||||
.is_some_and(|items| {
|
||||
items
|
||||
.iter()
|
||||
.filter_map(serde_json::Value::as_str)
|
||||
.any(|item| crate::ai_serving::normalize_api_format_alias(item) == api_format)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn read_candidate_transport_snapshot(
|
||||
state: PlannerAppState<'_>,
|
||||
candidate: &SchedulerMinimalCandidateSelectionCandidate,
|
||||
@@ -285,3 +378,102 @@ pub(crate) async fn read_candidate_transport_snapshot(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::candidate_auth_channel_skip_reason;
|
||||
use crate::ai_serving::GatewayProviderTransportSnapshot;
|
||||
use aether_provider_transport::snapshot::{
|
||||
GatewayProviderTransportEndpoint, GatewayProviderTransportKey,
|
||||
GatewayProviderTransportProvider,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
fn sample_transport(auth_type: &str) -> GatewayProviderTransportSnapshot {
|
||||
GatewayProviderTransportSnapshot {
|
||||
provider: GatewayProviderTransportProvider {
|
||||
id: "provider-1".to_string(),
|
||||
name: "provider".to_string(),
|
||||
provider_type: "custom".to_string(),
|
||||
website: None,
|
||||
is_active: true,
|
||||
keep_priority_on_conversion: false,
|
||||
enable_format_conversion: false,
|
||||
concurrent_limit: None,
|
||||
max_retries: None,
|
||||
proxy: None,
|
||||
request_timeout_secs: None,
|
||||
stream_first_byte_timeout_secs: None,
|
||||
config: None,
|
||||
},
|
||||
endpoint: GatewayProviderTransportEndpoint {
|
||||
id: "endpoint-1".to_string(),
|
||||
provider_id: "provider-1".to_string(),
|
||||
api_format: "claude:messages".to_string(),
|
||||
api_family: Some("claude".to_string()),
|
||||
endpoint_kind: Some("messages".to_string()),
|
||||
is_active: true,
|
||||
base_url: "https://example.test".to_string(),
|
||||
header_rules: None,
|
||||
body_rules: None,
|
||||
max_retries: None,
|
||||
custom_path: None,
|
||||
config: None,
|
||||
format_acceptance_config: None,
|
||||
proxy: None,
|
||||
},
|
||||
key: GatewayProviderTransportKey {
|
||||
id: "key-1".to_string(),
|
||||
provider_id: "provider-1".to_string(),
|
||||
name: "key".to_string(),
|
||||
auth_type: auth_type.to_string(),
|
||||
is_active: true,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
auth_type_by_format: None,
|
||||
allow_auth_channel_mismatch_formats: None,
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
global_priority_by_format: None,
|
||||
expires_at_unix_secs: None,
|
||||
proxy: None,
|
||||
fingerprint: None,
|
||||
decrypted_api_key: "secret".to_string(),
|
||||
decrypted_auth_config: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auth_channel_gate_skips_mismatched_raw_secret_auth() {
|
||||
let transport = sample_transport("bearer");
|
||||
assert_eq!(
|
||||
candidate_auth_channel_skip_reason(&transport, Some("api_key")),
|
||||
Some("auth_channel_mismatch")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auth_channel_gate_allows_explicit_mismatch_format() {
|
||||
let mut transport = sample_transport("bearer");
|
||||
transport.key.allow_auth_channel_mismatch_formats = Some(json!(["claude:messages"]));
|
||||
assert_eq!(
|
||||
candidate_auth_channel_skip_reason(&transport, Some("api_key")),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auth_channel_gate_treats_cli_oauth_provider_as_bearer_like() {
|
||||
let mut transport = sample_transport("oauth");
|
||||
transport.provider.provider_type = "claude_code".to_string();
|
||||
assert_eq!(
|
||||
candidate_auth_channel_skip_reason(&transport, Some("bearer_like")),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
candidate_auth_channel_skip_reason(&transport, Some("api_key")),
|
||||
Some("auth_channel_mismatch")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ pub(crate) struct LocalRequestedModelDecisionInput {
|
||||
pub(crate) requested_model: String,
|
||||
pub(crate) auth_snapshot: GatewayAuthApiKeySnapshot,
|
||||
pub(crate) required_capabilities: Option<serde_json::Value>,
|
||||
pub(crate) request_auth_channel: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -93,6 +94,7 @@ pub(crate) fn build_local_requested_model_decision_input(
|
||||
requested_model,
|
||||
auth_snapshot: resolved_input.auth_snapshot,
|
||||
required_capabilities: resolved_input.required_capabilities,
|
||||
request_auth_channel: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,10 +71,9 @@ pub(crate) async fn resolve_local_same_format_provider_decision_input(
|
||||
}
|
||||
};
|
||||
|
||||
Some(build_local_requested_model_decision_input(
|
||||
resolved_input,
|
||||
requested_model,
|
||||
))
|
||||
let mut input = build_local_requested_model_decision_input(resolved_input, requested_model);
|
||||
input.request_auth_channel = decision.request_auth_channel.clone();
|
||||
Some(input)
|
||||
}
|
||||
|
||||
pub(crate) async fn materialize_local_same_format_provider_candidate_attempts(
|
||||
@@ -110,6 +109,7 @@ pub(crate) async fn materialize_local_same_format_provider_candidate_attempts(
|
||||
Some(&input.auth_snapshot),
|
||||
input.required_capabilities.as_ref(),
|
||||
sticky_session_token.as_deref(),
|
||||
input.request_auth_channel.as_deref(),
|
||||
persistence_policy,
|
||||
candidates,
|
||||
preselection_skipped
|
||||
|
||||
@@ -1232,6 +1232,7 @@ mod tests {
|
||||
is_active: true,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
auth_type_by_format: None,
|
||||
allow_auth_channel_mismatch_formats: None,
|
||||
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
|
||||
@@ -92,6 +92,7 @@ pub(super) async fn materialize_local_gemini_files_candidate_attempts(
|
||||
Some(&input.auth_snapshot),
|
||||
input.required_capabilities.as_ref(),
|
||||
None,
|
||||
None,
|
||||
persistence_policy,
|
||||
candidates,
|
||||
Vec::new(),
|
||||
|
||||
@@ -65,10 +65,9 @@ pub(super) async fn resolve_local_openai_image_decision_input(
|
||||
}
|
||||
};
|
||||
|
||||
Some(build_local_requested_model_decision_input(
|
||||
resolved_input,
|
||||
requested_model,
|
||||
))
|
||||
let mut input = build_local_requested_model_decision_input(resolved_input, requested_model);
|
||||
input.request_auth_channel = decision.request_auth_channel.clone();
|
||||
Some(input)
|
||||
}
|
||||
|
||||
fn resolve_local_openai_image_auth_context(
|
||||
@@ -155,6 +154,7 @@ async fn materialize_local_openai_image_candidate_attempts(
|
||||
Some(&input.auth_snapshot),
|
||||
input.required_capabilities.as_ref(),
|
||||
sticky_session_token.as_deref(),
|
||||
input.request_auth_channel.as_deref(),
|
||||
persistence_policy,
|
||||
candidates,
|
||||
preselection_skipped,
|
||||
|
||||
@@ -73,10 +73,9 @@ pub(super) async fn resolve_local_video_create_decision_input(
|
||||
}
|
||||
};
|
||||
|
||||
Some(build_local_requested_model_decision_input(
|
||||
resolved_input,
|
||||
requested_model,
|
||||
))
|
||||
let mut input = build_local_requested_model_decision_input(resolved_input, requested_model);
|
||||
input.request_auth_channel = decision.request_auth_channel.clone();
|
||||
Some(input)
|
||||
}
|
||||
|
||||
fn resolve_local_video_create_auth_context(
|
||||
@@ -167,6 +166,7 @@ async fn materialize_local_video_create_candidate_attempts(
|
||||
Some(&input.auth_snapshot),
|
||||
input.required_capabilities.as_ref(),
|
||||
sticky_session_token.as_deref(),
|
||||
input.request_auth_channel.as_deref(),
|
||||
persistence_policy,
|
||||
candidates,
|
||||
preselection_skipped,
|
||||
|
||||
@@ -69,10 +69,9 @@ pub(super) async fn resolve_local_standard_decision_input(
|
||||
}
|
||||
};
|
||||
|
||||
Some(build_local_requested_model_decision_input(
|
||||
resolved_input,
|
||||
requested_model,
|
||||
))
|
||||
let mut input = build_local_requested_model_decision_input(resolved_input, requested_model);
|
||||
input.request_auth_channel = decision.request_auth_channel.clone();
|
||||
Some(input)
|
||||
}
|
||||
|
||||
pub(super) async fn materialize_local_standard_candidate_attempts(
|
||||
@@ -109,6 +108,7 @@ pub(super) async fn materialize_local_standard_candidate_attempts(
|
||||
Some(&input.auth_snapshot),
|
||||
input.required_capabilities.as_ref(),
|
||||
sticky_session_token.as_deref(),
|
||||
input.request_auth_channel.as_deref(),
|
||||
persistence_policy,
|
||||
preselection.candidates,
|
||||
preselection.skipped_candidates,
|
||||
|
||||
@@ -338,6 +338,7 @@ mod tests {
|
||||
requested_model: "claude-sonnet-4-5".to_string(),
|
||||
auth_snapshot: sample_auth_snapshot(),
|
||||
required_capabilities: None,
|
||||
request_auth_channel: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,6 +399,7 @@ mod tests {
|
||||
"openai:chat".to_string(),
|
||||
]),
|
||||
auth_type_by_format: None,
|
||||
allow_auth_channel_mismatch_formats: None,
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
|
||||
@@ -60,7 +60,7 @@ fn sample_transport(base_url: &str, api_format: &str) -> GatewayProviderTranspor
|
||||
is_active: true,
|
||||
api_formats: Some(vec![api_format.to_string()]),
|
||||
auth_type_by_format: None,
|
||||
|
||||
allow_auth_channel_mismatch_formats: None,
|
||||
allowed_models: None,
|
||||
capabilities: None,
|
||||
rate_multipliers: None,
|
||||
|
||||
@@ -135,6 +135,7 @@ pub(crate) async fn materialize_local_openai_chat_candidate_attempts(
|
||||
Some(&input.auth_snapshot),
|
||||
input.required_capabilities.as_ref(),
|
||||
sticky_session_token.as_deref(),
|
||||
input.request_auth_channel.as_deref(),
|
||||
persistence_policy,
|
||||
candidates,
|
||||
preselection_skipped,
|
||||
|
||||
@@ -104,8 +104,7 @@ pub(crate) async fn resolve_local_openai_chat_decision_input(
|
||||
}
|
||||
};
|
||||
|
||||
Some(build_local_requested_model_decision_input(
|
||||
resolved_input,
|
||||
requested_model,
|
||||
))
|
||||
let mut input = build_local_requested_model_decision_input(resolved_input, requested_model);
|
||||
input.request_auth_channel = decision.request_auth_channel.clone();
|
||||
Some(input)
|
||||
}
|
||||
|
||||
@@ -122,10 +122,9 @@ pub(crate) async fn resolve_local_openai_responses_decision_input(
|
||||
}
|
||||
};
|
||||
|
||||
Some(build_local_requested_model_decision_input(
|
||||
resolved_input,
|
||||
requested_model,
|
||||
))
|
||||
let mut input = build_local_requested_model_decision_input(resolved_input, requested_model);
|
||||
input.request_auth_channel = decision.request_auth_channel.clone();
|
||||
Some(input)
|
||||
}
|
||||
|
||||
pub(crate) async fn materialize_local_openai_responses_candidate_attempts(
|
||||
@@ -163,6 +162,7 @@ pub(crate) async fn materialize_local_openai_responses_candidate_attempts(
|
||||
Some(&input.auth_snapshot),
|
||||
input.required_capabilities.as_ref(),
|
||||
sticky_session_token.as_deref(),
|
||||
input.request_auth_channel.as_deref(),
|
||||
persistence_policy,
|
||||
preselection.candidates,
|
||||
preselection.skipped_candidates,
|
||||
|
||||
Reference in New Issue
Block a user