cargo-adk 0.9.1

CLI scaffolding tool for ADK-Rust — generate agent projects from templates
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
//! Template Registry — central data store for all templates, addons, and patterns.
//!
//! The registry is populated at startup with all built-in templates and can be
//! extended with custom templates loaded from a directory.

use std::collections::HashMap;
use std::path::Path;

use crate::addon::{AddonCodeFragments, CapabilityAddon};
use crate::pattern::EnterprisePattern;
use crate::template::{AgentCodeFragments, AgentTemplate, TemplateCategory};

/// Central registry of all available templates, addons, and patterns.
#[derive(Debug, Clone)]
pub struct TemplateRegistry {
    /// All registered agent templates.
    pub agent_templates: Vec<AgentTemplate>,
    /// All registered capability addons.
    pub capability_addons: Vec<CapabilityAddon>,
    /// All registered enterprise patterns.
    pub enterprise_patterns: Vec<EnterprisePattern>,
    /// Alias mappings (e.g., "basic" → "llm").
    pub aliases: HashMap<&'static str, &'static str>,
}

impl TemplateRegistry {
    /// Build the default registry with all built-in templates.
    ///
    /// Populates 8 agent templates, 9 capability addons, 5 enterprise patterns,
    /// and 6 legacy aliases.
    pub fn builtin() -> Self {
        Self {
            agent_templates: builtin_agent_templates(),
            capability_addons: builtin_capability_addons(),
            enterprise_patterns: builtin_enterprise_patterns(),
            aliases: builtin_aliases(),
        }
    }

    /// Load additional templates from a directory.
    pub fn load_custom_dir(&mut self, _dir: &Path) -> Result<(), String> {
        // Will be implemented in a later task
        Ok(())
    }

    /// Resolve a template name (handling aliases).
    pub fn resolve_template(&self, name: &str) -> Option<&AgentTemplate> {
        let resolved_name = self.aliases.get(name).copied().unwrap_or(name);
        self.agent_templates.iter().find(|t| t.name == resolved_name)
    }

    /// Get compatible addons for a given template.
    pub fn compatible_addons(&self, template: &str) -> Vec<&CapabilityAddon> {
        let tmpl = match self.resolve_template(template) {
            Some(t) => t,
            None => return Vec::new(),
        };

        self.capability_addons
            .iter()
            .filter(|addon| {
                // Addon is compatible if:
                // 1. Template doesn't list it as incompatible
                !tmpl.incompatible_addons.contains(&addon.name)
                // 2. Addon doesn't list the template as incompatible
                    && !addon.incompatible_with.contains(&tmpl.name)
            })
            .collect()
    }

    /// Resolve an enterprise pattern into its definition.
    pub fn resolve_pattern(&self, name: &str) -> Option<&EnterprisePattern> {
        self.enterprise_patterns.iter().find(|p| p.name == name)
    }
}

// ---------------------------------------------------------------------------
// Built-in data population
// ---------------------------------------------------------------------------

