use serde::{Deserialize, Serialize};
use crate::registry_spec::{
A2aServerSpec, AgentSpec, McpServerSpec, ModelPoolSpec, ModelSpec, ProviderSpec,
};
use crate::skill_spec::SkillSpec;
use crate::tool_spec::ToolSpec;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum BuiltinSpec {
Agent(Box<AgentSpec>),
Provider(ProviderSpec),
Model(ModelSpec),
ModelPool(ModelPoolSpec),
A2aServer(A2aServerSpec),
McpServer(McpServerSpec),
Tool(ToolSpec),
Skill(SkillSpec),
}
impl BuiltinSpec {
pub fn agent(spec: AgentSpec) -> Self {
Self::Agent(Box::new(spec))
}
pub fn provider(spec: ProviderSpec) -> Self {
Self::Provider(spec)
}
pub fn model(spec: ModelSpec) -> Self {
Self::Model(spec)
}
pub fn model_pool(spec: ModelPoolSpec) -> Self {
Self::ModelPool(spec)
}
pub fn a2a_server(spec: A2aServerSpec) -> Self {
Self::A2aServer(spec)
}
pub fn mcp_server(spec: McpServerSpec) -> Self {
Self::McpServer(spec)
}
pub fn tool(spec: ToolSpec) -> Self {
Self::Tool(spec)
}
pub fn skill(spec: SkillSpec) -> Self {
Self::Skill(spec)
}
pub fn namespace(&self) -> &'static str {
match self {
Self::Agent(_) => "agents",
Self::Provider(_) => "providers",
Self::Model(_) => "models",
Self::ModelPool(_) => "model-pools",
Self::A2aServer(_) => "a2a-servers",
Self::McpServer(_) => "mcp-servers",
Self::Tool(_) => "tools",
Self::Skill(_) => "skills",
}
}
pub fn id(&self) -> &str {
match self {
Self::Agent(s) => &s.id,
Self::Provider(s) => &s.id,
Self::Model(s) => &s.id,
Self::ModelPool(s) => &s.id,
Self::A2aServer(s) => &s.id,
Self::McpServer(s) => &s.id,
Self::Tool(s) => &s.id,
Self::Skill(s) => &s.id,
}
}
}
#[derive(Debug, Clone)]
pub struct BuiltinSeedSet {
pub binary_version: String,
pub specs: Vec<BuiltinSpec>,
}
impl BuiltinSeedSet {
pub fn empty(binary_version: impl Into<String>) -> Self {
Self {
binary_version: binary_version.into(),
specs: Vec::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constructor_agent_wraps_in_box() {
let spec = AgentSpec {
id: "a1".to_owned(),
model_id: "gpt-4o".to_owned(),
system_prompt: "hi".to_owned(),
..Default::default()
};
assert!(matches!(BuiltinSpec::agent(spec), BuiltinSpec::Agent(_)));
}
#[test]
fn constructor_provider_wraps() {
let spec = ProviderSpec {
id: "p1".to_owned(),
adapter: "openai".to_owned(),
..Default::default()
};
assert!(matches!(
BuiltinSpec::provider(spec),
BuiltinSpec::Provider(_)
));
}
#[test]
fn constructor_model_wraps() {
let spec = ModelSpec::new("m1", "openai", "gpt-4o");
assert!(matches!(BuiltinSpec::model(spec), BuiltinSpec::Model(_)));
}
#[test]
fn constructor_model_pool_wraps() {
let spec = ModelPoolSpec::new("pool1", ["m1"]);
assert!(matches!(
BuiltinSpec::model_pool(spec),
BuiltinSpec::ModelPool(_)
));
}
#[test]
fn constructor_mcp_server_wraps() {
let spec = McpServerSpec {
id: "mcp1".to_owned(),
..Default::default()
};
assert!(matches!(
BuiltinSpec::mcp_server(spec),
BuiltinSpec::McpServer(_)
));
}
#[test]
fn constructor_tool_wraps() {
let spec = crate::tool_spec::ToolSpec {
id: "t1".into(),
name: "Tool 1".into(),
description: "x".into(),
..Default::default()
};
assert!(matches!(BuiltinSpec::tool(spec), BuiltinSpec::Tool(_)));
}
#[test]
fn tool_namespace_and_id() {
let spec = crate::tool_spec::ToolSpec {
id: "t1".into(),
name: "x".into(),
description: "x".into(),
..Default::default()
};
let bi = BuiltinSpec::tool(spec);
assert_eq!(bi.namespace(), "tools");
assert_eq!(bi.id(), "t1");
}
#[test]
fn constructor_skill_wraps() {
let spec = crate::skill_spec::SkillSpec {
id: "s1".into(),
name: "Skill 1".into(),
description: "x".into(),
instructions_md: "Use the skill.".into(),
..Default::default()
};
let bi = BuiltinSpec::skill(spec);
assert_eq!(bi.namespace(), "skills");
assert_eq!(bi.id(), "s1");
}
}