Unify pool score display as account health

This commit is contained in:
fawney19
2026-05-12 15:27:21 +08:00
parent fa73655134
commit 9c05b5f4e0
14 changed files with 401 additions and 518 deletions

View File

@@ -5,8 +5,7 @@ use aether_data::repository::pool_scores::InMemoryPoolMemberScoreRepository;
use aether_data::repository::provider_catalog::InMemoryProviderCatalogReadRepository;
use aether_data::repository::usage::InMemoryUsageReadRepository;
use aether_data_contracts::repository::pool_scores::{
PoolMemberHardState, PoolMemberProbeStatus, StoredPoolMemberScore, POOL_KIND_PROVIDER_KEY_POOL,
POOL_MEMBER_KIND_PROVIDER_API_KEY, POOL_SCORE_SCOPE_KIND_MODEL,
PoolMemberHardState, PoolMemberIdentity, PoolMemberProbeStatus, StoredPoolMemberScore,
};
use aether_data_contracts::repository::provider_catalog::ProviderCatalogReadRepository;
use aether_data_contracts::repository::usage::StoredRequestUsageAudit;
@@ -20,6 +19,7 @@ use super::super::{
build_router_with_state, sample_endpoint, sample_key, sample_provider, start_server, AppState,
};
use crate::admin_api::{maybe_build_local_admin_pool_response, AdminAppState, AdminRequestContext};
use crate::ai_serving::{provider_key_pool_score_id, provider_key_pool_score_scope};
use crate::audit::AdminAuditEvent;
use crate::constants::{
GATEWAY_HEADER, TRUSTED_ADMIN_MANAGEMENT_TOKEN_ID_HEADER, TRUSTED_ADMIN_SESSION_ID_HEADER,
@@ -372,16 +372,19 @@ async fn gateway_handles_admin_pool_scores_locally_with_trusted_admin_principal(
Vec::new(),
vec![key.clone()],
));
let score_scope = provider_key_pool_score_scope();
let score_identity = PoolMemberIdentity::provider_api_key("provider-openai", "key-openai-a");
let score_id = provider_key_pool_score_id(&score_identity, &score_scope);
let pool_score_repository = Arc::new(InMemoryPoolMemberScoreRepository::seed(vec![
StoredPoolMemberScore {
id: "pms-provider-openai-key-openai-a-openai-chat-model-1".to_string(),
pool_kind: POOL_KIND_PROVIDER_KEY_POOL.to_string(),
pool_id: "provider-openai".to_string(),
member_kind: POOL_MEMBER_KIND_PROVIDER_API_KEY.to_string(),
member_id: "key-openai-a".to_string(),
capability: "openai:chat".to_string(),
scope_kind: POOL_SCORE_SCOPE_KIND_MODEL.to_string(),
scope_id: Some("model-1".to_string()),
id: score_id,
pool_kind: score_identity.pool_kind.clone(),
pool_id: score_identity.pool_id.clone(),
member_kind: score_identity.member_kind.clone(),
member_id: score_identity.member_id.clone(),
capability: score_scope.capability.clone(),
scope_kind: score_scope.scope_kind.clone(),
scope_id: score_scope.scope_id.clone(),
score: 0.875,
hard_state: PoolMemberHardState::Available,
score_version: 1,
@@ -414,7 +417,7 @@ async fn gateway_handles_admin_pool_scores_locally_with_trusted_admin_principal(
let response = reqwest::Client::new()
.get(format!(
"{gateway_url}/api/admin/pool/provider-openai/scores?api_format=openai:chat&model_id=model-1"
"{gateway_url}/api/admin/pool/provider-openai/scores"
))
.header(crate::constants::GATEWAY_HEADER, "rust-phase3b")
.header(TRUSTED_ADMIN_USER_ID_HEADER, "admin-user-123")
@@ -430,6 +433,9 @@ async fn gateway_handles_admin_pool_scores_locally_with_trusted_admin_principal(
Some(1)
);
assert_eq!(payload["items"][0]["member_id"], json!("key-openai-a"));
assert_eq!(payload["items"][0]["capability"], json!("account"));
assert_eq!(payload["items"][0]["scope_kind"], json!("account"));
assert_eq!(payload["items"][0]["scope_id"], serde_json::Value::Null);
assert_eq!(payload["items"][0]["key"]["name"], json!("score key"));
assert_eq!(payload["items"][0]["probe_status"], json!("ok"));
@@ -734,14 +740,46 @@ async fn gateway_handles_admin_pool_list_keys_locally_with_trusted_admin_princip
Vec::new(),
vec![primary_key, cooldown_key, inactive_key],
));
let score_scope = provider_key_pool_score_scope();
let score_identity = PoolMemberIdentity::provider_api_key("provider-openai", "key-openai-a");
let pool_score_repository = Arc::new(InMemoryPoolMemberScoreRepository::seed(vec![
StoredPoolMemberScore {
id: provider_key_pool_score_id(&score_identity, &score_scope),
pool_kind: score_identity.pool_kind.clone(),
pool_id: score_identity.pool_id.clone(),
member_kind: score_identity.member_kind.clone(),
member_id: score_identity.member_id.clone(),
capability: score_scope.capability.clone(),
scope_kind: score_scope.scope_kind.clone(),
scope_id: score_scope.scope_id.clone(),
score: 0.875,
hard_state: PoolMemberHardState::Available,
score_version: 1,
score_reason: json!({ "weights": { "manual_priority": 0.3 } }),
last_ranked_at: Some(1_700_000_000),
last_scheduled_at: Some(1_700_000_010),
last_success_at: Some(1_700_000_020),
last_failure_at: None,
failure_count: 0,
last_probe_attempt_at: Some(1_700_000_030),
last_probe_success_at: Some(1_700_000_040),
last_probe_failure_at: None,
probe_failure_count: 0,
probe_status: PoolMemberProbeStatus::Ok,
updated_at: 1_700_000_050,
},
]));
let (upstream_url, upstream_handle) = start_server(upstream).await;
let gateway = build_router_with_state(
AppState::new()
.expect("gateway should build")
.with_data_state_for_tests(GatewayDataState::with_provider_catalog_reader_for_tests(
provider_catalog_repository,
)),
.with_data_state_for_tests(
GatewayDataState::with_provider_catalog_reader_for_tests(
provider_catalog_repository,
)
.with_pool_score_repository_for_tests(pool_score_repository),
),
);
let (gateway_url, gateway_handle) = start_server(gateway).await;
@@ -765,6 +803,9 @@ async fn gateway_handles_admin_pool_list_keys_locally_with_trusted_admin_princip
let keys = payload["keys"].as_array().expect("keys should be array");
assert_eq!(keys.len(), 2);
assert_eq!(keys[0]["key_name"], json!("alpha"));
assert_eq!(keys[0]["pool_score"]["score"], json!(0.875));
assert_eq!(keys[0]["pool_score"]["scope_kind"], json!("account"));
assert_eq!(keys[0]["pool_score"]["scope_id"], serde_json::Value::Null);
assert_eq!(keys[0]["scheduling_status"], json!("available"));
assert_eq!(keys[1]["key_name"], json!("beta"));
assert_eq!(keys[1]["scheduling_reason"], json!("available"));