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