Skip to main content

distri_types/configuration/
package.rs

1use crate::ToolDefinition;
2use crate::agent::StandardDefinition;
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use utoipa::ToSchema;
6
7/// Cloud-specific metadata for agents (optional, only present in cloud responses)
8#[derive(Debug, Clone, Serialize, Deserialize, Default, ToSchema, JsonSchema)]
9pub struct AgentCloudMetadata {
10    #[serde(default, skip_serializing_if = "Option::is_none")]
11    pub published: Option<bool>,
12    #[serde(default, skip_serializing_if = "Option::is_none")]
13    pub published_at: Option<chrono::DateTime<chrono::Utc>>,
14    #[serde(default, skip_serializing_if = "Option::is_none")]
15    pub is_owner: Option<bool>,
16    #[serde(default, skip_serializing_if = "Option::is_none")]
17    pub is_system: Option<bool>,
18    /// True when the agent belongs to the current workspace (not from another workspace via publish)
19    #[serde(default, skip_serializing_if = "Option::is_none")]
20    pub is_workspace: Option<bool>,
21    /// Workspace slug the agent belongs to (for display on cross-workspace agents)
22    #[serde(default, skip_serializing_if = "Option::is_none")]
23    pub workspace_slug: Option<String>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
27pub struct AgentConfigWithTools {
28    #[serde(flatten)]
29    #[schema(value_type = Object)]
30    pub agent: AgentConfig,
31    #[serde(default, skip_serializing_if = "Vec::is_empty")]
32    pub resolved_tools: Vec<ToolDefinition>,
33    #[serde(default, skip_serializing_if = "Option::is_none")]
34    pub markdown: Option<String>,
35    /// Cloud-specific metadata (optional, only present in cloud responses)
36    #[serde(flatten, default)]
37    pub cloud: AgentCloudMetadata,
38}
39
40/// Unified agent configuration enum
41#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
42#[serde(tag = "agent_type", rename_all = "snake_case")]
43#[allow(clippy::large_enum_variant)]
44pub enum AgentConfig {
45    /// Standard markdown-based agent
46    #[schema(value_type = Object)]
47    StandardAgent(StandardDefinition),
48    /// Workflow-based agent — executes a workflow DAG instead of an LLM loop
49    WorkflowAgent(WorkflowAgentDefinition),
50}
51
52/// How a workflow agent is triggered.
53#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
54#[serde(tag = "type", rename_all = "snake_case")]
55pub enum Trigger {
56    /// Manual invocation (implicit default when triggers is empty)
57    OnCall {},
58    /// Cron-based scheduled execution
59    Schedule {
60        /// Cron expression, e.g. "0 * * * *" (every hour)
61        cron: String,
62        /// IANA timezone, e.g. "America/Los_Angeles". Defaults to UTC.
63        #[serde(default, skip_serializing_if = "Option::is_none")]
64        timezone: Option<String>,
65        /// Whether this schedule is active. Defaults to true.
66        #[serde(default = "default_true")]
67        enabled: bool,
68        /// Default input passed to the workflow on each scheduled run.
69        #[serde(default, skip_serializing_if = "Option::is_none")]
70        input: Option<serde_json::Value>,
71    },
72}
73
74fn default_true() -> bool {
75    true
76}
77
78/// Definition for a workflow-based agent.
79/// The workflow definition is stored as JSON to avoid crate dependency on distri-workflow.
80/// Deserialize to `distri_workflow::WorkflowDefinition` at execution time.
81#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
82pub struct WorkflowAgentDefinition {
83    pub name: String,
84    pub description: String,
85    #[serde(default = "default_version")]
86    pub version: String,
87    /// The workflow definition as JSON.
88    pub definition: serde_json::Value,
89    /// JSON Schema for required inputs (validated before execution).
90    #[serde(default, skip_serializing_if = "Option::is_none")]
91    pub input_schema: Option<serde_json::Value>,
92    /// How this workflow is triggered. Defaults to on_call if empty.
93    #[serde(default, skip_serializing_if = "Vec::is_empty")]
94    pub triggers: Vec<Trigger>,
95}
96
97fn default_version() -> String {
98    "0.1.0".to_string()
99}
100
101impl AgentConfig {
102    /// Get the name of the agent
103    pub fn get_name(&self) -> &str {
104        match self {
105            AgentConfig::StandardAgent(def) => &def.name,
106            AgentConfig::WorkflowAgent(def) => &def.name,
107        }
108    }
109
110    pub fn get_definition(&self) -> &StandardDefinition {
111        match self {
112            AgentConfig::StandardAgent(def) => def,
113            AgentConfig::WorkflowAgent(_) => {
114                panic!("WorkflowAgent does not have a StandardDefinition")
115            }
116        }
117    }
118
119    /// Get the description of the agent
120    pub fn get_description(&self) -> &str {
121        match self {
122            AgentConfig::StandardAgent(def) => &def.description,
123            AgentConfig::WorkflowAgent(def) => &def.description,
124        }
125    }
126
127    /// Get the tools configuration, if this is a standard agent.
128    pub fn get_tools_config(&self) -> Option<&crate::ToolsConfig> {
129        match self {
130            AgentConfig::StandardAgent(def) => def.tools.as_ref(),
131            AgentConfig::WorkflowAgent(_) => None,
132        }
133    }
134
135    /// Get schedule triggers for this agent (only workflow agents can have them).
136    pub fn get_schedule_triggers(&self) -> Vec<&Trigger> {
137        match self {
138            AgentConfig::StandardAgent(_) => vec![],
139            AgentConfig::WorkflowAgent(def) => def
140                .triggers
141                .iter()
142                .filter(|t| matches!(t, Trigger::Schedule { .. }))
143                .collect(),
144        }
145    }
146
147    /// Validate the configuration
148    pub fn validate(&self) -> anyhow::Result<()> {
149        match self {
150            AgentConfig::StandardAgent(def) => def.validate(),
151            AgentConfig::WorkflowAgent(_def) => Ok(()), // Workflow validation happens at execution
152        }
153    }
154}