mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
feat(adaptive): 完善自适应 RPM 学习并将 Pool 调度状态与健康分解耦
- orchestration 新增 AdaptiveSuccess 效果,在成功回报路径上根据利用率窗口扩张 learned_rpm_limit - 429 路径改用 429_observation/adjustment 记录以及基于历史的置信度评估,新增 last_rpm_peak 边界字段 - Pool 调度状态不再因 health_score 低或熔断而降级/拦截,前端同步移除相关按钮与文案兜底 - 新增前端 poolTrace 工具(附测试)承接原 HorizontalRequestTimeline 内的候选合并逻辑
This commit is contained in:
@@ -761,6 +761,93 @@ async fn gateway_marks_account_blocked_pool_key_in_list_keys_response() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn gateway_ignores_health_signals_in_pool_scheduling_status() {
|
||||
let mut provider = sample_provider("provider-openai", "openai", 10).with_transport_fields(
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(json!({
|
||||
"pool_advanced": {
|
||||
"enabled": true
|
||||
}
|
||||
})),
|
||||
);
|
||||
provider.provider_type = "openai".to_string();
|
||||
|
||||
let mut circuit_key = sample_key(
|
||||
"key-openai-circuit",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
"sk-circuit",
|
||||
);
|
||||
circuit_key.name = "circuit-open".to_string();
|
||||
circuit_key.circuit_breaker_by_format = Some(json!({
|
||||
"openai:chat": {
|
||||
"open": true
|
||||
}
|
||||
}));
|
||||
|
||||
let mut low_health_key = sample_key(
|
||||
"key-openai-health",
|
||||
"provider-openai",
|
||||
"openai:chat",
|
||||
"sk-health",
|
||||
);
|
||||
low_health_key.name = "low-health".to_string();
|
||||
low_health_key.health_by_format = Some(json!({
|
||||
"openai:chat": {
|
||||
"health_score": 0.2
|
||||
}
|
||||
}));
|
||||
|
||||
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
|
||||
vec![provider],
|
||||
Vec::new(),
|
||||
vec![circuit_key, low_health_key],
|
||||
));
|
||||
let state = AppState::new()
|
||||
.expect("gateway should build")
|
||||
.with_data_state_for_tests(GatewayDataState::with_provider_catalog_reader_for_tests(
|
||||
provider_catalog_repository,
|
||||
));
|
||||
|
||||
let response = local_admin_pool_response(
|
||||
&state,
|
||||
http::Method::GET,
|
||||
"/api/admin/pool/provider-openai/keys?page=1&page_size=50&status=all",
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let payload: serde_json::Value = serde_json::from_slice(
|
||||
&to_bytes(response.into_body(), usize::MAX)
|
||||
.await
|
||||
.expect("body should read"),
|
||||
)
|
||||
.expect("json body should parse");
|
||||
let keys = payload["keys"].as_array().expect("keys should be array");
|
||||
|
||||
assert_eq!(keys.len(), 2);
|
||||
assert_eq!(keys[0]["key_name"], json!("circuit-open"));
|
||||
assert_eq!(keys[0]["circuit_breaker_open"], json!(true));
|
||||
assert_eq!(keys[0]["scheduling_status"], json!("available"));
|
||||
assert_eq!(keys[0]["scheduling_reason"], json!("available"));
|
||||
assert_eq!(keys[0]["scheduling_label"], json!("可用"));
|
||||
|
||||
assert_eq!(keys[1]["key_name"], json!("low-health"));
|
||||
assert_eq!(keys[1]["health_score"], json!(0.2));
|
||||
assert_eq!(keys[1]["scheduling_status"], json!("available"));
|
||||
assert_eq!(keys[1]["scheduling_reason"], json!("available"));
|
||||
assert_eq!(keys[1]["scheduling_label"], json!("可用"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn gateway_handles_admin_pool_list_keys_with_quota_compatibility_fields() {
|
||||
let upstream_hits = Arc::new(Mutex::new(0usize));
|
||||
|
||||
Reference in New Issue
Block a user