/// All 8 built-in agent templates.
fn builtin_agent_templates() -> Vec<AgentTemplate> {
    vec![
        AgentTemplate {
            name: "llm",
            description: "Single LLM agent with tool calling support",
            category: TemplateCategory::AgentType,
            default_provider: "gemini",
            required_features: vec!["minimal"],
            incompatible_addons: vec![],
            code_fragments: AgentCodeFragments {
                imports: vec!["use std::sync::Arc;"],
                agent_construction: r#"let agent: Arc<dyn Agent> = Arc::new(
        LlmAgentBuilder::new("{name}")
            .description("An AI assistant")
            .instruction("You are a helpful assistant.")
            .model(Arc::new(model))
            .build()?,
    );"#,
                additional_files: vec![],
            },
        },
        AgentTemplate {
            name: "sequential",
            description: "Sequential multi-agent pipeline executing agents in order",
            category: TemplateCategory::AgentType,
            default_provider: "gemini",
            required_features: vec!["minimal"],
            incompatible_addons: vec![],
            code_fragments: AgentCodeFragments {
                imports: vec!["use std::sync::Arc;", "use adk_rust::agents::SequentialAgent;"],
                agent_construction: r#"let researcher = Arc::new(
        LlmAgentBuilder::new("researcher")
            .description("Research agent")
            .instruction("Research the given topic thoroughly.")
            .model(Arc::new(model.clone()))
            .build()?,
    );

    let writer = Arc::new(
        LlmAgentBuilder::new("writer")
            .description("Writing agent")
            .instruction("Write a clear summary based on the research.")
            .model(Arc::new(model))
            .build()?,
    );

    let agent: Arc<dyn Agent> = Arc::new(
        SequentialAgent::new("{name}", vec![researcher, writer]),
    );"#,
                additional_files: vec![],
            },
        },
        AgentTemplate {
            name: "parallel",
            description: "Parallel multi-agent execution with result aggregation",
            category: TemplateCategory::AgentType,
            default_provider: "gemini",
            required_features: vec!["minimal"],
            incompatible_addons: vec![],
            code_fragments: AgentCodeFragments {
                imports: vec!["use std::sync::Arc;", "use adk_rust::agents::ParallelAgent;"],
                agent_construction: r#"let analyst = Arc::new(
        LlmAgentBuilder::new("analyst")
            .description("Data analyst")
            .instruction("Analyze the data and provide insights.")
            .model(Arc::new(model.clone()))
            .build()?,
    );

    let reviewer = Arc::new(
        LlmAgentBuilder::new("reviewer")
            .description("Quality reviewer")
            .instruction("Review the analysis for accuracy.")
            .model(Arc::new(model))
            .build()?,
    );

    let agent: Arc<dyn Agent> = Arc::new(
        ParallelAgent::new("{name}", vec![analyst, reviewer]),
    );"#,
                additional_files: vec![],
            },
        },
        AgentTemplate {
            name: "loop",
            description: "Loop agent that iterates until a condition is met",
            category: TemplateCategory::AgentType,
            default_provider: "gemini",
            required_features: vec!["minimal"],
            incompatible_addons: vec![],
            code_fragments: AgentCodeFragments {
                imports: vec!["use std::sync::Arc;", "use adk_rust::agents::LoopAgent;"],
                agent_construction: r#"let worker = Arc::new(
        LlmAgentBuilder::new("worker")
            .description("Iterative worker")
            .instruction("Refine the output. When satisfied, respond with DONE.")
            .model(Arc::new(model))
            .build()?,
    );

    let agent: Arc<dyn Agent> = Arc::new(
        LoopAgent::builder()
            .name("{name}")
            .agent(worker)
            .max_iterations(5)
            .build(),
    );"#,
                additional_files: vec![],
            },
        },
        AgentTemplate {
            name: "conditional",
            description: "Conditional agent that routes based on LLM decisions",
            category: TemplateCategory::AgentType,
            default_provider: "gemini",
            required_features: vec!["minimal"],
            incompatible_addons: vec![],
            code_fragments: AgentCodeFragments {
                imports: vec!["use std::sync::Arc;", "use adk_rust::agents::ConditionalAgent;"],
                agent_construction: r#"let technical = Arc::new(
        LlmAgentBuilder::new("technical")
            .description("Technical expert")
            .instruction("Provide detailed technical answers.")
            .model(Arc::new(model.clone()))
            .build()?,
    );

    let general = Arc::new(
        LlmAgentBuilder::new("general")
            .description("General assistant")
            .instruction("Provide helpful general answers.")
            .model(Arc::new(model))
            .build()?,
    );

    let agent: Arc<dyn Agent> = Arc::new(
        ConditionalAgent::new("{name}", vec![technical, general]),
    );"#,
                additional_files: vec![],
            },
        },
        AgentTemplate {
            name: "graph",
            description: "Graph-based workflow with checkpoints and durable execution",
            category: TemplateCategory::AgentType,
            default_provider: "gemini",
            required_features: vec!["minimal", "graph"],
            incompatible_addons: vec![],
            code_fragments: AgentCodeFragments {
                imports: vec!["use std::sync::Arc;", "use adk_rust::graph::*;"],
                agent_construction: r#"// Graph-based workflow with checkpoints
    // See adk-graph documentation for full API
    let agent = LlmAgentBuilder::new("{name}")
        .description("A graph-based workflow agent")
        .instruction("You orchestrate a multi-step workflow.")
        .model(Arc::new(model))
        .build()?;
    let agent: Arc<dyn Agent> = Arc::new(agent);"#,
                additional_files: vec![],
            },
        },
        AgentTemplate {
            name: "realtime",
            description: "Real-time bidirectional audio/video streaming agent",
            category: TemplateCategory::AgentType,
            default_provider: "gemini",
            required_features: vec!["minimal", "realtime"],
            incompatible_addons: vec![],
            code_fragments: AgentCodeFragments {
                imports: vec!["use std::sync::Arc;", "use adk_rust::realtime::*;"],
                agent_construction: r#"// Real-time voice agent with bidirectional audio
    // See adk-realtime documentation for full API
    let agent = LlmAgentBuilder::new("{name}")
        .description("A real-time voice assistant")
        .instruction("You are a voice assistant. Respond naturally and concisely.")
        .model(Arc::new(model))
        .build()?;
    let agent: Arc<dyn Agent> = Arc::new(agent);"#,
                additional_files: vec![],
            },
        },
        AgentTemplate {
            name: "custom",
            description: "Custom agent with manual trait implementation",
            category: TemplateCategory::AgentType,
            default_provider: "gemini",
            required_features: vec!["minimal"],
            incompatible_addons: vec![],
            code_fragments: AgentCodeFragments {
                imports: vec!["use std::sync::Arc;", "use async_trait::async_trait;"],
                agent_construction: r#"// Custom agent implementing the Agent trait directly
    struct MyAgent;

    #[async_trait]
    impl Agent for MyAgent {
        fn name(&self) -> &str { "{name}" }
        fn description(&self) -> &str { "A custom agent" }
        fn sub_agents(&self) -> &[Arc<dyn Agent>] { &[] }
        async fn run(&self, ctx: Arc<dyn InvocationContext>) -> adk_rust::prelude::Result<EventStream> {
            // Your custom logic here
            Ok(Box::pin(futures::stream::empty()))
        }
    }

    let agent: Arc<dyn Agent> = Arc::new(MyAgent);"#,
                additional_files: vec![],
            },
        },
    ]
}

