Skip to content

Commit d3a8702

Browse files
authored
fix(agent): add provider health check probe (#358)
## Summary - Add an authenticated provider/model health probe endpoint backed by aionrs provider execution. - Keep health probes out of conversation persistence and session state. - Add structured health diagnostics and AWS credential error classification. ## Test plan - [x] `MACOSX_DEPLOYMENT_TARGET=15.5 just push -u origin fix/model-health-probe` --------- Co-authored-by: zynx <>
1 parent 0771eff commit d3a8702

10 files changed

Lines changed: 555 additions & 11 deletions

File tree

crates/aionui-ai-agent/src/factory/aionrs.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ pub(super) async fn build(
177177
/// Mirrors the frontend `src/process/agent/aionrs/envBuilder.ts` mapping.
178178
/// For `new-api` platform, per-model protocol overrides from `model_protocols`
179179
/// JSON take precedence.
180-
fn map_aionrs_provider(platform: &str, model_id: &str, model_protocols: Option<&str>) -> String {
180+
pub(crate) fn map_aionrs_provider(platform: &str, model_id: &str, model_protocols: Option<&str>) -> String {
181181
if platform == "new-api"
182182
&& let Some(protocols_json) = model_protocols
183183
&& let Ok(map) = serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(protocols_json)
@@ -202,7 +202,7 @@ fn map_aionrs_provider(platform: &str, model_id: &str, model_protocols: Option<&
202202
/// - Strips trailing `/v1` from base_url (aionrs appends its own path)
203203
/// - Gemini: prepends `/v1beta/openai` and overrides `api_path`
204204
/// - OpenAI official (`api.openai.com`): sets `max_completion_tokens`
205-
fn resolve_aionrs_url_and_compat(
205+
pub(crate) fn resolve_aionrs_url_and_compat(
206206
platform: &str,
207207
raw_base_url: &str,
208208
mapped_provider: &str,
@@ -249,7 +249,7 @@ fn normalize_aionrs_base_url(url: &str) -> String {
249249
trimmed.strip_suffix("/v1").unwrap_or(trimmed).to_owned()
250250
}
251251

252-
fn resolve_bedrock_config(json: Option<&str>) -> Option<aion_config::config::BedrockConfig> {
252+
pub(crate) fn resolve_bedrock_config(json: Option<&str>) -> Option<aion_config::config::BedrockConfig> {
253253
let bc: aionui_api_types::BedrockConfig = serde_json::from_str(json?).ok()?;
254254
Some(aion_config::config::BedrockConfig {
255255
region: Some(bc.region),

crates/aionui-ai-agent/src/factory/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pub mod acp_assembler;
22

33
mod acp;
4-
mod aionrs;
4+
pub(crate) mod aionrs;
55
mod context;
66
mod nanobot;
77
mod openclaw;

crates/aionui-ai-agent/src/routes/agent.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use axum::routing::{get, patch, post, put};
1313

1414
use aionui_api_types::{
1515
AcpHealthCheckRequest, AcpHealthCheckResponse, AgentMetadata, ApiResponse, CustomAgentUpsertRequest,
16-
DeleteCustomAgentResponse, SetEnabledRequest, TryConnectCustomAgentRequest, TryConnectCustomAgentResponse,
16+
DeleteCustomAgentResponse, ProviderHealthCheckRequest, ProviderHealthCheckResponse, SetEnabledRequest,
17+
TryConnectCustomAgentRequest, TryConnectCustomAgentResponse,
1718
};
1819
use aionui_auth::CurrentUser;
1920
use aionui_common::AppError;
@@ -25,6 +26,7 @@ pub fn agent_routes(state: AgentRouterState) -> Router {
2526
.route("/api/agents", get(list_agents))
2627
.route("/api/agents/refresh", post(refresh_agents))
2728
.route("/api/agents/health-check", post(health_check))
29+
.route("/api/agents/provider-health-check", post(provider_health_check))
2830
.route("/api/agents/{id}/enabled", patch(set_agent_enabled))
2931
.route("/api/agents/custom", post(create_custom))
3032
.route("/api/agents/custom/{id}", put(update_custom).delete(delete_custom))
@@ -55,6 +57,15 @@ async fn health_check(
5557
Ok(Json(ApiResponse::ok(state.service.acp_health_check(req).await?)))
5658
}
5759

60+
async fn provider_health_check(
61+
State(state): State<AgentRouterState>,
62+
Extension(_user): Extension<CurrentUser>,
63+
body: Result<Json<ProviderHealthCheckRequest>, JsonRejection>,
64+
) -> Result<Json<ApiResponse<ProviderHealthCheckResponse>>, AppError> {
65+
let Json(req) = body.map_err(|e| AppError::BadRequest(e.to_string()))?;
66+
Ok(Json(ApiResponse::ok(state.service.provider_health_check(req).await?)))
67+
}
68+
5869
async fn try_connect_custom(
5970
State(state): State<AgentRouterState>,
6071
Extension(_user): Extension<CurrentUser>,

crates/aionui-ai-agent/src/services/agent.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,35 @@
1515
use std::path::PathBuf;
1616
use std::sync::Arc;
1717

18-
use aionui_api_types::{AcpHealthCheckRequest, AcpHealthCheckResponse, AgentMetadata};
18+
use aionui_api_types::{
19+
AcpHealthCheckRequest, AcpHealthCheckResponse, AgentMetadata, ProviderHealthCheckRequest,
20+
ProviderHealthCheckResponse,
21+
};
1922
use aionui_common::AppError;
23+
use aionui_db::IProviderRepository;
2024

25+
use super::provider_health::ProviderHealthCheckService;
2126
use crate::registry::AgentRegistry;
2227

2328
pub struct AgentService {
2429
registry: Arc<AgentRegistry>,
2530
data_dir: PathBuf,
31+
provider_health: ProviderHealthCheckService,
2632
}
2733

2834
impl AgentService {
29-
pub fn new(registry: Arc<AgentRegistry>, data_dir: PathBuf) -> Arc<Self> {
30-
Arc::new(Self { registry, data_dir })
35+
pub fn new(
36+
registry: Arc<AgentRegistry>,
37+
provider_repo: Arc<dyn IProviderRepository>,
38+
encryption_key: [u8; 32],
39+
data_dir: PathBuf,
40+
) -> Arc<Self> {
41+
let provider_health = ProviderHealthCheckService::new(provider_repo, encryption_key, data_dir.clone());
42+
Arc::new(Self {
43+
registry,
44+
data_dir,
45+
provider_health,
46+
})
3147
}
3248

3349
/// Data directory used by the custom-agent probe to spawn CLI
@@ -57,4 +73,11 @@ impl AgentService {
5773
pub async fn acp_health_check(&self, req: AcpHealthCheckRequest) -> Result<AcpHealthCheckResponse, AppError> {
5874
Ok(crate::protocol::cli_detect::health_check(&self.registry, &req.backend).await)
5975
}
76+
77+
pub async fn provider_health_check(
78+
&self,
79+
req: ProviderHealthCheckRequest,
80+
) -> Result<ProviderHealthCheckResponse, AppError> {
81+
self.provider_health.health_check(req).await
82+
}
6083
}

crates/aionui-ai-agent/src/services/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod agent;
22
pub mod custom;
3+
pub mod provider_health;
34
pub mod remote;
45

56
pub use agent::AgentService;

0 commit comments

Comments
 (0)