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 stop_condition_specs: Vec<StopConditionSpec>,
61 pub stop_condition_ids: Vec<String>,
63}
64
65impl Default for AgentDefinition {
66 fn default() -> Self {
67 Self {
68 id: "default".to_string(),
69 name: None,
70 description: None,
71 model: "gpt-4o-mini".to_string(),
72 system_prompt: String::new(),
73 max_rounds: 10,
74 tool_execution_mode: ToolExecutionMode::ParallelStreaming,
75 chat_options: Some(
76 ChatOptions::default()
77 .with_capture_usage(true)
78 .with_capture_reasoning_content(true)
79 .with_capture_tool_calls(true),
80 ),
81 fallback_models: Vec::new(),
82 llm_retry_policy: LlmRetryPolicy::default(),
83 behavior_ids: Vec::new(),
84 allowed_tools: None,
85 excluded_tools: None,
86 allowed_skills: None,
87 excluded_skills: None,
88 allowed_agents: None,
89 excluded_agents: None,
90 stop_condition_specs: Vec::new(),
91 stop_condition_ids: Vec::new(),
92 }
93 }
94}
95
96impl std::fmt::Debug for AgentDefinition {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 f.debug_struct("AgentDefinition")
99 .field("id", &self.id)
100 .field("name", &self.name)
101 .field("description", &self.description)
102 .field("model", &self.model)
103 .field(
104 "system_prompt",
105 &format!("[{} chars]", self.system_prompt.len()),
106 )
107 .field("max_rounds", &self.max_rounds)
108 .field("tool_execution_mode", &self.tool_execution_mode)
109 .field("chat_options", &self.chat_options)
110 .field("fallback_models", &self.fallback_models)
111 .field("llm_retry_policy", &self.llm_retry_policy)
112 .field("behavior_ids", &self.behavior_ids)
113 .field("allowed_tools", &self.allowed_tools)
114 .field("excluded_tools", &self.excluded_tools)
115 .field("allowed_skills", &self.allowed_skills)
116 .field("excluded_skills", &self.excluded_skills)
117 .field("allowed_agents", &self.allowed_agents)
118 .field("excluded_agents", &self.excluded_agents)
119 .field("stop_condition_specs", &self.stop_condition_specs)
120 .field("stop_condition_ids", &self.stop_condition_ids)
121 .finish()
122 }
123}
124
125impl AgentDefinition {
126 tirea_contract::impl_shared_agent_builder_methods!();
127
128 #[must_use]
129 pub fn with_name(mut self, name: impl Into<String>) -> Self {
130 self.name = Some(name.into());
131 self
132 }
133
134 #[must_use]
135 pub fn with_description(mut self, description: impl Into<String>) -> Self {
136 self.description = Some(description.into());
137 self
138 }
139
140 #[must_use]
141 pub fn display_name(&self) -> &str {
142 self.name.as_deref().unwrap_or(&self.id)
143 }
144
145 #[must_use]
146 pub fn display_description(&self) -> &str {
147 self.description.as_deref().unwrap_or("")
148 }
149
150 #[must_use]
151 pub fn descriptor(&self) -> AgentDescriptor {
152 AgentDescriptor::new(self.id.clone())
153 .with_name(self.display_name())
154 .with_description(self.display_description())
155 }
156
157 #[must_use]
158 pub fn with_stop_condition_spec(mut self, spec: StopConditionSpec) -> Self {
159 self.stop_condition_specs.push(spec);
160 self
161 }
162
163 #[must_use]
164 pub fn with_stop_condition_specs(mut self, specs: Vec<StopConditionSpec>) -> Self {
165 self.stop_condition_specs = specs;
166 self
167 }
168
169 #[must_use]
170 pub fn with_tool_execution_mode(mut self, mode: ToolExecutionMode) -> Self {
171 self.tool_execution_mode = mode;
172 self
173 }
174
175 #[must_use]
176 pub fn with_behavior_ids(mut self, behavior_ids: Vec<String>) -> Self {
177 self.behavior_ids = behavior_ids;
178 self
179 }
180
181 #[must_use]
182 pub fn with_behavior_id(mut self, behavior_id: impl Into<String>) -> Self {
183 self.behavior_ids.push(behavior_id.into());
184 self
185 }
186
187 #[must_use]
188 pub fn with_stop_condition_id(mut self, id: impl Into<String>) -> Self {
189 self.stop_condition_ids.push(id.into());
190 self
191 }
192
193 #[must_use]
194 pub fn with_stop_condition_ids(mut self, ids: Vec<String>) -> Self {
195 self.stop_condition_ids = ids;
196 self
197 }
198
199 #[must_use]
200 pub fn with_allowed_tools(mut self, tools: Vec<String>) -> Self {
201 self.allowed_tools = Some(tools);
202 self
203 }
204
205 #[must_use]
206 pub fn with_excluded_tools(mut self, tools: Vec<String>) -> Self {
207 self.excluded_tools = Some(tools);
208 self
209 }
210
211 #[must_use]
212 pub fn with_allowed_skills(mut self, skills: Vec<String>) -> Self {
213 self.allowed_skills = Some(skills);
214 self
215 }
216
217 #[must_use]
218 pub fn with_excluded_skills(mut self, skills: Vec<String>) -> Self {
219 self.excluded_skills = Some(skills);
220 self
221 }
222
223 #[must_use]
224 pub fn with_allowed_agents(mut self, agents: Vec<String>) -> Self {
225 self.allowed_agents = Some(agents);
226 self
227 }
228
229 #[must_use]
230 pub fn with_excluded_agents(mut self, agents: Vec<String>) -> Self {
231 self.excluded_agents = Some(agents);
232 self
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use crate::runtime::resolve::normalize_definition_models_for_test;
240
241 #[test]
242 fn normalize_definition_trims_model_and_fallback_models() {
243 let definition =
244 AgentDefinition::new(" openai::gemini-2.5-flash ").with_fallback_models(vec![
245 " gpt-4o-mini ".to_string(),
246 " ".to_string(),
247 " claude-3-7-sonnet ".to_string(),
248 ]);
249 let normalized = normalize_definition_models_for_test(definition);
250
251 assert_eq!(normalized.model, "openai::gemini-2.5-flash");
252 assert_eq!(
253 normalized.fallback_models,
254 vec!["gpt-4o-mini".to_string(), "claude-3-7-sonnet".to_string()]
255 );
256 }
257}