systemprompt-agent 0.2.0

Core Agent protocol module for systemprompt.io
Documentation
use anyhow::{Result, anyhow};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use systemprompt_database::JsonRow;
use systemprompt_identifiers::{AgentId, CategoryId, SourceId};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Agent {
    #[serde(rename = "agent_id")]
    pub id: AgentId,
    pub name: String,
    pub display_name: String,
    pub description: String,
    pub version: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub system_prompt: Option<String>,
    pub enabled: bool,
    pub port: i32,
    pub endpoint: String,
    pub dev_only: bool,
    pub is_primary: bool,
    pub is_default: bool,
    pub tags: Vec<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub category_id: Option<CategoryId>,
    pub source_id: SourceId,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub provider: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
    pub mcp_servers: Vec<String>,
    pub skills: Vec<String>,
    pub card_json: serde_json::Value,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

impl Agent {
    pub fn from_json_row(row: &JsonRow) -> Result<Self> {
        let id = AgentId::new(
            row.get("agent_id")
                .and_then(|v| v.as_str())
                .ok_or_else(|| anyhow!("Missing agent_id"))?,
        );

        let name = row
            .get("name")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow!("Missing name"))?
            .to_string();

        let display_name = row
            .get("display_name")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow!("Missing display_name"))?
            .to_string();

        let description = row
            .get("description")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow!("Missing description"))?
            .to_string();

        let version = row
            .get("version")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow!("Missing version"))?
            .to_string();

        let system_prompt = row
            .get("system_prompt")
            .and_then(|v| v.as_str())
            .map(String::from);

        let enabled = row
            .get("enabled")
            .and_then(serde_json::Value::as_bool)
            .ok_or_else(|| anyhow!("Missing enabled"))?;

        let port = row
            .get("port")
            .and_then(serde_json::Value::as_i64)
            .ok_or_else(|| anyhow!("Missing port"))? as i32;

        let endpoint = row
            .get("endpoint")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow!("Missing endpoint"))?
            .to_string();

        let dev_only = row
            .get("dev_only")
            .and_then(serde_json::Value::as_bool)
            .unwrap_or(false);

        let is_primary = row
            .get("is_primary")
            .and_then(serde_json::Value::as_bool)
            .unwrap_or(false);

        let is_default = row
            .get("is_default")
            .and_then(serde_json::Value::as_bool)
            .unwrap_or(false);

        let tags = row
            .get("tags")
            .and_then(|v| v.as_array())
            .map_or_else(Vec::new, |arr| {
                arr.iter()
                    .filter_map(|v| v.as_str().map(String::from))
                    .collect()
            });

        let category_id = row
            .get("category_id")
            .and_then(|v| v.as_str())
            .map(CategoryId::new);

        let source_id = SourceId::new(
            row.get("source_id")
                .and_then(|v| v.as_str())
                .ok_or_else(|| anyhow!("Missing source_id"))?,
        );

        let provider = row
            .get("provider")
            .and_then(|v| v.as_str())
            .map(String::from);

        let model = row.get("model").and_then(|v| v.as_str()).map(String::from);

        let mcp_servers = row
            .get("mcp_servers")
            .and_then(|v| v.as_array())
            .map_or_else(Vec::new, |arr| {
                arr.iter()
                    .filter_map(|v| v.as_str().map(String::from))
                    .collect()
            });

        let skills = row
            .get("skills")
            .and_then(|v| v.as_array())
            .map_or_else(Vec::new, |arr| {
                arr.iter()
                    .filter_map(|v| v.as_str().map(String::from))
                    .collect()
            });

        let card_json = row
            .get("card_json")
            .cloned()
            .ok_or_else(|| anyhow!("Missing card_json"))?;

        let created_at = row
            .get("created_at")
            .and_then(systemprompt_database::parse_database_datetime)
            .ok_or_else(|| anyhow!("Missing or invalid created_at"))?;

        let updated_at = row
            .get("updated_at")
            .and_then(systemprompt_database::parse_database_datetime)
            .ok_or_else(|| anyhow!("Missing or invalid updated_at"))?;

        Ok(Self {
            id,
            name,
            display_name,
            description,
            version,
            system_prompt,
            enabled,
            port,
            endpoint,
            dev_only,
            is_primary,
            is_default,
            tags,
            category_id,
            source_id,
            provider,
            model,
            mcp_servers,
            skills,
            card_json,
            created_at,
            updated_at,
        })
    }
}