aether_project/
agent_config.rs1use crate::{McpSourceSpec, PromptSource};
2use aether_core::agent_spec::ToolFilter;
3use llm::{ModelSettings, ProviderConnectionOverrides, ReasoningEffort};
4
5#[doc = include_str!("docs/agent_config.md")]
6#[derive(Debug, Clone, Default, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
7#[serde(rename_all = "camelCase", deny_unknown_fields)]
8#[schemars(transform = require_agent_invocation_surface_schema)]
9pub struct AgentConfig {
10 #[schemars(length(min = 1))]
12 pub name: String,
13 #[schemars(length(min = 1))]
15 pub description: String,
16 #[schemars(length(min = 1))]
19 pub model: String,
20 #[serde(default, skip_serializing_if = "Option::is_none")]
22 pub reasoning_effort: Option<ReasoningEffort>,
23 #[serde(default, skip_serializing_if = "ModelSettings::is_empty")]
26 pub model_settings: ModelSettings,
27 #[serde(default, skip_serializing_if = "Option::is_none")]
30 #[schemars(range(min = 1))]
31 pub context_window: Option<u32>,
32 #[serde(default)]
34 pub user_invocable: bool,
35 #[serde(default)]
37 pub agent_invocable: bool,
38 #[serde(default, skip_serializing_if = "ProviderConnectionOverrides::is_empty")]
40 pub providers: ProviderConnectionOverrides,
41 #[serde(default, skip_serializing_if = "Vec::is_empty")]
43 #[schemars(length(min = 1))]
44 pub prompts: Vec<PromptSource>,
45 #[serde(default, skip_serializing_if = "Vec::is_empty")]
47 pub mcps: Vec<McpSourceSpec>,
48 #[serde(default, skip_serializing_if = "ToolFilter::is_empty")]
50 pub tools: ToolFilter,
51}
52
53fn require_agent_invocation_surface_schema(schema: &mut schemars::Schema) {
54 let Some(mut base_schema) = schema.as_object().cloned() else {
55 return;
56 };
57 let description = base_schema.remove("description");
58
59 let invocation_surface_schema = serde_json::json!({
60 "anyOf": [
61 {
62 "type": "object",
63 "additionalProperties": false,
64 "required": ["userInvocable"],
65 "properties": { "userInvocable": { "const": true } }
66 },
67 {
68 "type": "object",
69 "additionalProperties": false,
70 "required": ["agentInvocable"],
71 "properties": { "agentInvocable": { "const": true } }
72 }
73 ]
74 });
75
76 let mut composed_schema = serde_json::Map::new();
77 if let Some(description) = description {
78 composed_schema.insert("description".to_string(), description);
79 }
80 composed_schema.insert("allOf".to_string(), serde_json::json!([base_schema, invocation_surface_schema]));
81 *schema = composed_schema.into();
82}