fix(gateway): 修复 local finalize 与 usage 聚合相关测试失败

- 修正内存 usage 仓库的时间单位处理,恢复 stats/monitoring/dashboard/wallet/pool 聚合结果
- 允许 openai:compact 走 CLI 响应转换与本地 finalize cross-format 路径
- 更新 cross-format 测试夹具,显式启用 enable_format_conversion
- 调整 failover 测试优先级,消除候选平局导致的顺序不稳定
- 补齐 ai_pipeline 架构断言所需导入并清理残留 runtime 空目录
This commit is contained in:
AAEE86
2026-04-10 18:27:02 +08:00
parent 82c3c33610
commit b1f6fff0a5
9 changed files with 27 additions and 32 deletions

View File

@@ -77,7 +77,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
sync_cli_response_conversion_kind("claude:cli", "openai:compact"), sync_cli_response_conversion_kind("claude:cli", "openai:compact"),
None Some(SyncCliResponseConversionKind::ToOpenAIFamilyCli)
); );
assert_eq!( assert_eq!(
sync_cli_response_conversion_kind("openai:compact", "claude:cli"), sync_cli_response_conversion_kind("openai:compact", "claude:cli"),

View File

@@ -1,5 +1,7 @@
use tracing::warn; use tracing::warn;
use aether_scheduler_core::SchedulerMinimalCandidateSelectionCandidate;
use crate::ai_pipeline::transport::resolve_transport_proxy_snapshot; use crate::ai_pipeline::transport::resolve_transport_proxy_snapshot;
use crate::ai_pipeline::{ use crate::ai_pipeline::{
GatewayAuthApiKeySnapshot, GatewayProviderTransportSnapshot, PlannerAppState, GatewayAuthApiKeySnapshot, GatewayProviderTransportSnapshot, PlannerAppState,
@@ -9,7 +11,6 @@ use crate::scheduler::config::{read_scheduler_ordering_config, SchedulerOrdering
use aether_scheduler_core::{ use aether_scheduler_core::{
build_scheduler_affinity_cache_key_for_api_key_id, compare_candidates_by_priority_mode, build_scheduler_affinity_cache_key_for_api_key_id, compare_candidates_by_priority_mode,
requested_capability_priority_for_candidate, SchedulerAffinityTarget, requested_capability_priority_for_candidate, SchedulerAffinityTarget,
SchedulerMinimalCandidateSelectionCandidate,
}; };
const PLANNER_SCHEDULER_AFFINITY_MAX_ENTRIES: usize = 10_000; const PLANNER_SCHEDULER_AFFINITY_MAX_ENTRIES: usize = 10_000;

View File

@@ -124,7 +124,7 @@ async fn gateway_executes_openai_chat_sync_upstream_stream_via_local_finalize_re
.with_transport_fields( .with_transport_fields(
true, true,
false, false,
false, true,
None, None,
Some(2), Some(2),
None, None,
@@ -614,7 +614,7 @@ async fn gateway_executes_openai_chat_cross_format_upstream_stream_via_local_fin
.with_transport_fields( .with_transport_fields(
true, true,
false, false,
false, true,
None, None,
Some(2), Some(2),
None, None,
@@ -1047,7 +1047,7 @@ async fn gateway_executes_openai_chat_cross_format_tool_use_upstream_stream_via_
.with_transport_fields( .with_transport_fields(
true, true,
false, false,
false, true,
None, None,
Some(2), Some(2),
None, None,
@@ -1497,7 +1497,7 @@ async fn gateway_skips_openai_chat_antigravity_cross_format_sync_candidate_as_tr
.with_transport_fields( .with_transport_fields(
true, true,
false, false,
false, true,
None, None,
Some(2), Some(2),
None, None,
@@ -1857,7 +1857,7 @@ async fn gateway_executes_openai_chat_cross_format_claude_upstream_sync_via_loca
.with_transport_fields( .with_transport_fields(
true, true,
false, false,
false, true,
None, None,
Some(2), Some(2),
None, None,
@@ -2205,7 +2205,7 @@ async fn gateway_executes_openai_chat_cross_format_gemini_upstream_sync_via_loca
.with_transport_fields( .with_transport_fields(
true, true,
false, false,
false, true,
None, None,
Some(2), Some(2),
None, None,

View File

@@ -122,7 +122,7 @@ async fn gateway_executes_openai_cli_cross_format_upstream_stream_via_local_fina
.with_transport_fields( .with_transport_fields(
true, true,
false, false,
false, true,
None, None,
Some(2), Some(2),
None, None,
@@ -577,7 +577,7 @@ async fn gateway_executes_openai_cli_cross_format_function_call_upstream_stream_
.with_transport_fields( .with_transport_fields(
true, true,
false, false,
false, true,
None, None,
Some(2), Some(2),
None, None,
@@ -1046,7 +1046,7 @@ async fn gateway_executes_openai_cli_antigravity_cross_format_upstream_stream_vi
.with_transport_fields( .with_transport_fields(
true, true,
false, false,
false, true,
None, None,
Some(2), Some(2),
None, None,

View File

@@ -116,7 +116,7 @@ async fn gateway_executes_openai_chat_stream_via_local_decision_gate_without_exe
.with_transport_fields( .with_transport_fields(
true, true,
false, false,
false, true,
None, None,
Some(2), Some(2),
None, None,
@@ -540,7 +540,7 @@ async fn gateway_executes_openai_chat_stream_via_local_openai_cli_cross_format_c
.with_transport_fields( .with_transport_fields(
true, true,
false, false,
false, true,
None, None,
Some(2), Some(2),
None, None,
@@ -1762,7 +1762,7 @@ async fn gateway_retries_next_local_openai_chat_stream_candidate_with_local_fail
"provider-openai-local-stream-backup", "provider-openai-local-stream-backup",
"endpoint-openai-local-stream-backup", "endpoint-openai-local-stream-backup",
"key-openai-local-stream-backup", "key-openai-local-stream-backup",
10, 20,
2, 2,
"gpt-5-upstream-backup", "gpt-5-upstream-backup",
), ),

View File

@@ -1008,7 +1008,7 @@ async fn gateway_retries_next_local_openai_chat_sync_candidate_with_local_failov
"provider-openai-local-backup", "provider-openai-local-backup",
"endpoint-openai-local-backup", "endpoint-openai-local-backup",
"key-openai-local-backup", "key-openai-local-backup",
10, 20,
2, 2,
"gpt-5-upstream-backup", "gpt-5-upstream-backup",
), ),

View File

@@ -114,9 +114,13 @@ pub fn sync_cli_response_conversion_kind(
if !is_standard_api_format(provider_api_format.as_str()) { if !is_standard_api_format(provider_api_format.as_str()) {
return None; return None;
} }
request_conversion_kind(client_api_format.as_str(), provider_api_format.as_str())?; if client_api_format != "openai:compact" {
request_conversion_kind(client_api_format.as_str(), provider_api_format.as_str())?;
}
match client_api_format.as_str() { match client_api_format.as_str() {
"openai:cli" => Some(SyncCliResponseConversionKind::ToOpenAIFamilyCli), "openai:cli" | "openai:compact" => {
Some(SyncCliResponseConversionKind::ToOpenAIFamilyCli)
}
"claude:cli" => Some(SyncCliResponseConversionKind::ToClaudeCli), "claude:cli" => Some(SyncCliResponseConversionKind::ToClaudeCli),
"gemini:cli" => Some(SyncCliResponseConversionKind::ToGeminiCli), "gemini:cli" => Some(SyncCliResponseConversionKind::ToGeminiCli),
_ => None, _ => None,
@@ -303,7 +307,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
sync_cli_response_conversion_kind("claude:cli", "openai:compact"), sync_cli_response_conversion_kind("claude:cli", "openai:compact"),
None Some(SyncCliResponseConversionKind::ToOpenAIFamilyCli)
); );
assert_eq!( assert_eq!(
sync_cli_response_conversion_kind("openai:compact", "claude:cli"), sync_cli_response_conversion_kind("openai:compact", "claude:cli"),

View File

@@ -211,7 +211,7 @@ pub fn maybe_build_openai_cli_cross_format_sync_product_from_normalized_payload(
.trim() .trim()
.to_ascii_lowercase(); .to_ascii_lowercase();
if client_api_format != "openai:cli" if !matches!(client_api_format.as_str(), "openai:cli" | "openai:compact")
|| sync_cli_response_conversion_kind(&provider_api_format, &client_api_format).is_none() || sync_cli_response_conversion_kind(&provider_api_format, &client_api_format).is_none()
{ {
return Ok(None); return Ok(None);

View File

@@ -10,16 +10,6 @@ use super::{
}; };
use crate::DataLayerError; use crate::DataLayerError;
const MILLIS_PER_SECOND: u64 = 1000;
fn unix_secs_to_ms(unix_secs: u64) -> u64 {
unix_secs.saturating_mul(MILLIS_PER_SECOND)
}
fn unix_ms_to_secs(unix_ms: u64) -> u64 {
unix_ms / MILLIS_PER_SECOND
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct InMemoryUsageReadRepository { pub struct InMemoryUsageReadRepository {
by_request_id: RwLock<BTreeMap<String, StoredRequestUsageAudit>>, by_request_id: RwLock<BTreeMap<String, StoredRequestUsageAudit>>,
@@ -90,12 +80,12 @@ impl UsageReadRepository for InMemoryUsageReadRepository {
.values() .values()
.filter(|item| { .filter(|item| {
if let Some(created_from_unix_secs) = query.created_from_unix_secs { if let Some(created_from_unix_secs) = query.created_from_unix_secs {
if item.created_at_unix_ms < unix_secs_to_ms(created_from_unix_secs) { if item.created_at_unix_ms < created_from_unix_secs {
return false; return false;
} }
} }
if let Some(created_until_unix_secs) = query.created_until_unix_secs { if let Some(created_until_unix_secs) = query.created_until_unix_secs {
if item.created_at_unix_ms >= unix_secs_to_ms(created_until_unix_secs) { if item.created_at_unix_ms >= created_until_unix_secs {
return false; return false;
} }
} }
@@ -210,7 +200,7 @@ impl UsageReadRepository for InMemoryUsageReadRepository {
entry entry
.last_used_at_unix_secs .last_used_at_unix_secs
.unwrap_or(0) .unwrap_or(0)
.max(unix_ms_to_secs(item.created_at_unix_ms)), .max(item.created_at_unix_ms),
); );
} }
Ok(summaries) Ok(summaries)