tirea_agentos/composition/
agent_definition.rs1use super::stop_condition::StopConditionSpec;
2use super::AgentDescriptor;
3use crate::runtime::loop_runner::LlmRetryPolicy;
4use genai::chat::ChatOptions;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ToolExecutionMode {
9 Sequential,
10 ParallelBatchApproval,
11 ParallelStreaming,
12}
13
14#[derive(Clone)]
20pub struct AgentDefinition {
21 pub id: String,
23 #[allow(dead_code)]
25 pub name: Option<String>,
26 #[allow(dead_code)]
28 pub description: Option<String>,
29 pub model: String,
31 pub system_prompt: String,
33 pub max_rounds: usize,
35 pub tool_execution_mode: ToolExecutionMode,
37 pub chat_options: Option<ChatOptions>,
39 pub fallback_models: Vec<String>,
43 pub llm_retry_policy: LlmRetryPolicy,
45 pub behavior_ids: Vec<String>,
47 pub allowed_tools: Option<Vec<String>>,
49 pub excluded_tools: Option<Vec<String>>,
51 pub allowed_skills: Option<Vec<String>>,
53 pub excluded_skills: Option<Vec<String>>,
55 pub allowed_agents: Option<Vec<String>>,
57 pub excluded_agents: Option<Vec<String>>,
59 pub permission_rules: Vec<(String, String)>,
62 pub stop_condition_specs: Vec<StopConditionSpec>,
64 pub stop_condition_ids: Vec<String>,
66}
67
68impl Default for AgentDefinition {
69 fn default() -> Self {
70 Self {
71 id: "default".to_string(),
72 name: None,
73 description: None,
74 model: "gpt-4o-mini".to_string(),
75 system_prompt: String::new(),
76 max_rounds: 10,
77 tool_execution_mode: ToolExecutionMode::ParallelStreaming,
78 chat_options: Some(
79 ChatOptions::default()
80 .with_capture_usage(true)
81 .with_capture_reasoning_content(true)
82 .with_capture_tool_calls(true),
83 ),
84 fallback_models: Vec::new(),
85 llm_retry_policy: LlmRetryPolicy::default(),
86 behavior_ids: Vec::new(),
87 allowed_tools: None,
88 excluded_tools: None,
89 allowed_skills: None,
90 excluded_skills: None,
91 allowed_agents: None,
92 excluded_agents: None,
93 permission_rules: Vec::new(),
94 stop_condition_specs: Vec::new(),
95 stop_condition_ids: Vec::new(),
96 }
97 }
98}
99
100impl std::fmt::Debug for AgentDefinition {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 f.debug_struct("AgentDefinition")
103 .field("id", &self.id)
104 .field("name", &self.name)
105 .field("description", &self.description)
106 .field("model", &self.model)
107 .field(
108 "system_prompt",
109 &format!("[{} chars]", self.system_prompt.len()),
110 )
111 .field("max_rounds", &self.max_rounds)
112 .field("tool_execution_mode", &self.tool_execution_mode)
113 .field("chat_options", &self.chat_options)
114 .field("fallback_models", &self.fallback_models)
115 .field("llm_retry_policy", &self.llm_retry_policy)
116 .field("behavior_ids", &self.behavior_ids)
117 .field("allowed_tools", &self.allowed_tools)
118 .field("excluded_tools", &self.excluded_tools)
119 .field("allowed_skills", &self.allowed_skills)
120 .field("excluded_skills", &self.excluded_skills)
121 .field("allowed_agents", &self.allowed_agents)
122 .field("excluded_agents", &self.excluded_agents)
123 .field("stop_condition_specs", &self.stop_condition_specs)
124 .field("stop_condition_ids", &self.stop_condition_ids)
125 .finish()
126 }
127}
128
129impl AgentDefinition {
130 tirea_contract::impl_shared_agent_builder_methods!();
131
132 #[must_use]
133 pub fn with_name(mut self, name: impl Into<String>) -> Self {
134 self.name = Some(name.into());
135 self
136 }
137
138 #[must_use]
139 pub fn with_description(mut self, description: impl Into<String>) -> Self {
140 self.description = Some(description.into());
141 self
142 }
143
144 #[must_use]
145 pub fn display_name(&self) -> &str {
146 self.name.as_deref().unwrap_or(&self.id)
147 }
148
149 #[must_use]
150 pub fn display_description(&self) -> &str {
151 self.description.as_deref().unwrap_or("")
152 }
153
154 #[must_use]
155 pub fn descriptor(&self) -> AgentDescriptor {
156 AgentDescriptor::new(self.id.clone())
157 .with_name(self.display_name())
158 .with_description(self.display_description())
159 }
160
161 #[must_use]
162 pub fn with_stop_condition_spec(mut self, spec: StopConditionSpec) -> Self {
163 self.stop_condition_specs.push(spec);
164 self
165 }
166
167 #[must_use]
168 pub fn with_stop_condition_specs(mut self, specs: Vec<StopConditionSpec>) -> Self {
169 self.stop_condition_specs = specs;
170 self
171 }
172
173 #[must_use]
174 pub fn with_tool_execution_mode(mut self, mode: ToolExecutionMode) -> Self {
175 self.tool_execution_mode = mode;
176 self
177 }
178
179 #[must_use]
180 pub fn with_behavior_ids(mut self, behavior_ids: Vec<String>) -> Self {
181 self.behavior_ids = behavior_ids;
182 self
183 }
184
185 #[must_use]
186 pub fn with_behavior_id(mut self, behavior_id: impl Into<String>) -> Self {
187 self.behavior_ids.push(behavior_id.into());
188 self
189 }
190
191 #[must_use]
192 pub fn with_stop_condition_id(mut self, id: impl Into<String>) -> Self {
193 self.stop_condition_ids.push(id.into());
194 self
195 }
196
197 #[must_use]
198 pub fn with_stop_condition_ids(mut self, ids: Vec<String>) -> Self {
199 self.stop_condition_ids = ids;
200 self
201 }
202
203 #[must_use]
204 pub fn with_allowed_tools(mut self, tools: Vec<String>) -> Self {
205 self.allowed_tools = Some(tools);
206 self
207 }
208
209 #[must_use]
210 pub fn with_excluded_tools(mut self, tools: Vec<String>) -> Self {
211 self.excluded_tools = Some(tools);
212 self
213 }
214
215 #[must_use]
216 pub fn with_allowed_skills(mut self, skills: Vec<String>) -> Self {
217 self.allowed_skills = Some(skills);
218 self
219 }
220
221 #[must_use]
222 pub fn with_excluded_skills(mut self, skills: Vec<String>) -> Self {
223 self.excluded_skills = Some(skills);
224 self
225 }
226
227 #[must_use]
228 pub fn with_allowed_agents(mut self, agents: Vec<String>) -> Self {
229 self.allowed_agents = Some(agents);
230 self
231 }
232
233 #[must_use]
234 pub fn with_excluded_agents(mut self, agents: Vec<String>) -> Self {
235 self.excluded_agents = Some(agents);
236 self
237 }
238
239 pub fn with_permission_rule(
247 mut self,
248 behavior: impl Into<String>,
249 pattern: impl Into<String>,
250 ) -> Self {
251 self.permission_rules
252 .push((behavior.into(), pattern.into()));
253 self
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260 use crate::runtime::wiring::resolve::normalize_definition_models_for_test;
261
262 #[test]
263 fn normalize_definition_trims_model_and_fallback_models() {
264 let definition =
265 AgentDefinition::new(" openai::gemini-2.5-flash ").with_fallback_models(vec![
266 " gpt-4o-mini ".to_string(),
267 " ".to_string(),
268 " claude-3-7-sonnet ".to_string(),
269 ]);
270 let normalized = normalize_definition_models_for_test(definition);
271
272 assert_eq!(normalized.model, "openai::gemini-2.5-flash");
273 assert_eq!(
274 normalized.fallback_models,
275 vec!["gpt-4o-mini".to_string(), "claude-3-7-sonnet".to_string()]
276 );
277 }
278}