/// All 9 built-in capability addons.
fn builtin_capability_addons() -> Vec<CapabilityAddon> {
    vec![
        CapabilityAddon {
            name: "telemetry",
            description: "OpenTelemetry tracing integration",
            required_features: vec!["telemetry"],
            additional_deps: vec![],
            init_priority: 10,
            incompatible_with: vec![],
            code_fragments: AddonCodeFragments {
                imports: vec![],
                initialization: "",
                agent_builder_calls: "",
                env_vars: vec![(
                    "OTEL_EXPORTER_OTLP_ENDPOINT",
                    "OpenTelemetry collector endpoint (optional)",
                )],
                additional_files: vec![],
            },
        },
        CapabilityAddon {
            name: "auth",
            description: "API key and JWT authentication",
            required_features: vec!["auth"],
            additional_deps: vec![],
            init_priority: 20,
            incompatible_with: vec![],
            code_fragments: AddonCodeFragments {
                imports: vec!["use adk_rust::auth::*;"],
                initialization: r#"let auth_key = std::env::var("AUTH_API_KEY")
        .expect("AUTH_API_KEY must be set for authentication");"#,
                agent_builder_calls: "",
                env_vars: vec![("AUTH_API_KEY", "API key for request authentication")],
                additional_files: vec![],
            },
        },
        CapabilityAddon {
            name: "sessions",
            description: "Session state management and persistence",
            required_features: vec!["sessions"],
            additional_deps: vec![],
            init_priority: 30,
            incompatible_with: vec![],
            code_fragments: AddonCodeFragments {
                imports: vec!["use adk_rust::session::InMemorySessionService;"],
                initialization: r#"let session_service = Arc::new(InMemorySessionService::new());
    // For PostgreSQL: let session_service = Arc::new(PostgresSessionService::connect(&database_url).await?);
    // For Redis: let session_service = Arc::new(RedisSessionService::connect(&redis_url).await?);"#,
                agent_builder_calls: "",
                env_vars: vec![(
                    "DATABASE_URL",
                    "Session database URL (optional, uses in-memory by default)",
                )],
                additional_files: vec![],
            },
        },
        CapabilityAddon {
            name: "memory",
            description: "Semantic memory and RAG search integration",
            required_features: vec!["memory"],
            additional_deps: vec![],
            init_priority: 40,
            incompatible_with: vec![],
            code_fragments: AddonCodeFragments {
                imports: vec!["use adk_rust::memory::InMemoryMemoryService;"],
                initialization: r#"let memory_service = Arc::new(InMemoryMemoryService::new());
    tracing::info!("memory service initialized");"#,
                agent_builder_calls: "",
                env_vars: vec![],
                additional_files: vec![],
            },
        },
        CapabilityAddon {
            name: "mcp",
            description: "Model Context Protocol server connections",
            required_features: vec!["tools", "mcp"],
            additional_deps: vec![],
            init_priority: 50,
            incompatible_with: vec![],
            code_fragments: AddonCodeFragments {
                imports: vec!["use adk_rust::tool::McpToolset;"],
                initialization: r#"// Connect to MCP servers for extended tool capabilities
    // let mcp_tools = McpToolset::from_server("npx", &["-y", "@anthropic/mcp-server-filesystem", "/tmp"]).await?;
    tracing::info!("MCP tools ready");"#,
                agent_builder_calls: "",
                env_vars: vec![],
                additional_files: vec![],
            },
        },
        CapabilityAddon {
            name: "guardrails",
            description: "Input/output validation and content filtering",
            required_features: vec!["guardrail"],
            additional_deps: vec![],
            init_priority: 60,
            incompatible_with: vec![],
            code_fragments: AddonCodeFragments {
                imports: vec!["use adk_rust::guardrail::*;"],
                initialization: r#"// Configure input/output guardrails
    // let guardrail = PiiRedactionGuardrail::new();
    tracing::info!("guardrails configured");"#,
                agent_builder_calls: "",
                env_vars: vec![],
                additional_files: vec![],
            },
        },
        CapabilityAddon {
            name: "eval",
            description: "Evaluation framework for agent quality testing",
            required_features: vec!["eval"],
            additional_deps: vec![],
            init_priority: 70,
            incompatible_with: vec![],
            code_fragments: AddonCodeFragments {
                imports: vec!["use adk_rust::eval::*;"],
                initialization: r#"// Evaluation harness for agent quality testing
    // Run with: cargo test -- --ignored
    tracing::info!("eval framework available");"#,
                agent_builder_calls: "",
                env_vars: vec![],
                additional_files: vec![],
            },
        },
        CapabilityAddon {
            name: "browser",
            description: "Browser automation tools via WebDriver",
            required_features: vec!["browser"],
            additional_deps: vec![],
            init_priority: 80,
            incompatible_with: vec![],
            code_fragments: AddonCodeFragments {
                imports: vec!["use adk_rust::browser::BrowserTool;"],
                initialization: r#"// Browser automation tool via WebDriver
    // let browser_tool = BrowserTool::new().await?;
    tracing::info!("browser tools available");"#,
                agent_builder_calls: "",
                env_vars: vec![("WEBDRIVER_URL", "WebDriver endpoint URL (optional)")],
                additional_files: vec![],
            },
        },
        CapabilityAddon {
            name: "server",
            description: "HTTP server with A2A protocol support",
            required_features: vec!["server"],
            additional_deps: vec![],
            init_priority: 90,
            incompatible_with: vec![],
            code_fragments: AddonCodeFragments {
                imports: vec!["use adk_rust::server::A2aServer;"],
                initialization: r#"let port = std::env::var("PORT").unwrap_or_else(|_| "8080".to_string());
    let addr = format!("0.0.0.0:{port}");

    let server = A2aServer::builder()
        .agent(agent.clone())
        .bind_addr(&addr)
        .build()?;

    tracing::info!("starting A2A server on http://{}", addr);
    server.serve().await?;"#,
                agent_builder_calls: "",
                env_vars: vec![("PORT", "Server port (default: 8080)")],
                additional_files: vec![],
            },
        },
    ]
}

