Skip to main content

feagi_agent/sdk/registration/
mod.rs

1//! HTTP registration helpers for FEAGI agents.
2
3use crate::core::SdkError;
4use crate::sdk::base::TopologyCache;
5
6/// Configuration for FEAGI HTTP API access.
7#[derive(Debug, Clone)]
8pub struct FeagiApiConfig {
9    host: String,
10    port: u16,
11    timeout: std::time::Duration,
12}
13
14impl FeagiApiConfig {
15    /// Create a new FEAGI API configuration.
16    pub fn new(host: impl Into<String>, port: u16, timeout: std::time::Duration) -> Self {
17        Self {
18            host: host.into(),
19            port,
20            timeout,
21        }
22    }
23
24    fn base_url(&self) -> String {
25        format!("http://{}:{}", self.host, self.port)
26    }
27}
28
29/// Device registration counts returned from a sync operation.
30#[derive(Debug, Clone, Copy)]
31pub struct DeviceRegistrationCounts {
32    pub input_units: usize,
33    pub output_units: usize,
34    pub feedbacks: usize,
35}
36
37/// Registrar helper for syncing device registrations and accessing topology.
38#[derive(Debug, Clone)]
39pub struct AgentRegistrar {
40    config: FeagiApiConfig,
41    #[cfg(feature = "sdk-io")]
42    client: reqwest::Client,
43}
44
45impl AgentRegistrar {
46    /// Create a new registrar with FEAGI API configuration.
47    pub fn new(config: FeagiApiConfig) -> Result<Self, SdkError> {
48        #[cfg(feature = "sdk-io")]
49        let client = reqwest::Client::builder()
50            .timeout(config.timeout)
51            .build()
52            .map_err(|e| SdkError::Other(format!("Registrar HTTP client init failed: {e}")))?;
53        Ok(Self {
54            config,
55            #[cfg(feature = "sdk-io")]
56            client,
57        })
58    }
59
60    /// Build a topology cache tied to this registrar's API config.
61    pub fn topology_cache(&self) -> Result<TopologyCache, SdkError> {
62        TopologyCache::new(
63            self.config.host.clone(),
64            self.config.port,
65            self.config.timeout.as_secs_f64(),
66        )
67    }
68
69    /// Export registrations from the connector and push them to FEAGI.
70    #[cfg(feature = "sdk-io")]
71    pub async fn sync_device_registrations(
72        &self,
73        device_registrations: serde_json::Value,
74        agent_id: &str,
75    ) -> Result<DeviceRegistrationCounts, SdkError> {
76        let counts = count_device_registrations(&device_registrations)?;
77
78        let url = format!(
79            "{}/v1/agent/{}/device_registrations",
80            self.config.base_url(),
81            agent_id
82        );
83
84        let response = self
85            .client
86            .post(url)
87            .json(&serde_json::json!({
88                "device_registrations": device_registrations
89            }))
90            .send()
91            .await
92            .map_err(|e| SdkError::Other(format!("Device registration sync failed: {e}")))?;
93
94        response
95            .error_for_status()
96            .map_err(|e| SdkError::Other(format!("Device registration sync error: {e}")))?;
97
98        Ok(counts)
99    }
100}
101
102fn count_device_registrations(
103    device_registrations: &serde_json::Value,
104) -> Result<DeviceRegistrationCounts, SdkError> {
105    let input_units = device_registrations
106        .get("input_units_and_encoder_properties")
107        .and_then(|v| v.as_object())
108        .map(|m| m.len())
109        .ok_or_else(|| SdkError::Other("Device registrations missing input units".to_string()))?;
110    let output_units = device_registrations
111        .get("output_units_and_decoder_properties")
112        .and_then(|v| v.as_object())
113        .map(|m| m.len())
114        .ok_or_else(|| SdkError::Other("Device registrations missing output units".to_string()))?;
115    let feedbacks_value = device_registrations
116        .get("feedbacks")
117        .ok_or_else(|| SdkError::Other("Device registrations missing feedbacks".to_string()))?;
118    let feedbacks = if let Some(list) = feedbacks_value.as_array() {
119        list.len()
120    } else if let Some(obj) = feedbacks_value.as_object() {
121        obj.get("registered_feedbacks")
122            .and_then(|v| v.as_array())
123            .map(|v| v.len())
124            .ok_or_else(|| {
125                SdkError::Other(
126                    "Device registrations feedbacks missing registered_feedbacks".to_string(),
127                )
128            })?
129    } else {
130        return Err(SdkError::Other(
131            "Device registrations feedbacks must be an array or object".to_string(),
132        ));
133    };
134
135    Ok(DeviceRegistrationCounts {
136        input_units,
137        output_units,
138        feedbacks,
139    })
140}