use crate::{McpSourceSpec, PromptSource};
use aether_core::agent_spec::ToolFilter;
use llm::{ModelSettings, ProviderConnectionOverrides, ReasoningEffort};
#[doc = include_str!("docs/agent_config.md")]
#[derive(Debug, Clone, Default, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[schemars(transform = require_agent_invocation_surface_schema)]
pub struct AgentConfig {
#[schemars(length(min = 1))]
pub name: String,
#[schemars(length(min = 1))]
pub description: String,
#[schemars(length(min = 1))]
pub model: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reasoning_effort: Option<ReasoningEffort>,
#[serde(default, skip_serializing_if = "ModelSettings::is_empty")]
pub model_settings: ModelSettings,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1))]
pub context_window: Option<u32>,
#[serde(default)]
pub user_invocable: bool,
#[serde(default)]
pub agent_invocable: bool,
#[serde(default, skip_serializing_if = "ProviderConnectionOverrides::is_empty")]
pub providers: ProviderConnectionOverrides,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[schemars(length(min = 1))]
pub prompts: Vec<PromptSource>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub mcps: Vec<McpSourceSpec>,
#[serde(default, skip_serializing_if = "ToolFilter::is_empty")]
pub tools: ToolFilter,
}
fn require_agent_invocation_surface_schema(schema: &mut schemars::Schema) {
let Some(mut base_schema) = schema.as_object().cloned() else {
return;
};
let description = base_schema.remove("description");
let invocation_surface_schema = serde_json::json!({
"anyOf": [
{
"type": "object",
"additionalProperties": false,
"required": ["userInvocable"],
"properties": { "userInvocable": { "const": true } }
},
{
"type": "object",
"additionalProperties": false,
"required": ["agentInvocable"],
"properties": { "agentInvocable": { "const": true } }
}
]
});
let mut composed_schema = serde_json::Map::new();
if let Some(description) = description {
composed_schema.insert("description".to_string(), description);
}
composed_schema.insert("allOf".to_string(), serde_json::json!([base_schema, invocation_surface_schema]));
*schema = composed_schema.into();
}