Remove ranked candidate data read wrappers

This commit is contained in:
fawney19
2026-04-27 15:39:23 +08:00
parent dda6f34a07
commit e5e09b49f4
4 changed files with 60 additions and 154 deletions

View File

@@ -1,9 +1,9 @@
use aether_data::DataLayerError; use aether_data::DataLayerError;
use aether_data_contracts::repository::candidate_selection::StoredMinimalCandidateSelectionRow; use aether_data_contracts::repository::candidate_selection::StoredMinimalCandidateSelectionRow;
use aether_scheduler_core::{ use aether_scheduler_core::{
auth_constraints_allow_api_format, build_ranked_minimal_candidate_selection, auth_constraints_allow_api_format, collect_global_model_names_for_required_capability,
collect_global_model_names_for_required_capability, enumerate_minimal_candidate_selection, enumerate_minimal_candidate_selection, normalize_api_format,
normalize_api_format, resolve_requested_global_model_name, row_supports_requested_model, resolve_requested_global_model_name, row_supports_requested_model,
BuildMinimalCandidateSelectionInput, SchedulerAuthConstraints, BuildMinimalCandidateSelectionInput, SchedulerAuthConstraints,
SchedulerMinimalCandidateSelectionCandidate, SchedulerPriorityMode, SchedulerMinimalCandidateSelectionCandidate, SchedulerPriorityMode,
}; };
@@ -51,129 +51,6 @@ pub(crate) async fn read_requested_model_rows(
Ok(Some((resolved_global_model_name, rows))) Ok(Some((resolved_global_model_name, rows)))
} }
pub(crate) async fn read_ranked_minimal_candidate_selection(
state: &(impl MinimalCandidateSelectionRowSource + Sync),
api_format: &str,
requested_model_name: &str,
require_streaming: bool,
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
) -> Result<Vec<SchedulerMinimalCandidateSelectionCandidate>, DataLayerError> {
read_ranked_minimal_candidate_selection_with_priority_mode(
state,
api_format,
requested_model_name,
require_streaming,
auth_snapshot,
SchedulerPriorityMode::Provider,
)
.await
}
pub(crate) async fn read_ranked_minimal_candidate_selection_with_priority_mode(
state: &(impl MinimalCandidateSelectionRowSource + Sync),
api_format: &str,
requested_model_name: &str,
require_streaming: bool,
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
priority_mode: SchedulerPriorityMode,
) -> Result<Vec<SchedulerMinimalCandidateSelectionCandidate>, DataLayerError> {
read_ranked_minimal_candidate_selection_with_priority_mode_and_affinity_key(
state,
api_format,
requested_model_name,
require_streaming,
auth_snapshot,
priority_mode,
auth_snapshot_affinity_key(auth_snapshot),
)
.await
}
pub(crate) async fn read_ranked_minimal_candidate_selection_with_priority_mode_and_required_capabilities(
state: &(impl MinimalCandidateSelectionRowSource + Sync),
api_format: &str,
requested_model_name: &str,
require_streaming: bool,
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
priority_mode: SchedulerPriorityMode,
required_capabilities: Option<&serde_json::Value>,
) -> Result<Vec<SchedulerMinimalCandidateSelectionCandidate>, DataLayerError> {
read_ranked_minimal_candidate_selection_with_priority_mode_and_affinity_key_and_required_capabilities(
state,
api_format,
requested_model_name,
require_streaming,
auth_snapshot,
priority_mode,
auth_snapshot_affinity_key(auth_snapshot),
required_capabilities,
)
.await
}
pub(crate) async fn read_ranked_minimal_candidate_selection_with_priority_mode_and_affinity_key(
state: &(impl MinimalCandidateSelectionRowSource + Sync),
api_format: &str,
requested_model_name: &str,
require_streaming: bool,
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
priority_mode: SchedulerPriorityMode,
affinity_key: Option<&str>,
) -> Result<Vec<SchedulerMinimalCandidateSelectionCandidate>, DataLayerError> {
read_ranked_minimal_candidate_selection_with_priority_mode_and_affinity_key_and_required_capabilities(
state,
api_format,
requested_model_name,
require_streaming,
auth_snapshot,
priority_mode,
affinity_key,
None,
)
.await
}
pub(crate) async fn read_ranked_minimal_candidate_selection_with_priority_mode_and_affinity_key_and_required_capabilities(
state: &(impl MinimalCandidateSelectionRowSource + Sync),
api_format: &str,
requested_model_name: &str,
require_streaming: bool,
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
priority_mode: SchedulerPriorityMode,
affinity_key: Option<&str>,
required_capabilities: Option<&serde_json::Value>,
) -> Result<Vec<SchedulerMinimalCandidateSelectionCandidate>, DataLayerError> {
let normalized_api_format = normalize_api_format(api_format);
if normalized_api_format.is_empty() {
return Ok(Vec::new());
}
if !auth_constraints_allow_api_format(
auth_snapshot.map(auth_snapshot_constraints).as_ref(),
&normalized_api_format,
) {
return Ok(Vec::new());
}
let Some((resolved_global_model_name, rows)) =
read_requested_model_rows(state, &normalized_api_format, requested_model_name).await?
else {
return Ok(Vec::new());
};
let auth_constraints = auth_snapshot.map(auth_snapshot_constraints);
build_ranked_minimal_candidate_selection(BuildMinimalCandidateSelectionInput {
rows,
normalized_api_format: &normalized_api_format,
requested_model_name,
resolved_global_model_name: resolved_global_model_name.as_str(),
require_streaming,
required_capabilities,
auth_constraints: auth_constraints.as_ref(),
affinity_key,
priority_mode,
})
}
pub(crate) async fn enumerate_minimal_candidate_selection_with_required_capabilities( pub(crate) async fn enumerate_minimal_candidate_selection_with_required_capabilities(
state: &(impl MinimalCandidateSelectionRowSource + Sync), state: &(impl MinimalCandidateSelectionRowSource + Sync),
api_format: &str, api_format: &str,

View File

@@ -9,12 +9,15 @@ use aether_data_contracts::repository::candidate_selection::StoredProviderModelM
use aether_data_contracts::repository::candidates::{ use aether_data_contracts::repository::candidates::{
RequestCandidateStatus, StoredRequestCandidate, RequestCandidateStatus, StoredRequestCandidate,
}; };
use aether_scheduler_core::SchedulerMinimalCandidateSelectionCandidate; use aether_scheduler_core::{
build_ranked_minimal_candidate_selection, BuildMinimalCandidateSelectionInput,
SchedulerMinimalCandidateSelectionCandidate, SchedulerPriorityMode,
};
use crate::cache::SchedulerAffinityTarget; use crate::cache::SchedulerAffinityTarget;
use crate::data::auth::GatewayAuthApiKeySnapshot; use crate::data::auth::GatewayAuthApiKeySnapshot;
use crate::data::candidate_selection::{ use crate::data::candidate_selection::{
read_ranked_minimal_candidate_selection, MinimalCandidateSelectionRowSource, read_requested_model_rows, MinimalCandidateSelectionRowSource,
}; };
use crate::data::GatewayDataState; use crate::data::GatewayDataState;
use crate::{AppState, GatewayError}; use crate::{AppState, GatewayError};
@@ -86,14 +89,22 @@ async fn same_priority_candidates_are_distributed_by_affinity_key() {
let state = GatewayDataState::with_candidate_selection_and_quota_for_tests(candidates, quotas); let state = GatewayDataState::with_candidate_selection_and_quota_for_tests(candidates, quotas);
let auth_snapshot = sample_auth_snapshot("affinity-key-1"); let auth_snapshot = sample_auth_snapshot("affinity-key-1");
let selection = read_ranked_minimal_candidate_selection( let (_resolved_global_model_name, rows) =
&state, read_requested_model_rows(&state, "openai:chat", "gpt-4.1")
"openai:chat",
"gpt-4.1",
false,
Some(&auth_snapshot),
)
.await .await
.expect("selection rows should read")
.expect("selection rows should match requested model");
let selection = build_ranked_minimal_candidate_selection(BuildMinimalCandidateSelectionInput {
rows,
normalized_api_format: "openai:chat",
requested_model_name: "gpt-4.1",
resolved_global_model_name: "gpt-4.1",
require_streaming: false,
required_capabilities: None,
auth_constraints: None,
affinity_key: Some(auth_snapshot.api_key_id.as_str()),
priority_mode: SchedulerPriorityMode::Provider,
})
.expect("selection should succeed"); .expect("selection should succeed");
assert_eq!(selection.len(), 2); assert_eq!(selection.len(), 2);

View File

@@ -7,7 +7,7 @@ use aether_scheduler_core::{
resolve_requested_global_model_name, SchedulerMinimalCandidateSelectionCandidate, resolve_requested_global_model_name, SchedulerMinimalCandidateSelectionCandidate,
}; };
use crate::data::candidate_selection::read_ranked_minimal_candidate_selection; use crate::data::candidate_selection::enumerate_minimal_candidate_selection_with_required_capabilities;
use crate::data::GatewayDataState; use crate::data::GatewayDataState;
use super::super::{ use super::super::{
@@ -119,7 +119,7 @@ fn scheduler_candidate_is_serializable() {
} }
#[tokio::test] #[tokio::test]
async fn read_ranked_minimal_candidate_selection_resolves_provider_model_alias() { async fn enumerate_minimal_candidate_selection_resolves_provider_model_alias() {
let mut row = sample_row(); let mut row = sample_row();
row.global_model_name = "gpt-5".to_string(); row.global_model_name = "gpt-5".to_string();
row.model_provider_model_name = "gpt-5.2".to_string(); row.model_provider_model_name = "gpt-5.2".to_string();
@@ -135,8 +135,14 @@ async fn read_ranked_minimal_candidate_selection_resolves_provider_model_alias()
let quotas = Arc::new(InMemoryProviderQuotaRepository::seed(vec![])); let quotas = Arc::new(InMemoryProviderQuotaRepository::seed(vec![]));
let state = GatewayDataState::with_candidate_selection_and_quota_for_tests(candidates, quotas); let state = GatewayDataState::with_candidate_selection_and_quota_for_tests(candidates, quotas);
let selection = let selection = enumerate_minimal_candidate_selection_with_required_capabilities(
read_ranked_minimal_candidate_selection(&state, "openai:chat", "gpt-5.2", false, None) &state,
"openai:chat",
"gpt-5.2",
false,
None,
None,
)
.await .await
.expect("selection should succeed"); .expect("selection should succeed");
@@ -146,7 +152,7 @@ async fn read_ranked_minimal_candidate_selection_resolves_provider_model_alias()
} }
#[tokio::test] #[tokio::test]
async fn read_ranked_minimal_candidate_selection_keeps_all_rows_supporting_requested_model() { async fn enumerate_minimal_candidate_selection_keeps_all_rows_supporting_requested_model() {
let mut exact = sample_row(); let mut exact = sample_row();
exact.provider_id = "provider-exact".to_string(); exact.provider_id = "provider-exact".to_string();
exact.endpoint_id = "endpoint-exact".to_string(); exact.endpoint_id = "endpoint-exact".to_string();
@@ -178,8 +184,14 @@ async fn read_ranked_minimal_candidate_selection_keeps_all_rows_supporting_reque
let quotas = Arc::new(InMemoryProviderQuotaRepository::seed(vec![])); let quotas = Arc::new(InMemoryProviderQuotaRepository::seed(vec![]));
let state = GatewayDataState::with_candidate_selection_and_quota_for_tests(candidates, quotas); let state = GatewayDataState::with_candidate_selection_and_quota_for_tests(candidates, quotas);
let selection = let selection = enumerate_minimal_candidate_selection_with_required_capabilities(
read_ranked_minimal_candidate_selection(&state, "openai:chat", "gpt-5", false, None) &state,
"openai:chat",
"gpt-5",
false,
None,
None,
)
.await .await
.expect("selection should succeed"); .expect("selection should succeed");
@@ -195,7 +207,7 @@ async fn read_ranked_minimal_candidate_selection_keeps_all_rows_supporting_reque
} }
#[tokio::test] #[tokio::test]
async fn read_ranked_minimal_candidate_selection_allows_resolved_global_model_in_auth_snapshot() { async fn enumerate_minimal_candidate_selection_allows_resolved_global_model_in_auth_snapshot() {
let mut row = sample_row(); let mut row = sample_row();
row.global_model_name = "gpt-5".to_string(); row.global_model_name = "gpt-5".to_string();
row.global_model_mappings = Some(vec!["gpt-5(?:\\.\\d+)?".to_string()]); row.global_model_mappings = Some(vec!["gpt-5(?:\\.\\d+)?".to_string()]);
@@ -215,12 +227,13 @@ async fn read_ranked_minimal_candidate_selection_allows_resolved_global_model_in
auth_snapshot.user_allowed_models = Some(vec!["gpt-5".to_string()]); auth_snapshot.user_allowed_models = Some(vec!["gpt-5".to_string()]);
auth_snapshot.api_key_allowed_models = Some(vec!["gpt-5".to_string()]); auth_snapshot.api_key_allowed_models = Some(vec!["gpt-5".to_string()]);
let selection = read_ranked_minimal_candidate_selection( let selection = enumerate_minimal_candidate_selection_with_required_capabilities(
&state, &state,
"openai:chat", "openai:chat",
"gpt-5.2", "gpt-5.2",
false, false,
Some(&auth_snapshot), Some(&auth_snapshot),
None,
) )
.await .await
.expect("selection should succeed"); .expect("selection should succeed");

View File

@@ -558,9 +558,10 @@ fn scheduler_candidate_runtime_paths_depend_on_scheduler_core_and_state_trait()
"data/candidate_selection.rs should host requested-model row lookup" "data/candidate_selection.rs should host requested-model row lookup"
); );
assert!( assert!(
candidate_selection candidate_selection.contains(
.contains("pub(crate) async fn read_ranked_minimal_candidate_selection("), "pub(crate) async fn enumerate_minimal_candidate_selection_with_required_capabilities(",
"data/candidate_selection.rs should host ranked minimal candidate selection builder" ),
"data/candidate_selection.rs should host minimal candidate enumeration builder"
); );
assert!( assert!(
candidate_selection candidate_selection
@@ -572,8 +573,12 @@ fn scheduler_candidate_runtime_paths_depend_on_scheduler_core_and_state_trait()
"data/candidate_selection.rs should depend on core requested-model resolver" "data/candidate_selection.rs should depend on core requested-model resolver"
); );
assert!( assert!(
candidate_selection.contains("build_ranked_minimal_candidate_selection"), !candidate_selection.contains("read_ranked_minimal_candidate_selection"),
"data/candidate_selection.rs should depend on core ranked minimal candidate builder" "data/candidate_selection.rs should not host ranked candidate selection compatibility readers"
);
assert!(
!candidate_selection.contains("build_ranked_minimal_candidate_selection"),
"data/candidate_selection.rs should not depend on core ranked minimal candidate builder"
); );
assert!( assert!(
candidate_selection.contains("collect_global_model_names_for_required_capability"), candidate_selection.contains("collect_global_model_names_for_required_capability"),