/// All 5 built-in enterprise patterns.
fn builtin_enterprise_patterns() -> Vec<EnterprisePattern> {
    vec![
        EnterprisePattern {
            name: "multi-agent",
            description: "Multi-agent supervisor with telemetry observability",
            base_template: "sequential",
            included_addons: vec!["telemetry"],
            override_features: None,
            code_fragments: None,
        },
        EnterprisePattern {
            name: "production",
            description: "Production-ready LLM agent with server, auth, sessions, and telemetry",
            base_template: "llm",
            included_addons: vec!["server", "auth", "sessions", "telemetry"],
            override_features: None,
            code_fragments: None,
        },
        EnterprisePattern {
            name: "pipeline",
            description: "Sequential data processing pipeline with session state",
            base_template: "sequential",
            included_addons: vec!["sessions", "telemetry"],
            override_features: None,
            code_fragments: None,
        },
        EnterprisePattern {
            name: "chatbot",
            description: "Conversational chatbot with memory and HTTP interface",
            base_template: "llm",
            included_addons: vec!["sessions", "memory", "server"],
            override_features: None,
            code_fragments: None,
        },
        EnterprisePattern {
            name: "a2a-server",
            description: "Agent-to-Agent protocol server with session management",
            base_template: "llm",
            included_addons: vec!["server", "sessions"],
            override_features: None,
            code_fragments: None,
        },
    ]
}

/// Legacy alias mappings (6 total).
fn builtin_aliases() -> HashMap<&'static str, &'static str> {
    let mut aliases = HashMap::new();
    aliases.insert("basic", "llm");
    aliases.insert("tools", "llm");
    aliases.insert("rag", "llm");
    aliases.insert("api", "llm");
    aliases.insert("openai", "llm");
    aliases.insert("a2a", "llm");
    aliases
}