rsclaw 0.0.1-alpha.1

rsclaw: High-performance AI agent (BETA). Optimized for M4 Max and 2GB VPS. 100% compatible with openclaw
Documentation
use super::router::MessageRouter;
use super::types::{ChannelType, HealthResponse, InboundMessage, OutboundMessage};
use crate::agent::AgentManager;
use axum::{
    extract::State,
    http::StatusCode,
    response::IntoResponse,
    routing::{get, post},
    Json, Router,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::RwLock;

/// HTTP message request.
#[derive(Debug, Deserialize)]
pub struct MessageRequest {
    pub text: String,
    pub session_id: Option<String>,
    pub metadata: Option<serde_json::Value>,
}

/// HTTP message response.
#[derive(Debug, Serialize)]
pub struct MessageResponse {
    pub text: String,
    pub status: String,
    pub error: Option<String>,
}

/// HTTP channel handler.
pub struct HttpChannel {
    router: Arc<MessageRouter>,
    start_time: Instant,
}

impl HttpChannel {
    /// Create a new HTTP channel.
    pub fn new(agent_manager: Arc<RwLock<AgentManager>>) -> Self {
        Self {
            router: Arc::new(MessageRouter::new(agent_manager)),
            start_time: Instant::now(),
        }
    }

    /// Build the Axum router.
    pub fn build_router(self: Arc<Self>) -> Router {
        Router::new()
            .route("/v1/health", get(Self::health_handler))
            .route("/v1/message", post(Self::message_handler))
            .with_state(self)
    }

    async fn health_handler(
        State(channel): State<Arc<Self>>,
    ) -> impl IntoResponse {
        let uptime = channel.start_time.elapsed().as_secs();
        let response = HealthResponse {
            status: Arc::from("ok"),
            version: Arc::from(env!("CARGO_PKG_VERSION")),
            uptime_secs: uptime,
        };
        Json(response)
    }

    async fn message_handler(
        State(channel): State<Arc<Self>>,
        Json(request): Json<MessageRequest>,
    ) -> impl IntoResponse {
        let session_id = request.session_id
            .unwrap_or_else(|| uuid::Uuid::new_v4().to_string());

        let inbound = InboundMessage {
            text: Arc::from(request.text.as_str()),
            session_id: Arc::from(session_id.as_str()),
            channel: ChannelType::Http,
            user_id: None,
            metadata: request.metadata,
        };

        match channel.router.route(inbound).await {
            Ok(response) => {
                let json_response = MessageResponse {
                    text: response.text.to_string(),
                    status: "ok".to_string(),
                    error: response.error.map(|e| e.to_string()),
                };
                (StatusCode::OK, Json(json_response))
            }
            Err(e) => {
                let json_response = MessageResponse {
                    text: String::new(),
                    status: "error".to_string(),
                    error: Some(e.to_string()),
                };
                (StatusCode::INTERNAL_SERVER_ERROR, Json(json_response))
            }
        }
    }
}