systemprompt_models/services/agent_config/
mod.rs1mod card;
5mod disk;
6mod summary;
7
8pub use card::{
9 AgentCardConfig, AgentMetadataConfig, AgentProviderInfo, AgentSkillConfig, CapabilitiesConfig,
10 OAuthConfig,
11};
12pub use disk::DiskAgentConfig;
13pub use summary::AgentSummary;
14
15use crate::auth::Permission;
16use crate::errors::ConfigValidationError;
17use serde::{Deserialize, Serialize};
18
19pub const AGENT_CONFIG_FILENAME: &str = "config.yaml";
20pub const DEFAULT_AGENT_SYSTEM_PROMPT_FILE: &str = "system_prompt.md";
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct AgentConfig {
24 pub name: String,
25 pub port: u16,
26 pub endpoint: String,
27 pub enabled: bool,
28 #[serde(default)]
29 pub dev_only: bool,
30 #[serde(default)]
31 pub is_primary: bool,
32 #[serde(default)]
33 pub default: bool,
34 #[serde(default)]
35 pub tags: Vec<String>,
36 pub card: AgentCardConfig,
37 pub metadata: AgentMetadataConfig,
38 #[serde(default)]
39 pub oauth: OAuthConfig,
40}
41
42impl AgentConfig {
43 pub fn validate(&self, name: &str) -> Result<(), ConfigValidationError> {
44 if self.name != name {
45 return Err(ConfigValidationError::invalid_field(format!(
46 "Agent config key '{}' does not match name field '{}'",
47 name, self.name
48 )));
49 }
50
51 if !self
52 .name
53 .chars()
54 .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')
55 {
56 return Err(ConfigValidationError::invalid_field(format!(
57 "Agent name '{}' must be lowercase alphanumeric with underscores only",
58 self.name
59 )));
60 }
61
62 if self.name.len() < 3 || self.name.len() > 50 {
63 return Err(ConfigValidationError::invalid_field(format!(
64 "Agent name '{}' must be between 3 and 50 characters",
65 self.name
66 )));
67 }
68
69 if self.port == 0 {
70 return Err(ConfigValidationError::invalid_field(format!(
71 "Agent '{}' has invalid port {}",
72 self.name, self.port
73 )));
74 }
75
76 Ok(())
77 }
78
79 pub fn extract_oauth_scopes_from_card(&mut self) {
80 if let Some(security_vec) = &self.card.security {
81 for security_obj in security_vec {
82 if let Some(oauth2_scopes) = security_obj.get("oauth2").and_then(|v| v.as_array()) {
83 let mut permissions = Vec::new();
84 for scope_val in oauth2_scopes {
85 if let Some(scope_str) = scope_val.as_str() {
86 match scope_str {
87 "admin" => permissions.push(Permission::Admin),
88 "user" => permissions.push(Permission::User),
89 "service" => permissions.push(Permission::Service),
90 "a2a" => permissions.push(Permission::A2a),
91 "mcp" => permissions.push(Permission::Mcp),
92 "anonymous" => permissions.push(Permission::Anonymous),
93 _ => {},
94 }
95 }
96 }
97 if !permissions.is_empty() {
98 self.oauth.scopes = permissions;
99 self.oauth.required = true;
100 }
101 }
102 }
103 }
104 }
105
106 #[must_use]
107 pub fn construct_url(&self, base_url: &str) -> String {
108 format!(
109 "{}/api/v1/agents/{}",
110 base_url.trim_end_matches('/'),
111 self.name
112 )
113 }
114}