systemprompt-api 0.1.18

HTTP API server and gateway for systemprompt.io OS
Documentation
use systemprompt_database::{ServiceConfig, ServiceRepository};
use systemprompt_mcp::services::McpManager;
use systemprompt_runtime::AppContext;

use super::backend::ProxyError;

pub struct ServiceResolver;

impl ServiceResolver {
    pub async fn resolve(
        service_name: &str,
        ctx: &AppContext,
    ) -> Result<ServiceConfig, ProxyError> {
        let service_repo =
            ServiceRepository::new(ctx.db_pool()).map_err(|e| ProxyError::DatabaseError {
                service: service_name.to_string(),
                source: e,
            })?;

        let service = match service_repo.get_service_by_name(service_name).await {
            Ok(svc) => svc,
            Err(e) => {
                tracing::error!(service = %service_name, error = %e, "Database error when looking up service");
                return Err(ProxyError::DatabaseError {
                    service: service_name.to_string(),
                    source: e,
                });
            },
        };

        let Some(service) = service else {
            tracing::warn!(service = %service_name, "Service not found");
            return Err(ProxyError::ServiceNotFound {
                service: service_name.to_string(),
            });
        };

        if service.status != "running" {
            if service.status == "crashed" {
                tracing::info!(service = %service_name, "Service crashed, attempting restart");

                if Self::attempt_restart(service_name, ctx).await.is_ok() {
                    tracing::info!("Service restarted successfully, retrying proxy");
                    return Box::pin(Self::resolve(service_name, ctx)).await;
                }
            }

            tracing::warn!(service = %service_name, status = %service.status, "Service not running");
            return Err(ProxyError::ServiceNotRunning {
                service: service_name.to_string(),
                status: service.status.clone(),
            });
        }

        Ok(service)
    }

    async fn attempt_restart(service_name: &str, ctx: &AppContext) -> Result<(), ProxyError> {
        let orchestrator =
            McpManager::new(ctx.db_pool().clone()).map_err(|e| ProxyError::ServiceNotRunning {
                service: service_name.to_string(),
                status: format!("Failed to create orchestrator: {e}"),
            })?;

        match orchestrator
            .start_services(Some(service_name.to_string()))
            .await
        {
            Ok(()) => {},
            Err(e) => {
                tracing::error!(service = %service_name, error = %e, "Failed to restart service");
                return Err(ProxyError::ServiceNotRunning {
                    service: service_name.to_string(),
                    status: format!("Restart failed: {e}"),
                });
            },
        }

        tokio::time::sleep(std::time::Duration::from_millis(500)).await;
        Ok(())
    }
}