mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 16:37:46 +08:00
feat(codex): 按操作语义路由 Responses V2 压缩
(cherry picked from commit 2fc604e047)
This commit is contained in:
@@ -706,6 +706,7 @@ pub(crate) async fn build_lazy_requested_model_execution_candidate_attempt_sourc
|
||||
trace_id: &str,
|
||||
client_api_format: &str,
|
||||
requested_model: &str,
|
||||
request_operation: Option<&str>,
|
||||
require_streaming: bool,
|
||||
auth_snapshot: &GatewayAuthApiKeySnapshot,
|
||||
client_session_affinity: Option<&ClientSessionAffinity>,
|
||||
@@ -734,6 +735,7 @@ where
|
||||
model_directive_policy,
|
||||
client_api_format,
|
||||
requested_model,
|
||||
request_operation,
|
||||
require_streaming,
|
||||
required_capabilities,
|
||||
auth_snapshot,
|
||||
@@ -1141,6 +1143,7 @@ async fn resolve_priority_candidate_page_with_cache(
|
||||
|
||||
let key = CandidateResolvedPageCacheKey::new(
|
||||
&cursor.requested_model,
|
||||
cursor.page_cursor.resolved_page_cache_request_operation(),
|
||||
&cursor.client_api_format,
|
||||
true,
|
||||
&cursor.auth_snapshot,
|
||||
@@ -2190,6 +2193,7 @@ mod tests {
|
||||
&model_directive_policy,
|
||||
"openai:chat",
|
||||
"gpt-5",
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
&auth_snapshot,
|
||||
@@ -2245,6 +2249,7 @@ mod tests {
|
||||
&model_directive_policy,
|
||||
"openai:chat",
|
||||
"gpt-5",
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
&auth_snapshot,
|
||||
@@ -2282,6 +2287,7 @@ mod tests {
|
||||
&model_directive_policy,
|
||||
"openai:chat",
|
||||
"gpt-5",
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
&auth_snapshot,
|
||||
|
||||
@@ -6,9 +6,10 @@ use aether_routing_core::ResolvedRoutingPolicy;
|
||||
use aether_runtime::ConcurrencyPermit;
|
||||
use aether_scheduler_core::{
|
||||
enumerate_minimal_candidate_selection_with_model_directives, normalize_api_format,
|
||||
resolve_requested_global_model_name_with_model_directives,
|
||||
row_supports_requested_model_with_model_directives, ClientSessionAffinity,
|
||||
EnumerateMinimalCandidateSelectionInput, SchedulerMinimalCandidateSelectionCandidate,
|
||||
resolve_requested_global_model_name_with_model_directives_and_request_operation,
|
||||
row_supports_requested_model_with_model_directives_and_request_operation,
|
||||
ClientSessionAffinity, EnumerateMinimalCandidateSelectionInput,
|
||||
SchedulerMinimalCandidateSelectionCandidate,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
||||
@@ -56,6 +57,7 @@ struct GatewayLocalCandidatePreselectionPort<'a> {
|
||||
state: PlannerAppState<'a>,
|
||||
client_api_format: &'a str,
|
||||
requested_model: &'a str,
|
||||
request_operation: Option<&'a str>,
|
||||
require_streaming: bool,
|
||||
required_capabilities: Option<&'a serde_json::Value>,
|
||||
auth_snapshot: &'a GatewayAuthApiKeySnapshot,
|
||||
@@ -112,7 +114,7 @@ impl AiCandidatePreselectionPort for GatewayLocalCandidatePreselectionPort<'_> {
|
||||
let auth_snapshot = matches_client_format.then_some(self.auth_snapshot);
|
||||
let (candidates, skipped_candidates) = self
|
||||
.state
|
||||
.list_selectable_candidates_with_skip_reasons(
|
||||
.list_selectable_candidates_with_skip_reasons_for_request_operation(
|
||||
candidate_api_format,
|
||||
self.routing_model(candidate_api_format),
|
||||
self.require_streaming,
|
||||
@@ -121,6 +123,7 @@ impl AiCandidatePreselectionPort for GatewayLocalCandidatePreselectionPort<'_> {
|
||||
self.client_session_affinity,
|
||||
self.ranking_seed,
|
||||
false,
|
||||
self.request_operation,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -197,6 +200,7 @@ pub(crate) async fn preselect_local_execution_candidates_with_serving(
|
||||
model_directive_policy: &crate::system_features::ModelDirectivePolicySnapshot,
|
||||
client_api_format: &str,
|
||||
requested_model: &str,
|
||||
request_operation: Option<&str>,
|
||||
require_streaming: bool,
|
||||
required_capabilities: Option<&serde_json::Value>,
|
||||
auth_snapshot: &GatewayAuthApiKeySnapshot,
|
||||
@@ -221,6 +225,7 @@ pub(crate) async fn preselect_local_execution_candidates_with_serving(
|
||||
model_directive_policy,
|
||||
client_api_format,
|
||||
requested_model,
|
||||
request_operation,
|
||||
require_streaming,
|
||||
required_capabilities,
|
||||
auth_snapshot,
|
||||
@@ -239,6 +244,7 @@ pub(crate) async fn preselect_local_execution_candidates_for_api_formats_with_se
|
||||
model_directive_policy: &crate::system_features::ModelDirectivePolicySnapshot,
|
||||
client_api_format: &str,
|
||||
requested_model: &str,
|
||||
request_operation: Option<&str>,
|
||||
require_streaming: bool,
|
||||
required_capabilities: Option<&serde_json::Value>,
|
||||
auth_snapshot: &GatewayAuthApiKeySnapshot,
|
||||
@@ -263,6 +269,7 @@ pub(crate) async fn preselect_local_execution_candidates_for_api_formats_with_se
|
||||
state,
|
||||
client_api_format,
|
||||
requested_model,
|
||||
request_operation,
|
||||
require_streaming,
|
||||
required_capabilities,
|
||||
auth_snapshot,
|
||||
@@ -283,6 +290,7 @@ pub(crate) struct LocalCandidatePreselectionPageCursor<'a> {
|
||||
trace_id: String,
|
||||
client_api_format: String,
|
||||
requested_model: String,
|
||||
request_operation: Option<String>,
|
||||
require_streaming: bool,
|
||||
required_capabilities: Option<serde_json::Value>,
|
||||
auth_snapshot: GatewayAuthApiKeySnapshot,
|
||||
@@ -336,6 +344,7 @@ impl<'a> LocalCandidatePreselectionPageCursor<'a> {
|
||||
model_directive_policy: &crate::system_features::ModelDirectivePolicySnapshot,
|
||||
client_api_format: &str,
|
||||
requested_model: &str,
|
||||
request_operation: Option<&str>,
|
||||
require_streaming: bool,
|
||||
required_capabilities: Option<&serde_json::Value>,
|
||||
auth_snapshot: &GatewayAuthApiKeySnapshot,
|
||||
@@ -370,6 +379,7 @@ impl<'a> LocalCandidatePreselectionPageCursor<'a> {
|
||||
trace_id: trace_id.unwrap_or_default().to_string(),
|
||||
client_api_format: client_api_format.to_string(),
|
||||
requested_model: requested_model.to_string(),
|
||||
request_operation: request_operation.map(str::to_string),
|
||||
require_streaming,
|
||||
required_capabilities: required_capabilities.cloned(),
|
||||
auth_snapshot: auth_snapshot.clone(),
|
||||
@@ -452,6 +462,10 @@ impl<'a> LocalCandidatePreselectionPageCursor<'a> {
|
||||
self.key_mode.cache_key_name()
|
||||
}
|
||||
|
||||
pub(crate) fn resolved_page_cache_request_operation(&self) -> Option<&str> {
|
||||
self.request_operation.as_deref()
|
||||
}
|
||||
|
||||
pub(crate) fn resolved_page_cache_use_api_format_alias_match(&self) -> bool {
|
||||
self.use_api_format_alias_match
|
||||
}
|
||||
@@ -515,6 +529,7 @@ impl<'a> LocalCandidatePreselectionPageCursor<'a> {
|
||||
> {
|
||||
let key = CandidatePageCacheKey::new(
|
||||
&self.requested_model,
|
||||
self.request_operation.as_deref(),
|
||||
&self.client_api_format,
|
||||
self.require_streaming,
|
||||
&self.auth_snapshot,
|
||||
@@ -965,11 +980,12 @@ impl<'a> LocalCandidatePreselectionPageCursor<'a> {
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?
|
||||
.into_iter()
|
||||
.filter(|row| {
|
||||
row_supports_requested_model_with_model_directives(
|
||||
row_supports_requested_model_with_model_directives_and_request_operation(
|
||||
row,
|
||||
&routing_model,
|
||||
normalized_api_format,
|
||||
false,
|
||||
self.request_operation.as_deref(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
@@ -1009,12 +1025,15 @@ impl<'a> LocalCandidatePreselectionPageCursor<'a> {
|
||||
if let Some(value) = self.resolved_global_model_names.get(normalized_api_format) {
|
||||
value.clone()
|
||||
} else {
|
||||
let Some(value) = resolve_requested_global_model_name_with_model_directives(
|
||||
&rows,
|
||||
&routing_model,
|
||||
normalized_api_format,
|
||||
false,
|
||||
) else {
|
||||
let Some(value) =
|
||||
resolve_requested_global_model_name_with_model_directives_and_request_operation(
|
||||
&rows,
|
||||
&routing_model,
|
||||
normalized_api_format,
|
||||
false,
|
||||
self.request_operation.as_deref(),
|
||||
)
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.resolved_global_model_names
|
||||
@@ -1037,6 +1056,7 @@ impl<'a> LocalCandidatePreselectionPageCursor<'a> {
|
||||
EnumerateMinimalCandidateSelectionInput {
|
||||
rows,
|
||||
normalized_api_format,
|
||||
request_operation: self.request_operation.as_deref(),
|
||||
requested_model_name: &routing_model,
|
||||
resolved_global_model_name: resolved_global_model_name.as_str(),
|
||||
require_streaming: self.require_streaming,
|
||||
@@ -1316,6 +1336,7 @@ mod tests {
|
||||
&model_directive_policy,
|
||||
"openai:chat",
|
||||
"gpt-5",
|
||||
None,
|
||||
true,
|
||||
None,
|
||||
&auth_snapshot,
|
||||
@@ -1531,6 +1552,7 @@ mod tests {
|
||||
priority: 1,
|
||||
api_formats: None,
|
||||
endpoint_ids: Some(vec!["endpoint-opg-openai".to_string()]),
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1561,6 +1583,7 @@ mod tests {
|
||||
&model_directive_policy,
|
||||
"claude:messages",
|
||||
"gpt-5.5-xhigh",
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
&auth_snapshot,
|
||||
@@ -1590,6 +1613,70 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn paged_preselection_prefers_operation_scoped_mapping_for_compaction() {
|
||||
let mut row = openai_responses_mapping_row();
|
||||
row.global_model_mappings = None;
|
||||
row.global_model_name = "gpt-5.6-sol".to_string();
|
||||
row.model_provider_model_name = "gpt-5.6-sol".to_string();
|
||||
row.model_provider_model_mappings = Some(vec![
|
||||
StoredProviderModelMapping {
|
||||
name: "gpt-5.6-sol".to_string(),
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
},
|
||||
StoredProviderModelMapping {
|
||||
name: "gpt-5.6-terra".to_string(),
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: Some(vec!["compact".to_string()]),
|
||||
},
|
||||
]);
|
||||
let repository: Arc<dyn MinimalCandidateSelectionReadRepository> =
|
||||
Arc::new(InMemoryMinimalCandidateSelectionReadRepository::seed([row]));
|
||||
let data_state =
|
||||
GatewayDataState::with_minimal_candidate_selection_reader_for_tests(repository);
|
||||
let app = AppState::new()
|
||||
.expect("gateway state should build")
|
||||
.with_data_state_for_tests(data_state);
|
||||
let auth_snapshot = unrestricted_auth_snapshot();
|
||||
let model_directive_policy =
|
||||
crate::system_features::ModelDirectivePolicySnapshot::load(&app).await;
|
||||
let mut cursor = LocalCandidatePreselectionPageCursor::new(
|
||||
PlannerAppState::new(&app),
|
||||
&model_directive_policy,
|
||||
"openai:responses",
|
||||
"gpt-5.6-sol",
|
||||
Some("compact"),
|
||||
false,
|
||||
None,
|
||||
&auth_snapshot,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
LocalCandidatePreselectionKeyMode::ProviderEndpointKeyModelAndApiFormat,
|
||||
true,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
let page = cursor
|
||||
.next_page()
|
||||
.await
|
||||
.expect("preselection should succeed")
|
||||
.expect("compact mapping should find a provider");
|
||||
|
||||
assert_eq!(page.candidates.len(), 1);
|
||||
assert_eq!(
|
||||
page.candidates[0].selected_provider_model_name,
|
||||
"gpt-5.6-terra"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn custom_policy_suffix_uses_the_same_base_model_for_candidate_selection() {
|
||||
let mut row = openai_responses_mapping_row();
|
||||
@@ -1634,6 +1721,7 @@ mod tests {
|
||||
&model_directive_policy,
|
||||
"openai:responses",
|
||||
"deployment-alias-VendorFuture",
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
&auth_snapshot,
|
||||
@@ -1695,6 +1783,7 @@ mod tests {
|
||||
&model_directive_policy,
|
||||
"claude:messages",
|
||||
"deepseek-v4-pro",
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
&auth_snapshot,
|
||||
@@ -1773,6 +1862,7 @@ mod tests {
|
||||
&model_directive_policy,
|
||||
"claude:messages",
|
||||
"gpt-5",
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
&auth_snapshot,
|
||||
|
||||
@@ -124,6 +124,7 @@ pub(super) async fn materialize_local_standard_candidate_attempts(
|
||||
&input.model_directive_policy,
|
||||
spec_metadata.api_format,
|
||||
&input.requested_model,
|
||||
None,
|
||||
false,
|
||||
input.required_capabilities.as_ref(),
|
||||
&input.auth_snapshot,
|
||||
@@ -250,6 +251,7 @@ pub(super) async fn build_local_standard_candidate_attempt_source<'a>(
|
||||
trace_id,
|
||||
spec_metadata.api_format,
|
||||
&input.requested_model,
|
||||
None,
|
||||
spec_metadata.require_streaming,
|
||||
&input.auth_snapshot,
|
||||
input.client_session_affinity.as_ref(),
|
||||
@@ -345,6 +347,7 @@ async fn maybe_append_gemini_image_openai_image_preselection(
|
||||
&input.model_directive_policy,
|
||||
spec_metadata.api_format,
|
||||
&input.requested_model,
|
||||
None,
|
||||
spec_metadata.require_streaming,
|
||||
input.required_capabilities.as_ref(),
|
||||
&input.auth_snapshot,
|
||||
|
||||
@@ -297,6 +297,7 @@ pub(crate) async fn build_lazy_local_openai_chat_candidate_attempt_source<'a>(
|
||||
trace_id,
|
||||
"openai:chat",
|
||||
&input.requested_model,
|
||||
None,
|
||||
require_streaming,
|
||||
&input.auth_snapshot,
|
||||
input.client_session_affinity.as_ref(),
|
||||
|
||||
@@ -24,6 +24,7 @@ pub(crate) async fn list_local_openai_chat_candidates(
|
||||
&input.model_directive_policy,
|
||||
"openai:chat",
|
||||
&input.requested_model,
|
||||
None,
|
||||
require_streaming,
|
||||
input.required_capabilities.as_ref(),
|
||||
&input.auth_snapshot,
|
||||
|
||||
+7
-2
@@ -31,8 +31,8 @@ use crate::ai_serving::planner::spec_metadata::local_openai_responses_spec_metad
|
||||
use crate::ai_serving::planner::CandidateFailureDiagnostic;
|
||||
use crate::ai_serving::{
|
||||
ai_local_execution_contract_for_formats, extract_pool_sticky_session_token,
|
||||
resolve_local_decision_execution_runtime_auth_context, ExecutionRuntimeAuthContext,
|
||||
GatewayControlDecision, PlannerAppState,
|
||||
openai_responses_request_operation, resolve_local_decision_execution_runtime_auth_context,
|
||||
ExecutionRuntimeAuthContext, GatewayControlDecision, PlannerAppState,
|
||||
};
|
||||
use crate::client_session_affinity::client_session_affinity_from_parts;
|
||||
use crate::{AppState, GatewayError};
|
||||
@@ -163,6 +163,7 @@ pub(crate) async fn materialize_local_openai_responses_candidate_attempts(
|
||||
spec: LocalOpenAiResponsesSpec,
|
||||
) -> Result<(Vec<LocalOpenAiResponsesCandidateAttempt>, usize), GatewayError> {
|
||||
let spec_metadata = local_openai_responses_spec_metadata(spec);
|
||||
let request_operation = openai_responses_request_operation(spec_metadata.api_format, body_json);
|
||||
let planner_state = PlannerAppState::new(state);
|
||||
let sticky_session_token = extract_pool_sticky_session_token(body_json);
|
||||
let auth_context: &ExecutionRuntimeAuthContext = &input.auth_context;
|
||||
@@ -176,6 +177,7 @@ pub(crate) async fn materialize_local_openai_responses_candidate_attempts(
|
||||
&input.model_directive_policy,
|
||||
spec_metadata.api_format,
|
||||
&input.requested_model,
|
||||
request_operation,
|
||||
spec_metadata.require_streaming,
|
||||
input.required_capabilities.as_ref(),
|
||||
&input.auth_snapshot,
|
||||
@@ -262,6 +264,7 @@ pub(crate) async fn build_local_openai_responses_candidate_attempt_source<'a>(
|
||||
spec: LocalOpenAiResponsesSpec,
|
||||
) -> Result<(LocalOpenAiResponsesCandidateAttemptSource<'a>, usize), GatewayError> {
|
||||
let spec_metadata = local_openai_responses_spec_metadata(spec);
|
||||
let request_operation = openai_responses_request_operation(spec_metadata.api_format, body_json);
|
||||
let planner_state = PlannerAppState::new(state);
|
||||
let sticky_session_token = extract_pool_sticky_session_token(body_json);
|
||||
let auth_context: &ExecutionRuntimeAuthContext = &input.auth_context;
|
||||
@@ -287,6 +290,7 @@ pub(crate) async fn build_local_openai_responses_candidate_attempt_source<'a>(
|
||||
trace_id,
|
||||
spec_metadata.api_format,
|
||||
&input.requested_model,
|
||||
request_operation,
|
||||
spec_metadata.require_streaming,
|
||||
&input.auth_snapshot,
|
||||
input.client_session_affinity.as_ref(),
|
||||
@@ -372,6 +376,7 @@ pub(crate) async fn build_local_openai_responses_image_candidate_attempt_source<
|
||||
&input.model_directive_policy,
|
||||
spec_metadata.api_format,
|
||||
&input.requested_model,
|
||||
None,
|
||||
false,
|
||||
input.required_capabilities.as_ref(),
|
||||
&input.auth_snapshot,
|
||||
|
||||
@@ -53,13 +53,45 @@ impl<'a> PlannerAppState<'a> {
|
||||
Vec<SchedulerSkippedCandidate>,
|
||||
),
|
||||
GatewayError,
|
||||
> {
|
||||
self.list_selectable_candidates_with_skip_reasons_for_request_operation(
|
||||
api_format,
|
||||
global_model_name,
|
||||
require_streaming,
|
||||
required_capabilities,
|
||||
auth_snapshot,
|
||||
client_session_affinity,
|
||||
now_unix_secs,
|
||||
enable_model_directives,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn list_selectable_candidates_with_skip_reasons_for_request_operation(
|
||||
self,
|
||||
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,
|
||||
> {
|
||||
let wait_timeout = Duration::from_millis(API_KEY_CONCURRENCY_WAIT_TIMEOUT_MS);
|
||||
let wait_interval = Duration::from_millis(API_KEY_CONCURRENCY_WAIT_POLL_INTERVAL_MS.max(1));
|
||||
let wait_deadline = Instant::now() + wait_timeout;
|
||||
let mut attempt_now_unix_secs = now_unix_secs;
|
||||
loop {
|
||||
let result = crate::scheduler::candidate::list_selectable_candidates_with_skip_reasons(
|
||||
let result = crate::scheduler::candidate::list_selectable_candidates_with_skip_reasons_for_request_operation(
|
||||
self.app().data.as_ref(),
|
||||
self.app(),
|
||||
api_format,
|
||||
@@ -70,6 +102,7 @@ impl<'a> PlannerAppState<'a> {
|
||||
client_session_affinity,
|
||||
attempt_now_unix_secs,
|
||||
enable_model_directives,
|
||||
request_operation,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -165,5 +165,5 @@ pub(crate) use aether_ai_formats::api::{
|
||||
pub(crate) use aether_ai_formats::{
|
||||
api_format_defaults_to_client_error_failover, api_format_defaults_to_non_stream,
|
||||
api_format_permission_covers, intersect_api_format_allowed_lists, is_embedding_api_format,
|
||||
is_rerank_api_format,
|
||||
is_rerank_api_format, openai_responses_request_operation,
|
||||
};
|
||||
|
||||
+31
@@ -82,6 +82,7 @@ pub(crate) struct CandidateRowPageCacheKey {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct CandidatePageCacheKey {
|
||||
requested_model: String,
|
||||
request_operation: String,
|
||||
client_api_format: String,
|
||||
auth_identity: CandidatePageAuthIdentity,
|
||||
require_streaming: bool,
|
||||
@@ -131,6 +132,7 @@ impl CandidatePageCacheKey {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
requested_model: &str,
|
||||
request_operation: Option<&str>,
|
||||
client_api_format: &str,
|
||||
require_streaming: bool,
|
||||
auth_snapshot: &GatewayAuthApiKeySnapshot,
|
||||
@@ -145,6 +147,7 @@ impl CandidatePageCacheKey {
|
||||
) -> Self {
|
||||
Self {
|
||||
requested_model: normalize_text_key(requested_model),
|
||||
request_operation: normalize_text_key(request_operation.unwrap_or_default()),
|
||||
client_api_format: normalize_api_format(client_api_format),
|
||||
auth_identity: CandidatePageAuthIdentity::from_auth_snapshot(auth_snapshot),
|
||||
require_streaming,
|
||||
@@ -164,6 +167,7 @@ impl CandidateResolvedPageCacheKey {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
requested_model: &str,
|
||||
request_operation: Option<&str>,
|
||||
client_api_format: &str,
|
||||
require_streaming: bool,
|
||||
auth_snapshot: &GatewayAuthApiKeySnapshot,
|
||||
@@ -180,6 +184,7 @@ impl CandidateResolvedPageCacheKey {
|
||||
Self {
|
||||
page_key: CandidatePageCacheKey::new(
|
||||
requested_model,
|
||||
request_operation,
|
||||
client_api_format,
|
||||
require_streaming,
|
||||
auth_snapshot,
|
||||
@@ -564,6 +569,7 @@ mod tests {
|
||||
let auth_b = auth_snapshot("user-b", "key-a");
|
||||
let base = CandidatePageCacheKey::new(
|
||||
"gpt-4o",
|
||||
None,
|
||||
"openai:chat",
|
||||
true,
|
||||
&auth_a,
|
||||
@@ -578,6 +584,7 @@ mod tests {
|
||||
);
|
||||
let different_user = CandidatePageCacheKey::new(
|
||||
"gpt-4o",
|
||||
None,
|
||||
"openai:chat",
|
||||
true,
|
||||
&auth_b,
|
||||
@@ -592,6 +599,22 @@ mod tests {
|
||||
);
|
||||
let different_model = CandidatePageCacheKey::new(
|
||||
"gpt-4.1",
|
||||
None,
|
||||
"openai:chat",
|
||||
true,
|
||||
&auth_a,
|
||||
Some(&json!({"vision": true})),
|
||||
None,
|
||||
Some("bearer"),
|
||||
7,
|
||||
"provider_endpoint_key_model",
|
||||
true,
|
||||
None,
|
||||
"policy-a",
|
||||
);
|
||||
let different_operation = CandidatePageCacheKey::new(
|
||||
"gpt-4o",
|
||||
Some("compact"),
|
||||
"openai:chat",
|
||||
true,
|
||||
&auth_a,
|
||||
@@ -606,6 +629,7 @@ mod tests {
|
||||
);
|
||||
let different_format = CandidatePageCacheKey::new(
|
||||
"gpt-4o",
|
||||
None,
|
||||
"openai:responses",
|
||||
true,
|
||||
&auth_a,
|
||||
@@ -620,6 +644,7 @@ mod tests {
|
||||
);
|
||||
let different_capabilities = CandidatePageCacheKey::new(
|
||||
"gpt-4o",
|
||||
None,
|
||||
"openai:chat",
|
||||
true,
|
||||
&auth_a,
|
||||
@@ -634,6 +659,7 @@ mod tests {
|
||||
);
|
||||
let same_policy = CandidatePageCacheKey::new(
|
||||
"gpt-4o",
|
||||
None,
|
||||
"openai:chat",
|
||||
true,
|
||||
&auth_a,
|
||||
@@ -648,6 +674,7 @@ mod tests {
|
||||
);
|
||||
let different_policy = CandidatePageCacheKey::new(
|
||||
"gpt-4o",
|
||||
None,
|
||||
"openai:chat",
|
||||
true,
|
||||
&auth_a,
|
||||
@@ -664,12 +691,14 @@ mod tests {
|
||||
assert_eq!(base, same_policy);
|
||||
assert_ne!(base, different_user);
|
||||
assert_ne!(base, different_model);
|
||||
assert_ne!(base, different_operation);
|
||||
assert_ne!(base, different_format);
|
||||
assert_ne!(base, different_capabilities);
|
||||
assert_ne!(base, different_policy);
|
||||
|
||||
let resolved_base = CandidateResolvedPageCacheKey::new(
|
||||
"gpt-4o",
|
||||
None,
|
||||
"openai:chat",
|
||||
true,
|
||||
&auth_a,
|
||||
@@ -685,6 +714,7 @@ mod tests {
|
||||
);
|
||||
let resolved_same_policy = CandidateResolvedPageCacheKey::new(
|
||||
"gpt-4o",
|
||||
None,
|
||||
"openai:chat",
|
||||
true,
|
||||
&auth_a,
|
||||
@@ -700,6 +730,7 @@ mod tests {
|
||||
);
|
||||
let resolved_different_policy = CandidateResolvedPageCacheKey::new(
|
||||
"gpt-4o",
|
||||
None,
|
||||
"openai:chat",
|
||||
true,
|
||||
&auth_a,
|
||||
|
||||
@@ -621,6 +621,7 @@ mod tests {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -954,6 +955,7 @@ mod tests {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]);
|
||||
let state = state_with_rows(vec![row]);
|
||||
let decision = decision_with_allowed_models(vec!["gpt-5".to_string()]);
|
||||
|
||||
@@ -239,6 +239,29 @@ pub(crate) async fn enumerate_minimal_candidate_selection_with_required_capabili
|
||||
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
|
||||
required_capabilities: Option<&serde_json::Value>,
|
||||
enable_model_directives: bool,
|
||||
) -> Result<Vec<SchedulerMinimalCandidateSelectionCandidate>, DataLayerError> {
|
||||
enumerate_minimal_candidate_selection_with_required_capabilities_for_request_operation(
|
||||
state,
|
||||
api_format,
|
||||
requested_model_name,
|
||||
require_streaming,
|
||||
auth_snapshot,
|
||||
required_capabilities,
|
||||
enable_model_directives,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn enumerate_minimal_candidate_selection_with_required_capabilities_for_request_operation(
|
||||
state: &(impl MinimalCandidateSelectionRowSource + Sync),
|
||||
api_format: &str,
|
||||
requested_model_name: &str,
|
||||
require_streaming: bool,
|
||||
auth_snapshot: Option<&GatewayAuthApiKeySnapshot>,
|
||||
required_capabilities: Option<&serde_json::Value>,
|
||||
enable_model_directives: bool,
|
||||
request_operation: Option<&str>,
|
||||
) -> Result<Vec<SchedulerMinimalCandidateSelectionCandidate>, DataLayerError> {
|
||||
let normalized_api_format = normalize_api_format(api_format);
|
||||
if normalized_api_format.is_empty() {
|
||||
@@ -267,6 +290,7 @@ pub(crate) async fn enumerate_minimal_candidate_selection_with_required_capabili
|
||||
EnumerateMinimalCandidateSelectionInput {
|
||||
rows,
|
||||
normalized_api_format: &normalized_api_format,
|
||||
request_operation,
|
||||
requested_model_name,
|
||||
resolved_global_model_name: resolved_global_model_name.as_str(),
|
||||
require_streaming,
|
||||
|
||||
@@ -590,6 +590,7 @@ fn sample_minimal_candidate_selection_row(
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: None,
|
||||
model_is_active: true,
|
||||
@@ -907,6 +908,7 @@ async fn data_state_reads_minimal_candidate_selection_with_auth_filters() {
|
||||
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,
|
||||
|
||||
+18
@@ -213,6 +213,7 @@ fn provider_query_parse_embedded_provider_model_mappings(
|
||||
priority: 1,
|
||||
api_formats: None,
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]))
|
||||
}
|
||||
|
||||
@@ -237,6 +238,7 @@ fn provider_query_parse_provider_model_mappings_array(
|
||||
priority: 1,
|
||||
api_formats: None,
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -293,6 +295,11 @@ fn provider_query_parse_provider_model_mapping_object_lenient(
|
||||
object.get("endpoint_ids"),
|
||||
"models.provider_model_mappings.endpoint_ids",
|
||||
)?;
|
||||
let operations = provider_query_parse_mapping_string_list(
|
||||
object.get("operations"),
|
||||
"models.provider_model_mappings.operations",
|
||||
)?
|
||||
.and_then(provider_query_normalize_request_operations);
|
||||
|
||||
Ok(Some(StoredProviderModelMapping {
|
||||
name: name.to_string(),
|
||||
@@ -303,9 +310,19 @@ fn provider_query_parse_provider_model_mapping_object_lenient(
|
||||
})?,
|
||||
api_formats,
|
||||
endpoint_ids,
|
||||
operations,
|
||||
}))
|
||||
}
|
||||
|
||||
fn provider_query_normalize_request_operations(values: Vec<String>) -> Option<Vec<String>> {
|
||||
let operations = values
|
||||
.into_iter()
|
||||
.map(|value| value.trim().to_ascii_lowercase())
|
||||
.filter(|value| !value.is_empty())
|
||||
.collect::<Vec<_>>();
|
||||
(!operations.is_empty()).then_some(operations)
|
||||
}
|
||||
|
||||
fn provider_query_parse_mapping_string_list(
|
||||
value: Option<&Value>,
|
||||
field_name: &str,
|
||||
@@ -389,6 +406,7 @@ mod tests {
|
||||
priority: 1,
|
||||
api_formats: Some(vec![api_format.to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ use serde_json::json;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use uuid::Uuid;
|
||||
|
||||
fn normalize_provider_model_mappings_api_formats(
|
||||
fn normalize_provider_model_mapping_scopes(
|
||||
value: Option<serde_json::Value>,
|
||||
) -> Option<serde_json::Value> {
|
||||
let Some(mut value) = value else {
|
||||
@@ -36,6 +36,9 @@ fn normalize_provider_model_mappings_api_formats(
|
||||
normalize_provider_model_mapping_string_array_field(object, "endpoint_ids", |value| {
|
||||
value.trim().to_string()
|
||||
});
|
||||
normalize_provider_model_mapping_string_array_field(object, "operations", |value| {
|
||||
value.trim().to_ascii_lowercase()
|
||||
});
|
||||
}
|
||||
Some(value)
|
||||
}
|
||||
@@ -155,7 +158,7 @@ impl<'a> AdminAppState<'a> {
|
||||
"price_per_request",
|
||||
)?;
|
||||
let tiered_pricing = normalize_json_object(payload.tiered_pricing, "tiered_pricing")?;
|
||||
let provider_model_mappings = normalize_provider_model_mappings_api_formats(
|
||||
let provider_model_mappings = normalize_provider_model_mapping_scopes(
|
||||
normalize_json_array(payload.provider_model_mappings, "provider_model_mappings")?,
|
||||
);
|
||||
let config = normalize_json_object(payload.config, "config")?;
|
||||
@@ -241,7 +244,7 @@ impl<'a> AdminAppState<'a> {
|
||||
existing.tiered_pricing.clone()
|
||||
};
|
||||
let provider_model_mappings = if fields.contains("provider_model_mappings") {
|
||||
normalize_provider_model_mappings_api_formats(normalize_json_array(
|
||||
normalize_provider_model_mapping_scopes(normalize_json_array(
|
||||
payload.provider_model_mappings,
|
||||
"provider_model_mappings",
|
||||
)?)
|
||||
|
||||
@@ -488,6 +488,7 @@ fn build_users_me_usage_record_payload(
|
||||
"cache_read_input_tokens": item.cache_read_input_tokens,
|
||||
"status_code": item.status_code,
|
||||
"error_message": item.error_message,
|
||||
"request_type": item.request_type,
|
||||
"input_price_per_1m": input_price_per_1m,
|
||||
"output_price_per_1m": output_price_per_1m,
|
||||
"cache_creation_price_per_1m": cache_creation_price_per_1m,
|
||||
@@ -525,6 +526,7 @@ fn build_users_me_usage_active_payload(item: &StoredRequestUsageAudit) -> serde_
|
||||
let mut payload = json!({
|
||||
"id": item.id,
|
||||
"status": item.status,
|
||||
"request_type": item.request_type,
|
||||
"input_tokens": item.input_tokens,
|
||||
"effective_input_tokens": users_me_usage_effective_input_tokens(item),
|
||||
"output_tokens": item.output_tokens,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1404,6 +1404,7 @@ mod tests {
|
||||
let ttl = Duration::from_secs(300);
|
||||
let cache_key = CandidatePageCacheKey::new(
|
||||
"gpt-5",
|
||||
None,
|
||||
"openai:chat",
|
||||
true,
|
||||
&sample_auth_snapshot(),
|
||||
|
||||
@@ -247,6 +247,7 @@ fn openai_chat_pressure_candidates(
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: Some(vec![pressure_endpoint_id(index)]),
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -138,6 +138,7 @@ async fn gateway_executes_openai_chat_sync_upstream_stream_via_local_finalize_re
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -637,6 +638,7 @@ async fn gateway_executes_openai_chat_cross_format_upstream_stream_via_local_fin
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1082,6 +1084,7 @@ async fn gateway_executes_openai_chat_cross_format_tool_use_upstream_stream_via_
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1564,6 +1567,7 @@ async fn gateway_executes_openai_chat_antigravity_cross_format_sync_via_local_fi
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -2074,6 +2078,7 @@ async fn gateway_executes_openai_chat_cross_format_claude_upstream_sync_via_loca
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -2433,6 +2438,7 @@ async fn gateway_executes_openai_chat_cross_format_gemini_upstream_sync_via_loca
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -113,6 +113,7 @@ async fn gateway_executes_openai_responses_compact_openai_family_upstream_stream
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses:compact".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -113,6 +113,7 @@ async fn gateway_executes_openai_responses_cross_format_upstream_stream_via_loca
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -586,6 +587,7 @@ async fn gateway_executes_openai_responses_cross_format_function_call_upstream_s
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1071,6 +1073,7 @@ async fn gateway_executes_openai_responses_antigravity_cross_format_upstream_str
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -135,6 +135,7 @@ async fn gateway_executes_openai_responses_sync_upstream_stream_via_local_finali
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -650,6 +651,7 @@ async fn gateway_executes_kiro_claude_cli_sync_upstream_stream_via_local_finaliz
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -131,6 +131,7 @@ async fn gateway_executes_claude_chat_sync_same_format_via_local_finalize_respon
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -587,6 +588,7 @@ async fn gateway_executes_claude_chat_sync_upstream_stream_via_local_finalize_re
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1048,6 +1050,7 @@ async fn gateway_executes_claude_cli_sync_upstream_stream_via_local_finalize_res
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -136,6 +136,7 @@ async fn gateway_executes_gemini_chat_sync_same_format_via_local_finalize_respon
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -640,6 +641,7 @@ async fn gateway_executes_gemini_chat_sync_upstream_stream_via_local_finalize_re
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1134,6 +1136,7 @@ async fn gateway_executes_gemini_cli_sync_upstream_stream_via_local_finalize_res
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1642,6 +1645,7 @@ async fn gateway_executes_antigravity_gemini_cli_sync_upstream_stream_via_local_
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -111,6 +111,7 @@ fn sample_local_openai_candidate_row() -> StoredMinimalCandidateSelectionRow {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -131,6 +131,7 @@ async fn gateway_executes_openai_chat_stream_via_local_decision_gate_without_exe
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -535,6 +536,7 @@ async fn gateway_executes_openai_chat_stream_via_local_openai_responses_cross_fo
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1020,6 +1022,7 @@ async fn gateway_executes_openai_chat_stream_via_local_cross_format_gemini_candi
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1458,6 +1461,7 @@ async fn gateway_executes_openai_chat_stream_with_custom_path_via_local_decision
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1961,6 +1965,7 @@ async fn gateway_retries_next_local_openai_chat_stream_candidate_after_retryable
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -131,6 +131,7 @@ async fn gateway_executes_codex_image_stream_via_local_decision_gate_after_oauth
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:image".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -517,6 +518,7 @@ async fn gateway_bridges_codex_image_sync_json_to_streaming_image_sse_impl() {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:image".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -840,6 +842,7 @@ fn image_bridge_candidate_row(
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:image".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(false),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -150,6 +150,7 @@ fn candidate_row() -> StoredMinimalCandidateSelectionRow {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: Some(vec!["endpoint-ai-execute-stream-pii-redaction".to_string()]),
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -123,6 +123,7 @@ async fn gateway_executes_openai_responses_compact_as_unary_request_impl() {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses:compact".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -136,6 +136,7 @@ async fn gateway_executes_codex_cli_stream_via_local_decision_gate_after_oauth_r
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -182,6 +182,7 @@ async fn gateway_executes_kiro_claude_cli_stream_via_local_provider_catalog_cand
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -700,6 +701,7 @@ async fn gateway_executes_claude_cli_stream_via_local_decision_gate_without_wait
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1161,6 +1163,7 @@ async fn gateway_executes_claude_code_cli_stream_via_local_decision_gate_with_lo
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1694,6 +1697,7 @@ async fn gateway_executes_claude_chat_stream_via_local_decision_gate_with_local_
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -101,6 +101,7 @@ async fn gateway_executes_gemini_chat_stream_via_local_decision_gate_with_local_
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -101,6 +101,7 @@ async fn gateway_executes_gemini_cli_stream_via_local_decision_gate_with_local_s
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -553,6 +554,7 @@ async fn gateway_executes_gemini_cli_stream_via_local_decision_gate_after_oauth_
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1108,6 +1110,7 @@ async fn gateway_executes_vertex_ai_gemini_cli_stream_via_local_decision_gate_wi
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1573,6 +1576,7 @@ async fn gateway_executes_antigravity_gemini_cli_stream_via_local_decision_gate_
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -91,6 +91,7 @@ async fn gateway_skips_unsupported_local_openai_chat_sync_candidate_before_tryin
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -296,6 +297,7 @@ async fn gateway_skips_unsupported_local_openai_chat_sync_candidate_before_tryin
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]);
|
||||
let candidate_selection_repository =
|
||||
Arc::new(InMemoryMinimalCandidateSelectionReadRepository::seed(vec![
|
||||
@@ -497,6 +499,7 @@ async fn gateway_surfaces_local_execution_runtime_miss_reason_when_all_openai_ch
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -775,6 +778,7 @@ async fn gateway_retries_next_local_openai_chat_sync_candidate_after_auth_failur
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -150,6 +150,7 @@ async fn proxy_pii_redaction_local_openai_chat_runtime_masks_headers_and_restore
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: Some(vec!["endpoint-redaction-1".to_string()]),
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -454,6 +455,7 @@ async fn gateway_executes_openai_chat_sync_via_local_decision_gate_without_execu
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -647,6 +649,7 @@ async fn gateway_executes_openai_chat_sync_via_local_decision_gate_without_execu
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]);
|
||||
let candidate_selection_repository =
|
||||
Arc::new(InMemoryMinimalCandidateSelectionReadRepository::seed(vec![
|
||||
@@ -839,6 +842,7 @@ async fn gateway_executes_openai_chat_sync_with_regex_model_mapping_in_execution
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1130,6 +1134,7 @@ async fn gateway_executes_openai_chat_sync_via_local_cross_format_gemini_candida
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1151,6 +1156,7 @@ async fn gateway_executes_openai_chat_sync_via_local_cross_format_gemini_candida
|
||||
priority: 2,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]);
|
||||
row
|
||||
}
|
||||
@@ -1698,6 +1704,7 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_claude_cli_syn
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -2102,6 +2109,7 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_cli_syn
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -2531,6 +2539,7 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_claude_sync_fa
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -2939,6 +2948,7 @@ async fn gateway_returns_openai_chat_error_for_local_cross_format_gemini_sync_fa
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -3387,6 +3397,7 @@ async fn gateway_executes_openai_chat_sync_with_custom_path_via_local_decision_g
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -113,6 +113,7 @@ fn candidate_row(test_id: &str) -> StoredMinimalCandidateSelectionRow {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: Some(vec![format!("endpoint-{test_id}")]),
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -129,6 +129,7 @@ async fn gateway_executes_claude_code_cli_sync_via_local_decision_gate_with_loca
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -197,6 +197,7 @@ async fn gateway_executes_kiro_claude_cli_sync_via_local_provider_catalog_candid
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -835,6 +836,7 @@ async fn gateway_executes_kiro_claude_cli_sync_via_local_provider_catalog_candid
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -124,6 +124,7 @@ async fn gateway_executes_claude_chat_sync_via_local_decision_gate_with_local_sy
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -694,6 +695,7 @@ async fn gateway_returns_claude_chat_error_for_local_sync_failure_impl() {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -124,6 +124,7 @@ async fn gateway_executes_claude_cli_sync_via_local_decision_gate_with_local_syn
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -570,6 +571,7 @@ async fn gateway_returns_claude_cli_error_for_local_sync_failure_impl() {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -853,6 +855,7 @@ async fn gateway_marks_claude_cli_cross_format_runtime_miss_when_format_conversi
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -141,6 +141,7 @@ async fn gateway_executes_openai_responses_sync_via_local_decision_gate_with_loc
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -650,6 +651,7 @@ async fn gateway_waits_for_api_key_concurrency_slot_then_executes_openai_respons
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1042,6 +1044,7 @@ async fn gateway_executes_openai_responses_sync_after_api_key_concurrency_wait_b
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1412,6 +1415,7 @@ async fn gateway_returns_openai_responses_error_for_local_sync_failure_impl() {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1725,6 +1729,7 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_cl
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -2131,6 +2136,7 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_claude_sy
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -2516,6 +2522,7 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_claude_ch
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["claude:messages".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -2904,6 +2911,7 @@ async fn gateway_returns_openai_responses_error_for_local_cross_format_gemini_ch
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -3301,6 +3309,7 @@ async fn gateway_executes_codex_cli_sync_via_local_decision_gate_after_oauth_ref
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -123,6 +123,7 @@ async fn gateway_executes_gemini_cli_sync_via_local_decision_gate_with_local_syn
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -561,6 +562,7 @@ async fn gateway_returns_gemini_cli_error_for_local_sync_failure_impl() {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -864,6 +866,7 @@ async fn gateway_executes_gemini_cli_sync_via_local_decision_gate_after_oauth_re
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1426,6 +1429,7 @@ async fn gateway_executes_vertex_ai_gemini_cli_sync_via_local_decision_gate_with
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1878,6 +1882,7 @@ async fn gateway_executes_antigravity_gemini_cli_sync_via_local_decision_gate_af
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -123,6 +123,7 @@ async fn gateway_executes_gemini_chat_sync_via_local_decision_gate_with_local_sy
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -551,6 +552,7 @@ async fn gateway_returns_gemini_chat_error_for_local_sync_failure_impl() {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -129,6 +129,7 @@ async fn gateway_converts_openai_image_sync_to_gemini_image_provider_impl() {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:generate_content".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -481,6 +482,7 @@ async fn gateway_converts_gemini_image_sync_to_openai_image_provider_impl() {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:image".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -833,6 +835,7 @@ async fn gateway_executes_codex_image_sync_via_local_decision_gate_after_oauth_r
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:image".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1242,6 +1245,7 @@ async fn gateway_plans_chatgpt_web_image_sync_with_internal_web_executor_url_imp
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:image".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -608,6 +608,7 @@ fn candidate_row(case: &RedactionFormatCase) -> StoredMinimalCandidateSelectionR
|
||||
priority: 1,
|
||||
api_formats: Some(vec![case.provider_format.api_format().to_string()]),
|
||||
endpoint_ids: Some(vec![format!("endpoint-{}", case.test_id)]),
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -119,6 +119,7 @@ async fn gateway_executes_codex_search_with_responses_permission_and_search_cont
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(false),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -112,6 +112,7 @@ fn sample_local_openai_candidate_row() -> StoredMinimalCandidateSelectionRow {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -115,6 +115,7 @@ fn sample_files_candidate_row() -> StoredMinimalCandidateSelectionRow {
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:files".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -291,6 +291,7 @@ fn sample_models_candidate_row(
|
||||
priority: 1,
|
||||
api_formats: Some(vec![api_format.to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -74,6 +74,7 @@ fn sample_codex_models_candidate_row(
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
},
|
||||
]);
|
||||
row
|
||||
|
||||
@@ -101,6 +101,7 @@ pub(super) fn sample_local_openai_candidate_row() -> StoredMinimalCandidateSelec
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:chat".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -1611,6 +1611,7 @@ async fn gateway_records_failed_usage_when_all_local_claude_cli_candidates_are_s
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
@@ -1926,6 +1927,7 @@ fn gateway_keeps_failed_usage_request_capture_lightweight_for_large_local_claude
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:responses".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -268,6 +268,7 @@ fn sample_candidate_row(spec: ProviderSpec) -> StoredMinimalCandidateSelectionRo
|
||||
priority: 1,
|
||||
api_formats: Some(vec![spec.api_format.to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(true),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -110,6 +110,7 @@ async fn gateway_executes_gemini_video_create_via_local_decision_gate_with_local
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["gemini:video".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(false),
|
||||
model_is_active: true,
|
||||
|
||||
@@ -115,6 +115,7 @@ async fn gateway_executes_openai_video_create_via_local_decision_gate_with_local
|
||||
priority: 1,
|
||||
api_formats: Some(vec!["openai:video".to_string()]),
|
||||
endpoint_ids: None,
|
||||
operations: None,
|
||||
}]),
|
||||
model_supports_streaming: Some(false),
|
||||
model_is_active: true,
|
||||
|
||||
Reference in New Issue
Block a user