feat(codex): 按操作语义路由 Responses V2 压缩

(cherry picked from commit 2fc604e047)
This commit is contained in:
MMEXA
2026-07-16 00:34:42 +08:00
committed by elky
parent 598b2fb374
commit 20b27a13b2
75 changed files with 732 additions and 50 deletions
@@ -2,7 +2,7 @@ use aether_scheduler_core::SchedulerMinimalCandidateSelectionCandidate;
use crate::data::auth::GatewayAuthApiKeySnapshot;
use crate::data::candidate_selection::{
enumerate_minimal_candidate_selection_with_required_capabilities,
enumerate_minimal_candidate_selection_with_required_capabilities_for_request_operation,
MinimalCandidateSelectionRowSource,
};
use crate::GatewayError;
@@ -15,8 +15,9 @@ pub(super) async fn enumerate_scheduler_candidates(
required_capabilities: Option<&serde_json::Value>,
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
enable_model_directives: bool,
request_operation: Option<&str>,
) -> Result<Vec<SchedulerMinimalCandidateSelectionCandidate>, GatewayError> {
enumerate_minimal_candidate_selection_with_required_capabilities(
enumerate_minimal_candidate_selection_with_required_capabilities_for_request_operation(
selection_row_source,
api_format,
global_model_name,
@@ -24,6 +25,7 @@ pub(super) async fn enumerate_scheduler_candidates(
auth_snapshot,
required_capabilities,
enable_model_directives,
request_operation,
)
.await
.map_err(|err| GatewayError::Internal(err.to_string()))
@@ -116,6 +116,42 @@ pub(crate) async fn list_selectable_candidates_with_skip_reasons(
client_session_affinity,
now_unix_secs,
enable_model_directives,
None,
)
.await
}
pub(crate) async fn list_selectable_candidates_with_skip_reasons_for_request_operation(
selection_row_source: &(impl MinimalCandidateSelectionRowSource + Sync),
runtime_state: &impl SchedulerRuntimeState,
api_format: &str,
global_model_name: &str,
require_streaming: bool,
required_capabilities: Option<&serde_json::Value>,
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
client_session_affinity: Option<&ClientSessionAffinity>,
now_unix_secs: u64,
enable_model_directives: bool,
request_operation: Option<&str>,
) -> Result<
(
Vec<SchedulerMinimalCandidateSelectionCandidate>,
Vec<SchedulerSkippedCandidate>,
),
GatewayError,
> {
collect_selectable_candidates_with_skip_reasons(
selection_row_source,
runtime_state,
api_format,
global_model_name,
require_streaming,
required_capabilities,
auth_snapshot,
client_session_affinity,
now_unix_secs,
enable_model_directives,
request_operation,
)
.await
}
@@ -237,6 +273,7 @@ pub(crate) async fn list_selectable_candidates_for_required_capability_without_r
client_session_affinity,
now_unix_secs,
false,
None,
)
.await?;
all_attempts_blocked_by_auth_limit &=
@@ -81,6 +81,7 @@ pub(super) async fn select_minimal_candidate(
required_capabilities,
auth_snapshot,
enable_model_directives,
None,
)
.await?;
let selected = collect_selectable_enumerated_candidates_with_skip_reasons(
@@ -137,6 +138,7 @@ pub(super) async fn collect_selectable_candidates(
client_session_affinity,
now_unix_secs,
enable_model_directives,
None,
)
.await?
.0)
@@ -153,6 +155,7 @@ pub(super) async fn collect_selectable_candidates_with_skip_reasons(
client_session_affinity: Option<&ClientSessionAffinity>,
now_unix_secs: u64,
enable_model_directives: bool,
request_operation: Option<&str>,
) -> Result<
(
Vec<SchedulerMinimalCandidateSelectionCandidate>,
@@ -174,6 +177,7 @@ pub(super) async fn collect_selectable_candidates_with_skip_reasons(
required_capabilities,
auth_snapshot,
enable_model_directives,
request_operation,
)
.await?;
collect_selectable_enumerated_candidates_with_skip_reasons(
@@ -107,6 +107,7 @@ async fn same_priority_candidates_are_distributed_by_affinity_key() {
priority: 1,
api_formats: Some(vec!["openai:chat".to_string()]),
endpoint_ids: None,
operations: None,
}]);
let mut second = sample_row();
@@ -124,6 +125,7 @@ async fn same_priority_candidates_are_distributed_by_affinity_key() {
priority: 1,
api_formats: Some(vec!["openai:chat".to_string()]),
endpoint_ids: None,
operations: None,
}]);
let candidates = Arc::new(InMemoryMinimalCandidateSelectionReadRepository::seed(vec![
@@ -142,6 +144,7 @@ async fn same_priority_candidates_are_distributed_by_affinity_key() {
enumerate_minimal_candidate_selection(EnumerateMinimalCandidateSelectionInput {
rows,
normalized_api_format: "openai:chat",
request_operation: None,
requested_model_name: "gpt-4.1",
resolved_global_model_name: "gpt-4.1",
require_streaming: false,
@@ -45,6 +45,7 @@ fn provider_model_mapping_respects_endpoint_scope() {
priority: 1,
api_formats: Some(vec!["openai:chat".to_string()]),
endpoint_ids: Some(vec!["endpoint-selected".to_string()]),
operations: None,
}]);
assert_eq!(
@@ -101,6 +102,7 @@ fn resolves_requested_global_model_from_provider_model_alias() {
priority: 1,
api_formats: Some(vec!["openai:chat".to_string()]),
endpoint_ids: None,
operations: None,
}]);
let resolved = resolve_requested_global_model_name(&[row], "gpt-5.2", "openai:chat");
@@ -156,6 +158,7 @@ async fn enumerate_minimal_candidate_selection_resolves_provider_model_alias() {
priority: 1,
api_formats: Some(vec!["openai:chat".to_string()]),
endpoint_ids: None,
operations: None,
}]);
let candidates = Arc::new(InMemoryMinimalCandidateSelectionReadRepository::seed(vec![
@@ -193,6 +196,7 @@ async fn enumerate_minimal_candidate_selection_filters_endpoint_scoped_alias_row
priority: 1,
api_formats: Some(vec!["openai:chat".to_string()]),
endpoint_ids: Some(vec!["endpoint-selected".to_string()]),
operations: None,
}]);
let mut other = selected.clone();
@@ -248,6 +252,7 @@ async fn enumerate_minimal_candidate_selection_keeps_only_resolved_global_model_
priority: 1,
api_formats: Some(vec!["openai:chat".to_string()]),
endpoint_ids: None,
operations: None,
}]);
let candidates = Arc::new(InMemoryMinimalCandidateSelectionReadRepository::seed(vec![
@@ -287,6 +292,7 @@ async fn enumerate_minimal_candidate_selection_allows_resolved_global_model_in_a
priority: 1,
api_formats: Some(vec!["openai:chat".to_string()]),
endpoint_ids: None,
operations: None,
}]);
let candidates = Arc::new(InMemoryMinimalCandidateSelectionReadRepository::seed(vec![
@@ -105,6 +105,7 @@ async fn collect_selectable_candidates_with_skip_reasons(
None,
now_unix_secs,
false,
None,
)
.await
}
@@ -843,6 +844,7 @@ async fn selects_next_candidate_when_first_provider_quota_is_exhausted() {
priority: 1,
api_formats: Some(vec!["openai:chat".to_string()]),
endpoint_ids: None,
operations: None,
}]);
first.key_global_priority_by_format = Some(serde_json::json!({"openai:chat": 1}));
@@ -858,6 +860,7 @@ async fn selects_next_candidate_when_first_provider_quota_is_exhausted() {
priority: 1,
api_formats: Some(vec!["openai:chat".to_string()]),
endpoint_ids: None,
operations: None,
}]);
second.key_global_priority_by_format = Some(serde_json::json!({"openai:chat": 2}));
@@ -914,6 +917,7 @@ async fn cooled_down_when_recent_failures_are_recorded_for_same_key() {
priority: 1,
api_formats: Some(vec!["openai:chat".to_string()]),
endpoint_ids: None,
operations: None,
}]);
first.key_global_priority_by_format = Some(serde_json::json!({"openai:chat": 1}));
@@ -929,6 +933,7 @@ async fn cooled_down_when_recent_failures_are_recorded_for_same_key() {
priority: 1,
api_formats: Some(vec!["openai:chat".to_string()]),
endpoint_ids: None,
operations: None,
}]);
second.key_global_priority_by_format = Some(serde_json::json!({"openai:chat": 2}));
@@ -40,12 +40,14 @@ pub(super) fn sample_row() -> StoredMinimalCandidateSelectionRow {
priority: 1,
api_formats: Some(vec!["openai:chat".to_string()]),
endpoint_ids: None,
operations: None,
},
StoredProviderModelMapping {
name: "gpt-4.1-responses".to_string(),
priority: 1,
api_formats: Some(vec!["openai:responses".to_string()]),
endpoint_ids: None,
operations: None,
},
]),
model_supports_streaming: None,