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
@@ -77,7 +77,7 @@ mod tests {
);
assert_eq!(
sync_cli_response_conversion_kind("claude:cli", "openai:compact"),
None
Some(SyncCliResponseConversionKind::ToOpenAIFamilyCli)
);
assert_eq!(
sync_cli_response_conversion_kind("openai:compact", "claude:cli"),
@@ -1,5 +1,7 @@
use tracing::warn;
use aether_scheduler_core::SchedulerMinimalCandidateSelectionCandidate;
use crate::ai_pipeline::transport::resolve_transport_proxy_snapshot;
use crate::ai_pipeline::{
GatewayAuthApiKeySnapshot, GatewayProviderTransportSnapshot, PlannerAppState,
@@ -9,7 +11,6 @@ use crate::scheduler::config::{read_scheduler_ordering_config, SchedulerOrdering
use aether_scheduler_core::{
build_scheduler_affinity_cache_key_for_api_key_id, compare_candidates_by_priority_mode,
requested_capability_priority_for_candidate, SchedulerAffinityTarget,
SchedulerMinimalCandidateSelectionCandidate,
};
const PLANNER_SCHEDULER_AFFINITY_MAX_ENTRIES: usize = 10_000;
@@ -124,7 +124,7 @@ async fn gateway_executes_openai_chat_sync_upstream_stream_via_local_finalize_re
.with_transport_fields(
true,
false,
false,
true,
None,
Some(2),
None,
@@ -614,7 +614,7 @@ async fn gateway_executes_openai_chat_cross_format_upstream_stream_via_local_fin
.with_transport_fields(
true,
false,
false,
true,
None,
Some(2),
None,
@@ -1047,7 +1047,7 @@ async fn gateway_executes_openai_chat_cross_format_tool_use_upstream_stream_via_
.with_transport_fields(
true,
false,
false,
true,
None,
Some(2),
None,
@@ -1497,7 +1497,7 @@ async fn gateway_skips_openai_chat_antigravity_cross_format_sync_candidate_as_tr
.with_transport_fields(
true,
false,
false,
true,
None,
Some(2),
None,
@@ -1857,7 +1857,7 @@ async fn gateway_executes_openai_chat_cross_format_claude_upstream_sync_via_loca
.with_transport_fields(
true,
false,
false,
true,
None,
Some(2),
None,
@@ -2205,7 +2205,7 @@ async fn gateway_executes_openai_chat_cross_format_gemini_upstream_sync_via_loca
.with_transport_fields(
true,
false,
false,
true,
None,
Some(2),
None,
@@ -122,7 +122,7 @@ async fn gateway_executes_openai_cli_cross_format_upstream_stream_via_local_fina
.with_transport_fields(
true,
false,
false,
true,
None,
Some(2),
None,
@@ -577,7 +577,7 @@ async fn gateway_executes_openai_cli_cross_format_function_call_upstream_stream_
.with_transport_fields(
true,
false,
false,
true,
None,
Some(2),
None,
@@ -1046,7 +1046,7 @@ async fn gateway_executes_openai_cli_antigravity_cross_format_upstream_stream_vi
.with_transport_fields(
true,
false,
false,
true,
None,
Some(2),
None,
@@ -116,7 +116,7 @@ async fn gateway_executes_openai_chat_stream_via_local_decision_gate_without_exe
.with_transport_fields(
true,
false,
false,
true,
None,
Some(2),
None,
@@ -540,7 +540,7 @@ async fn gateway_executes_openai_chat_stream_via_local_openai_cli_cross_format_c
.with_transport_fields(
true,
false,
false,
true,
None,
Some(2),
None,
@@ -1762,7 +1762,7 @@ async fn gateway_retries_next_local_openai_chat_stream_candidate_with_local_fail
"provider-openai-local-stream-backup",
"endpoint-openai-local-stream-backup",
"key-openai-local-stream-backup",
10,
20,
2,
"gpt-5-upstream-backup",
),
@@ -1008,7 +1008,7 @@ async fn gateway_retries_next_local_openai_chat_sync_candidate_with_local_failov
"provider-openai-local-backup",
"endpoint-openai-local-backup",
"key-openai-local-backup",
10,
20,
2,
"gpt-5-upstream-backup",
),
@@ -114,9 +114,13 @@ pub fn sync_cli_response_conversion_kind(
if !is_standard_api_format(provider_api_format.as_str()) {
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() {
"openai:cli" => Some(SyncCliResponseConversionKind::ToOpenAIFamilyCli),
"openai:cli" | "openai:compact" => {
Some(SyncCliResponseConversionKind::ToOpenAIFamilyCli)
}
"claude:cli" => Some(SyncCliResponseConversionKind::ToClaudeCli),
"gemini:cli" => Some(SyncCliResponseConversionKind::ToGeminiCli),
_ => None,
@@ -303,7 +307,7 @@ mod tests {
);
assert_eq!(
sync_cli_response_conversion_kind("claude:cli", "openai:compact"),
None
Some(SyncCliResponseConversionKind::ToOpenAIFamilyCli)
);
assert_eq!(
sync_cli_response_conversion_kind("openai:compact", "claude:cli"),
@@ -211,7 +211,7 @@ pub fn maybe_build_openai_cli_cross_format_sync_product_from_normalized_payload(
.trim()
.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()
{
return Ok(None);
@@ -10,16 +10,6 @@ use super::{
};
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)]
pub struct InMemoryUsageReadRepository {
by_request_id: RwLock<BTreeMap<String, StoredRequestUsageAudit>>,
@@ -90,12 +80,12 @@ impl UsageReadRepository for InMemoryUsageReadRepository {
.values()
.filter(|item| {
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;
}
}
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;
}
}
@@ -210,7 +200,7 @@ impl UsageReadRepository for InMemoryUsageReadRepository {
entry
.last_used_at_unix_secs
.unwrap_or(0)
.max(unix_ms_to_secs(item.created_at_unix_ms)),
.max(item.created_at_unix_ms),
);
}
Ok(summaries)