mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 08:27:46 +08:00
Add admin operations dashboard and usage state fixes
This commit is contained in:
@@ -4100,6 +4100,98 @@ mockHandlers['GET /api/admin/stats/performance/percentiles'] = async () => {
|
||||
])
|
||||
}
|
||||
|
||||
mockHandlers['GET /api/admin/stats/performance/providers'] = async () => {
|
||||
await delay()
|
||||
requireAdmin()
|
||||
return createMockResponse({
|
||||
summary: {
|
||||
request_count: 18420,
|
||||
success_rate: 97.36,
|
||||
avg_output_tps: 78.4,
|
||||
avg_first_byte_time_ms: 318,
|
||||
avg_response_time_ms: 1640,
|
||||
p90_response_time_ms: 2860,
|
||||
p99_response_time_ms: 9120,
|
||||
p90_first_byte_time_ms: 690,
|
||||
p99_first_byte_time_ms: 2210,
|
||||
tps_sample_count: 17120,
|
||||
response_time_sample_count: 18200,
|
||||
first_byte_sample_count: 16840,
|
||||
slow_request_count: 214
|
||||
},
|
||||
providers: [
|
||||
{
|
||||
provider_id: 'provider-openai',
|
||||
provider: 'OpenAI',
|
||||
request_count: 8120,
|
||||
success_count: 7930,
|
||||
error_count: 190,
|
||||
success_rate: 97.66,
|
||||
output_tokens: 1480000,
|
||||
avg_output_tps: 83.2,
|
||||
avg_first_byte_time_ms: 286,
|
||||
avg_response_time_ms: 1420,
|
||||
p90_response_time_ms: 2440,
|
||||
p99_response_time_ms: 7520,
|
||||
p90_first_byte_time_ms: 620,
|
||||
p99_first_byte_time_ms: 1840,
|
||||
tps_sample_count: 7890,
|
||||
response_time_sample_count: 8050,
|
||||
first_byte_sample_count: 7760,
|
||||
slow_request_count: 71
|
||||
},
|
||||
{
|
||||
provider_id: 'provider-anthropic',
|
||||
provider: 'Anthropic',
|
||||
request_count: 6230,
|
||||
success_count: 6044,
|
||||
error_count: 186,
|
||||
success_rate: 97.01,
|
||||
output_tokens: 1120000,
|
||||
avg_output_tps: 62.7,
|
||||
avg_first_byte_time_ms: 410,
|
||||
avg_response_time_ms: 1880,
|
||||
p90_response_time_ms: 3180,
|
||||
p99_response_time_ms: 10120,
|
||||
p90_first_byte_time_ms: 820,
|
||||
p99_first_byte_time_ms: 2680,
|
||||
tps_sample_count: 5900,
|
||||
response_time_sample_count: 6150,
|
||||
first_byte_sample_count: 5810,
|
||||
slow_request_count: 109
|
||||
},
|
||||
{
|
||||
provider_id: 'provider-gemini',
|
||||
provider: 'Gemini',
|
||||
request_count: 4070,
|
||||
success_count: 3960,
|
||||
error_count: 110,
|
||||
success_rate: 97.30,
|
||||
output_tokens: 780000,
|
||||
avg_output_tps: 91.1,
|
||||
avg_first_byte_time_ms: 240,
|
||||
avg_response_time_ms: 1210,
|
||||
p90_response_time_ms: 2190,
|
||||
p99_response_time_ms: 6420,
|
||||
p90_first_byte_time_ms: 520,
|
||||
p99_first_byte_time_ms: 1510,
|
||||
tps_sample_count: 3930,
|
||||
response_time_sample_count: 4000,
|
||||
first_byte_sample_count: 3820,
|
||||
slow_request_count: 34
|
||||
}
|
||||
],
|
||||
timeline: [
|
||||
{ date: '2026-02-01', provider_id: 'provider-openai', provider: 'OpenAI', request_count: 4020, output_tokens: 720000, avg_output_tps: 81.4, avg_first_byte_time_ms: 294, avg_response_time_ms: 1450, success_rate: 97.4, slow_request_count: 38 },
|
||||
{ date: '2026-02-02', provider_id: 'provider-openai', provider: 'OpenAI', request_count: 4100, output_tokens: 760000, avg_output_tps: 85.1, avg_first_byte_time_ms: 278, avg_response_time_ms: 1390, success_rate: 97.9, slow_request_count: 33 },
|
||||
{ date: '2026-02-01', provider_id: 'provider-anthropic', provider: 'Anthropic', request_count: 3020, output_tokens: 540000, avg_output_tps: 60.8, avg_first_byte_time_ms: 430, avg_response_time_ms: 1940, success_rate: 96.7, slow_request_count: 58 },
|
||||
{ date: '2026-02-02', provider_id: 'provider-anthropic', provider: 'Anthropic', request_count: 3210, output_tokens: 580000, avg_output_tps: 64.2, avg_first_byte_time_ms: 392, avg_response_time_ms: 1810, success_rate: 97.3, slow_request_count: 51 },
|
||||
{ date: '2026-02-01', provider_id: 'provider-gemini', provider: 'Gemini', request_count: 2020, output_tokens: 382000, avg_output_tps: 88.6, avg_first_byte_time_ms: 252, avg_response_time_ms: 1260, success_rate: 97.0, slow_request_count: 18 },
|
||||
{ date: '2026-02-02', provider_id: 'provider-gemini', provider: 'Gemini', request_count: 2050, output_tokens: 398000, avg_output_tps: 93.6, avg_first_byte_time_ms: 228, avg_response_time_ms: 1160, success_rate: 97.6, slow_request_count: 16 }
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
mockHandlers['GET /api/admin/stats/errors/distribution'] = async () => {
|
||||
await delay()
|
||||
requireAdmin()
|
||||
@@ -4249,6 +4341,241 @@ mockHandlers['GET /api/admin/monitoring/resilience/circuit-history'] = async ()
|
||||
})
|
||||
}
|
||||
|
||||
mockHandlers['GET /api/admin/monitoring/cache/stats'] = async () => {
|
||||
await delay()
|
||||
requireAdmin()
|
||||
return createMockResponse({
|
||||
status: 'ok',
|
||||
data: {
|
||||
scheduler: 'adaptive-cache-affinity',
|
||||
total_affinities: 1428,
|
||||
cache_hit_rate: 0.842,
|
||||
provider_switches: 38,
|
||||
key_switches: 74,
|
||||
cache_hits: 93842,
|
||||
cache_misses: 17610,
|
||||
scheduler_metrics: {
|
||||
cache_hits: 93842,
|
||||
cache_misses: 17610,
|
||||
cache_hit_rate: 0.842,
|
||||
total_batches: 0,
|
||||
last_batch_size: 0,
|
||||
total_candidates: 0,
|
||||
last_candidate_count: 0,
|
||||
concurrency_denied: 0,
|
||||
avg_candidates_per_batch: 0,
|
||||
scheduling_mode: 'adaptive',
|
||||
provider_priority_mode: 'weighted'
|
||||
},
|
||||
affinity_stats: {
|
||||
storage_type: 'redis',
|
||||
total_affinities: 1428,
|
||||
active_affinities: 1410,
|
||||
cache_hits: 93842,
|
||||
cache_misses: 17610,
|
||||
cache_hit_rate: 0.842,
|
||||
cache_invalidations: 42,
|
||||
provider_switches: 38,
|
||||
key_switches: 74,
|
||||
config: {
|
||||
default_ttl: 300
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
mockHandlers['GET /api/admin/monitoring/cache/redis-keys'] = async () => {
|
||||
await delay()
|
||||
requireAdmin()
|
||||
return createMockResponse({
|
||||
status: 'ok',
|
||||
data: {
|
||||
available: true,
|
||||
categories: [
|
||||
{ key: 'cache_affinity', name: '缓存亲和性', pattern: 'cache_affinity:*', description: '请求路由亲和性缓存', count: 1428 },
|
||||
{ key: 'concurrency_lock', name: '并发锁', pattern: 'concurrency:*', description: '请求并发控制锁', count: 186 },
|
||||
{ key: 'apikey', name: 'API Key', pattern: 'apikey:*', description: 'API Key 认证缓存', count: 263 },
|
||||
{ key: 'health', name: '健康检查', pattern: 'health:*', description: '端点健康状态缓存', count: 154 },
|
||||
{ key: 'models_list', name: '模型列表', pattern: 'models:list:*', description: '/v1/models 端点模型列表缓存', count: 38 },
|
||||
{ key: 'provider_balance', name: 'Provider 余额', pattern: 'provider_ops:balance:*', description: 'Provider 余额查询缓存', count: 24 }
|
||||
],
|
||||
total_keys: 2093
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
mockHandlers['GET /api/admin/proxy-nodes'] = async () => {
|
||||
await delay()
|
||||
requireAdmin()
|
||||
const now = new Date().toISOString()
|
||||
return createMockResponse({
|
||||
items: [
|
||||
{
|
||||
id: 'proxy-node-1',
|
||||
name: 'edge-shanghai-1',
|
||||
ip: '10.8.0.12',
|
||||
port: 8443,
|
||||
region: 'cn-east',
|
||||
status: 'online',
|
||||
is_manual: false,
|
||||
tunnel_mode: true,
|
||||
tunnel_connected: true,
|
||||
tunnel_connected_at: now,
|
||||
hardware_info: {
|
||||
cpu_cores: 8,
|
||||
total_memory_mb: 16384,
|
||||
os_info: 'linux'
|
||||
},
|
||||
estimated_max_concurrency: 620,
|
||||
remote_config: null,
|
||||
config_version: 12,
|
||||
registered_by: 'agent',
|
||||
last_heartbeat_at: now,
|
||||
heartbeat_interval: 15,
|
||||
active_connections: 82,
|
||||
total_requests: 43820,
|
||||
avg_latency_ms: 42,
|
||||
failed_requests: 34,
|
||||
dns_failures: 2,
|
||||
stream_errors: 7,
|
||||
proxy_metadata: {
|
||||
resource_usage: {
|
||||
system_cpu_usage_percent: 41.8,
|
||||
process_cpu_usage_percent: 18.4,
|
||||
memory_total_bytes: 17179869184,
|
||||
memory_used_bytes: 9878424780,
|
||||
memory_used_percent: 57.5,
|
||||
process_memory_bytes: 438304768,
|
||||
process_memory_percent: 2.6,
|
||||
process_uptime_secs: 86400
|
||||
}
|
||||
},
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
},
|
||||
{
|
||||
id: 'proxy-node-2',
|
||||
name: 'edge-us-west-1',
|
||||
ip: '10.8.1.18',
|
||||
port: 8443,
|
||||
region: 'us-west',
|
||||
status: 'online',
|
||||
is_manual: false,
|
||||
tunnel_mode: true,
|
||||
tunnel_connected: true,
|
||||
tunnel_connected_at: now,
|
||||
hardware_info: {
|
||||
cpu_cores: 16,
|
||||
total_memory_mb: 32768,
|
||||
os_info: 'linux'
|
||||
},
|
||||
estimated_max_concurrency: 980,
|
||||
remote_config: null,
|
||||
config_version: 9,
|
||||
registered_by: 'agent',
|
||||
last_heartbeat_at: now,
|
||||
heartbeat_interval: 15,
|
||||
active_connections: 94,
|
||||
total_requests: 58340,
|
||||
avg_latency_ms: 58,
|
||||
failed_requests: 52,
|
||||
dns_failures: 1,
|
||||
stream_errors: 9,
|
||||
proxy_metadata: {
|
||||
resource_usage: {
|
||||
system_cpu_usage_percent: 55.2,
|
||||
process_cpu_usage_percent: 22.6,
|
||||
memory_total_bytes: 34359738368,
|
||||
memory_used_bytes: 22333829939,
|
||||
memory_used_percent: 65.0,
|
||||
process_memory_bytes: 612368384,
|
||||
process_memory_percent: 1.8,
|
||||
process_uptime_secs: 172800
|
||||
}
|
||||
},
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
}
|
||||
],
|
||||
total: 2,
|
||||
skip: 0,
|
||||
limit: 200,
|
||||
rollout: null
|
||||
})
|
||||
}
|
||||
|
||||
mockHandlers['GET /api/admin/proxy-nodes/metrics/fleet'] = async () => {
|
||||
await delay()
|
||||
requireAdmin()
|
||||
const now = Math.floor(Date.now() / 1000)
|
||||
return createMockResponse({
|
||||
step: '1m',
|
||||
from: now - 3600,
|
||||
to: now,
|
||||
items: [
|
||||
{
|
||||
bucket_start_unix_secs: now - 120,
|
||||
bucket_start: new Date((now - 120) * 1000).toISOString(),
|
||||
samples: 2,
|
||||
uptime_samples: 2,
|
||||
uptime_ratio: 1,
|
||||
active_connections_sum: 168,
|
||||
active_connections_max: 92,
|
||||
active_connections_avg: 84,
|
||||
heartbeat_rtt_ms_sum: 86,
|
||||
heartbeat_rtt_ms_max: 52,
|
||||
heartbeat_rtt_ms_avg: 43,
|
||||
connect_errors_delta: 0,
|
||||
disconnects_delta: 0,
|
||||
error_events_delta: 1,
|
||||
ws_in_bytes_delta: 4849664,
|
||||
ws_out_bytes_delta: 12812288,
|
||||
ws_in_frames_delta: 18320,
|
||||
ws_out_frames_delta: 42110
|
||||
},
|
||||
{
|
||||
bucket_start_unix_secs: now - 60,
|
||||
bucket_start: new Date((now - 60) * 1000).toISOString(),
|
||||
samples: 2,
|
||||
uptime_samples: 2,
|
||||
uptime_ratio: 1,
|
||||
active_connections_sum: 176,
|
||||
active_connections_max: 94,
|
||||
active_connections_avg: 88,
|
||||
heartbeat_rtt_ms_sum: 82,
|
||||
heartbeat_rtt_ms_max: 48,
|
||||
heartbeat_rtt_ms_avg: 41,
|
||||
connect_errors_delta: 0,
|
||||
disconnects_delta: 0,
|
||||
error_events_delta: 0,
|
||||
ws_in_bytes_delta: 5154816,
|
||||
ws_out_bytes_delta: 13996032,
|
||||
ws_in_frames_delta: 19120,
|
||||
ws_out_frames_delta: 44710
|
||||
}
|
||||
],
|
||||
summary: {
|
||||
samples: 4,
|
||||
uptime_samples: 4,
|
||||
uptime_ratio: 1,
|
||||
active_connections_sum: 344,
|
||||
active_connections_max: 94,
|
||||
active_connections_avg: 86,
|
||||
heartbeat_rtt_ms_sum: 168,
|
||||
heartbeat_rtt_ms_max: 52,
|
||||
heartbeat_rtt_ms_avg: 42,
|
||||
connect_errors_delta: 0,
|
||||
disconnects_delta: 0,
|
||||
error_events_delta: 1,
|
||||
ws_in_bytes_delta: 10004480,
|
||||
ws_out_bytes_delta: 26808320,
|
||||
ws_in_frames_delta: 37440,
|
||||
ws_out_frames_delta: 86820
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
mockHandlers['GET /_gateway/metrics'] = async () => {
|
||||
await delay(60)
|
||||
requireAdmin()
|
||||
@@ -4288,6 +4615,45 @@ aether_gateway_tunnel_nodes 6
|
||||
# HELP aether_gateway_tunnel_active_streams Current number of active local relay streams.
|
||||
# TYPE aether_gateway_tunnel_active_streams gauge
|
||||
aether_gateway_tunnel_active_streams 164
|
||||
# HELP aether_gateway_tunnel_proxy_connections_available Current number of available proxy sockets.
|
||||
# TYPE aether_gateway_tunnel_proxy_connections_available gauge
|
||||
aether_gateway_tunnel_proxy_connections_available 24
|
||||
# HELP aether_gateway_tunnel_proxy_connections_closing Current number of closing proxy sockets.
|
||||
# TYPE aether_gateway_tunnel_proxy_connections_closing gauge
|
||||
aether_gateway_tunnel_proxy_connections_closing 1
|
||||
# HELP aether_gateway_tunnel_proxy_connections_draining Current number of draining proxy sockets.
|
||||
# TYPE aether_gateway_tunnel_proxy_connections_draining gauge
|
||||
aether_gateway_tunnel_proxy_connections_draining 2
|
||||
# HELP aether_gateway_tunnel_proxy_connections_soft_avoid Current number of soft-avoid proxy sockets.
|
||||
# TYPE aether_gateway_tunnel_proxy_connections_soft_avoid gauge
|
||||
aether_gateway_tunnel_proxy_connections_soft_avoid 3
|
||||
# HELP aether_gateway_tunnel_proxy_outbound_queue_depth_total Current outbound queue depth total.
|
||||
# TYPE aether_gateway_tunnel_proxy_outbound_queue_depth_total gauge
|
||||
aether_gateway_tunnel_proxy_outbound_queue_depth_total 312
|
||||
# HELP aether_gateway_tunnel_proxy_outbound_queue_depth_max Current outbound queue max depth.
|
||||
# TYPE aether_gateway_tunnel_proxy_outbound_queue_depth_max gauge
|
||||
aether_gateway_tunnel_proxy_outbound_queue_depth_max 38
|
||||
# HELP aether_gateway_tunnel_proxy_outbound_queue_capacity_total Current outbound queue capacity total.
|
||||
# TYPE aether_gateway_tunnel_proxy_outbound_queue_capacity_total gauge
|
||||
aether_gateway_tunnel_proxy_outbound_queue_capacity_total 2048
|
||||
# HELP aether_gateway_tunnel_proxy_outbound_queue_rejected_full_total Outbound queue full rejections.
|
||||
# TYPE aether_gateway_tunnel_proxy_outbound_queue_rejected_full_total counter
|
||||
aether_gateway_tunnel_proxy_outbound_queue_rejected_full_total 7
|
||||
# HELP aether_gateway_tunnel_proxy_outbound_queue_rejected_closed_total Outbound queue closed rejections.
|
||||
# TYPE aether_gateway_tunnel_proxy_outbound_queue_rejected_closed_total counter
|
||||
aether_gateway_tunnel_proxy_outbound_queue_rejected_closed_total 2
|
||||
# HELP aether_gateway_tunnel_proxy_connection_congested_total Congested proxy selections.
|
||||
# TYPE aether_gateway_tunnel_proxy_connection_congested_total counter
|
||||
aether_gateway_tunnel_proxy_connection_congested_total 21
|
||||
# HELP aether_gateway_tunnel_proxy_soft_avoid_selection_total Soft-avoid proxy selections.
|
||||
# TYPE aether_gateway_tunnel_proxy_soft_avoid_selection_total counter
|
||||
aether_gateway_tunnel_proxy_soft_avoid_selection_total 18
|
||||
# HELP aether_gateway_tunnel_proxy_selection_retry_total Proxy selection retries.
|
||||
# TYPE aether_gateway_tunnel_proxy_selection_retry_total counter
|
||||
aether_gateway_tunnel_proxy_selection_retry_total 11
|
||||
# HELP aether_gateway_tunnel_proxy_selection_unavailable_total Proxy selection unavailable events.
|
||||
# TYPE aether_gateway_tunnel_proxy_selection_unavailable_total counter
|
||||
aether_gateway_tunnel_proxy_selection_unavailable_total 4
|
||||
# HELP aether_gateway_decision_remote_total Number of requests that fell back to Python decision endpoints.
|
||||
# TYPE aether_gateway_decision_remote_total counter
|
||||
aether_gateway_decision_remote_total{route_kind="chat",reason="local_decision_miss"} 4
|
||||
@@ -4343,7 +4709,7 @@ mockHandlers['GET /api/admin/stats/time-series'] = async () => {
|
||||
await delay()
|
||||
requireAdmin()
|
||||
return createMockResponse([
|
||||
{ date: '2026-02-01', total_requests: 120, input_tokens: 20000, output_tokens: 30000, total_cost: 12.3 },
|
||||
{ date: '2026-02-02', total_requests: 140, input_tokens: 22000, output_tokens: 32000, total_cost: 13.8 }
|
||||
{ date: '2026-02-01', total_requests: 120, input_tokens: 20000, output_tokens: 30000, total_tokens: 50000, total_cost: 12.3 },
|
||||
{ date: '2026-02-02', total_requests: 140, input_tokens: 22000, output_tokens: 32000, total_tokens: 54000, total_cost: 13.8 }
|
||||
])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user