prakash 1.2.0

Optics and light simulation — ray optics, wave optics, spectral math, lens geometry, atmospheric scattering, physically-based rendering
Documentation
//! AI integration — daimon/hoosh client for prakash.

use serde::{Deserialize, Serialize};

use crate::error::{PrakashError, Result};

/// Configuration for the daimon AI agent service.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DaimonConfig {
    /// HTTP endpoint URL for the daimon service.
    pub endpoint: String,
    /// Optional API key for authentication.
    pub api_key: Option<String>,
}

impl Default for DaimonConfig {
    fn default() -> Self {
        Self {
            endpoint: "http://localhost:8090".into(),
            api_key: None,
        }
    }
}

/// Configuration for the hoosh AI orchestrator service.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HooshConfig {
    /// HTTP endpoint URL for the hoosh service.
    pub endpoint: String,
}

impl Default for HooshConfig {
    fn default() -> Self {
        Self {
            endpoint: "http://localhost:8088".into(),
        }
    }
}

/// HTTP client for the daimon AI agent service.
pub struct DaimonClient {
    config: DaimonConfig,
    client: reqwest::Client,
}

impl DaimonClient {
    /// Create a new daimon client from configuration.
    pub fn new(config: DaimonConfig) -> Result<Self> {
        let client = reqwest::Client::builder()
            .timeout(std::time::Duration::from_secs(30))
            .build()
            .map_err(|e| PrakashError::InvalidParameter {
                reason: format!("failed to build HTTP client: {e}").into(),
            })?;
        Ok(Self { config, client })
    }

    /// Register this crate as an agent with the daimon service.
    pub async fn register_agent(&self) -> Result<String> {
        let body = serde_json::json!({
            "name": "prakash",
            "capabilities": ["optics", "ray_tracing", "spectral", "pbr", "wave_optics"],
        });
        let resp = self
            .client
            .post(format!("{}/v1/agents/register", self.config.endpoint))
            .json(&body)
            .send()
            .await
            .map_err(|e| PrakashError::InvalidParameter {
                reason: format!("registration request failed: {e}").into(),
            })?;
        let data: serde_json::Value =
            resp.json()
                .await
                .map_err(|e| PrakashError::InvalidParameter {
                    reason: format!("invalid registration response: {e}").into(),
                })?;
        Ok(data["agent_id"].as_str().unwrap_or("unknown").to_string())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_default_config() {
        let c = DaimonConfig::default();
        assert_eq!(c.endpoint, "http://localhost:8090");
        assert!(c.api_key.is_none());
    }

    #[test]
    fn test_hoosh_default() {
        let c = HooshConfig::default();
        assert_eq!(c.endpoint, "http://localhost:8088");
    }

    #[test]
    fn test_daimon_client_new() {
        let config = DaimonConfig::default();
        let client = DaimonClient::new(config);
        assert!(client.is_ok());
    }

    #[test]
    fn test_daimon_config_with_api_key() {
        let c = DaimonConfig {
            endpoint: "https://custom.host:9090".into(),
            api_key: Some("test-key".into()),
        };
        assert_eq!(c.api_key.as_deref(), Some("test-key"));
    }

    #[test]
    fn test_daimon_config_serde_roundtrip() {
        let c = DaimonConfig {
            endpoint: "http://example.com".into(),
            api_key: Some("key123".into()),
        };
        let json = serde_json::to_string(&c).unwrap();
        let back: DaimonConfig = serde_json::from_str(&json).unwrap();
        assert_eq!(back.endpoint, c.endpoint);
        assert_eq!(back.api_key, c.api_key);
    }

    #[test]
    fn test_hoosh_config_serde_roundtrip() {
        let c = HooshConfig::default();
        let json = serde_json::to_string(&c).unwrap();
        let back: HooshConfig = serde_json::from_str(&json).unwrap();
        assert_eq!(back.endpoint, c.endpoint);
    }
}