codegen_demo/
codegen_demo.rs

1//! ADK Studio Codegen Demo
2//!
3//! Demonstrates generating Rust code from project schemas (templates).
4//! Run: cargo run -p adk-studio --example codegen_demo
5
6use adk_studio::codegen::generate_rust_project;
7use adk_studio::schema::{AgentSchema, AgentType, ProjectSchema, Route};
8use std::fs;
9
10fn main() {
11    println!("🔧 ADK Studio Codegen Demo\n");
12    println!("This demo generates Rust projects from agent templates.\n");
13
14    // Generate all template examples
15    let templates = vec![
16        ("simple_chat", simple_chat_project()),
17        ("research_pipeline", research_pipeline_project()),
18        ("content_refiner", content_refiner_project()),
19        ("parallel_analyzer", parallel_analyzer_project()),
20        ("support_router", support_router_project()),
21    ];
22
23    for (name, project) in templates {
24        println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
25        println!("📦 Generating: {}", name);
26        println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
27
28        match generate_rust_project(&project) {
29            Ok(generated) => {
30                for file in &generated.files {
31                    println!("📄 {}\n", file.path);
32                    // Print first 60 lines of main.rs
33                    if file.path == "src/main.rs" {
34                        let lines: Vec<&str> = file.content.lines().take(60).collect();
35                        for line in lines {
36                            println!("  {}", line);
37                        }
38                        println!("  ... (truncated)\n");
39                    }
40                }
41
42                // Optionally write to disk
43                let out_dir = format!("/tmp/adk-codegen-demo/{}", name);
44                fs::create_dir_all(format!("{}/src", out_dir)).ok();
45                for file in &generated.files {
46                    let path = format!("{}/{}", out_dir, file.path);
47                    fs::write(&path, &file.content).ok();
48                }
49                println!("✅ Written to: {}\n", out_dir);
50            }
51            Err(e) => println!("❌ Error: {}\n", e),
52        }
53    }
54
55    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
56    println!("🚀 To run an example:");
57    println!("   cd /tmp/adk-codegen-demo/simple_chat");
58    println!("   GOOGLE_API_KEY=your_key cargo run");
59    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
60}
61
62// ============================================================================
63// Template Projects
64// ============================================================================
65
66fn simple_chat_project() -> ProjectSchema {
67    let mut p = ProjectSchema::new("simple_chat");
68    p.agents.insert(
69        "chat_agent".to_string(),
70        AgentSchema {
71            agent_type: AgentType::Llm,
72            model: Some("gemini-2.0-flash".to_string()),
73            instruction:
74                "You are a helpful, friendly assistant. Answer questions clearly and concisely."
75                    .to_string(),
76            tools: vec![],
77            sub_agents: vec![],
78            position: Default::default(),
79            max_iterations: None,
80            routes: vec![],
81        },
82    );
83    p.workflow.edges.push(adk_studio::schema::Edge {
84        from: "START".to_string(),
85        to: "chat_agent".to_string(),
86        condition: None,
87    });
88    p.workflow.edges.push(adk_studio::schema::Edge {
89        from: "chat_agent".to_string(),
90        to: "END".to_string(),
91        condition: None,
92    });
93    p
94}
95
96fn research_pipeline_project() -> ProjectSchema {
97    let mut p = ProjectSchema::new("research_pipeline");
98
99    // Sub-agents
100    p.agents.insert(
101        "researcher".to_string(),
102        AgentSchema {
103            agent_type: AgentType::Llm,
104            model: Some("gemini-2.0-flash".to_string()),
105            instruction:
106                "Research the topic using Google Search. Gather key facts and recent developments."
107                    .to_string(),
108            tools: vec!["google_search".to_string()],
109            sub_agents: vec![],
110            position: Default::default(),
111            max_iterations: None,
112            routes: vec![],
113        },
114    );
115    p.agents.insert(
116        "summarizer".to_string(),
117        AgentSchema {
118            agent_type: AgentType::Llm,
119            model: Some("gemini-2.0-flash".to_string()),
120            instruction: "Summarize the research into key takeaways and conclusions.".to_string(),
121            tools: vec![],
122            sub_agents: vec![],
123            position: Default::default(),
124            max_iterations: None,
125            routes: vec![],
126        },
127    );
128
129    // Sequential container
130    p.agents.insert(
131        "pipeline".to_string(),
132        AgentSchema {
133            agent_type: AgentType::Sequential,
134            model: None,
135            instruction: String::new(),
136            tools: vec![],
137            sub_agents: vec!["researcher".to_string(), "summarizer".to_string()],
138            position: Default::default(),
139            max_iterations: None,
140            routes: vec![],
141        },
142    );
143
144    p.workflow.edges.push(adk_studio::schema::Edge {
145        from: "START".to_string(),
146        to: "pipeline".to_string(),
147        condition: None,
148    });
149    p.workflow.edges.push(adk_studio::schema::Edge {
150        from: "pipeline".to_string(),
151        to: "END".to_string(),
152        condition: None,
153    });
154    p
155}
156
157fn content_refiner_project() -> ProjectSchema {
158    let mut p = ProjectSchema::new("content_refiner");
159
160    p.agents.insert(
161        "improver".to_string(),
162        AgentSchema {
163            agent_type: AgentType::Llm,
164            model: Some("gemini-2.0-flash".to_string()),
165            instruction: "Improve the content: fix errors, enhance clarity, improve flow."
166                .to_string(),
167            tools: vec![],
168            sub_agents: vec![],
169            position: Default::default(),
170            max_iterations: None,
171            routes: vec![],
172        },
173    );
174    p.agents.insert(
175        "reviewer".to_string(),
176        AgentSchema {
177            agent_type: AgentType::Llm,
178            model: Some("gemini-2.0-flash".to_string()),
179            instruction:
180                "Review content quality. Call exit_loop when polished, otherwise continue."
181                    .to_string(),
182            tools: vec!["exit_loop".to_string()],
183            sub_agents: vec![],
184            position: Default::default(),
185            max_iterations: None,
186            routes: vec![],
187        },
188    );
189
190    // Loop container
191    p.agents.insert(
192        "refiner".to_string(),
193        AgentSchema {
194            agent_type: AgentType::Loop,
195            model: None,
196            instruction: String::new(),
197            tools: vec![],
198            sub_agents: vec!["improver".to_string(), "reviewer".to_string()],
199            position: Default::default(),
200            max_iterations: Some(3),
201            routes: vec![],
202        },
203    );
204
205    p.workflow.edges.push(adk_studio::schema::Edge {
206        from: "START".to_string(),
207        to: "refiner".to_string(),
208        condition: None,
209    });
210    p.workflow.edges.push(adk_studio::schema::Edge {
211        from: "refiner".to_string(),
212        to: "END".to_string(),
213        condition: None,
214    });
215    p
216}
217
218fn parallel_analyzer_project() -> ProjectSchema {
219    let mut p = ProjectSchema::new("parallel_analyzer");
220
221    p.agents.insert(
222        "sentiment".to_string(),
223        AgentSchema {
224            agent_type: AgentType::Llm,
225            model: Some("gemini-2.0-flash".to_string()),
226            instruction: "Analyze sentiment: positive/negative/neutral with key emotional tones."
227                .to_string(),
228            tools: vec![],
229            sub_agents: vec![],
230            position: Default::default(),
231            max_iterations: None,
232            routes: vec![],
233        },
234    );
235    p.agents.insert(
236        "entities".to_string(),
237        AgentSchema {
238            agent_type: AgentType::Llm,
239            model: Some("gemini-2.0-flash".to_string()),
240            instruction: "Extract entities: people, organizations, locations, dates.".to_string(),
241            tools: vec![],
242            sub_agents: vec![],
243            position: Default::default(),
244            max_iterations: None,
245            routes: vec![],
246        },
247    );
248
249    // Parallel container
250    p.agents.insert(
251        "analyzer".to_string(),
252        AgentSchema {
253            agent_type: AgentType::Parallel,
254            model: None,
255            instruction: String::new(),
256            tools: vec![],
257            sub_agents: vec!["sentiment".to_string(), "entities".to_string()],
258            position: Default::default(),
259            max_iterations: None,
260            routes: vec![],
261        },
262    );
263
264    p.workflow.edges.push(adk_studio::schema::Edge {
265        from: "START".to_string(),
266        to: "analyzer".to_string(),
267        condition: None,
268    });
269    p.workflow.edges.push(adk_studio::schema::Edge {
270        from: "analyzer".to_string(),
271        to: "END".to_string(),
272        condition: None,
273    });
274    p
275}
276
277fn support_router_project() -> ProjectSchema {
278    let mut p = ProjectSchema::new("support_router");
279
280    // Router
281    p.agents.insert(
282        "router".to_string(),
283        AgentSchema {
284            agent_type: AgentType::Router,
285            model: Some("gemini-2.0-flash".to_string()),
286            instruction: "Classify request as: technical, billing, or general.".to_string(),
287            tools: vec![],
288            sub_agents: vec![],
289            position: Default::default(),
290            max_iterations: None,
291            routes: vec![
292                Route { condition: "technical".to_string(), target: "tech_agent".to_string() },
293                Route { condition: "billing".to_string(), target: "billing_agent".to_string() },
294                Route { condition: "general".to_string(), target: "general_agent".to_string() },
295            ],
296        },
297    );
298
299    // Target agents
300    p.agents.insert(
301        "tech_agent".to_string(),
302        AgentSchema {
303            agent_type: AgentType::Llm,
304            model: Some("gemini-2.0-flash".to_string()),
305            instruction: "You are technical support. Help with coding and bugs.".to_string(),
306            tools: vec![],
307            sub_agents: vec![],
308            position: Default::default(),
309            max_iterations: None,
310            routes: vec![],
311        },
312    );
313    p.agents.insert(
314        "billing_agent".to_string(),
315        AgentSchema {
316            agent_type: AgentType::Llm,
317            model: Some("gemini-2.0-flash".to_string()),
318            instruction: "You are billing support. Help with payments and subscriptions."
319                .to_string(),
320            tools: vec![],
321            sub_agents: vec![],
322            position: Default::default(),
323            max_iterations: None,
324            routes: vec![],
325        },
326    );
327    p.agents.insert(
328        "general_agent".to_string(),
329        AgentSchema {
330            agent_type: AgentType::Llm,
331            model: Some("gemini-2.0-flash".to_string()),
332            instruction: "You are general support. Help with general questions.".to_string(),
333            tools: vec![],
334            sub_agents: vec![],
335            position: Default::default(),
336            max_iterations: None,
337            routes: vec![],
338        },
339    );
340
341    p.workflow.edges.push(adk_studio::schema::Edge {
342        from: "START".to_string(),
343        to: "router".to_string(),
344        condition: None,
345    });
346    p.workflow.edges.push(adk_studio::schema::Edge {
347        from: "router".to_string(),
348        to: "tech_agent".to_string(),
349        condition: Some("technical".to_string()),
350    });
351    p.workflow.edges.push(adk_studio::schema::Edge {
352        from: "router".to_string(),
353        to: "billing_agent".to_string(),
354        condition: Some("billing".to_string()),
355    });
356    p.workflow.edges.push(adk_studio::schema::Edge {
357        from: "router".to_string(),
358        to: "general_agent".to_string(),
359        condition: Some("general".to_string()),
360    });
361    p.workflow.edges.push(adk_studio::schema::Edge {
362        from: "tech_agent".to_string(),
363        to: "END".to_string(),
364        condition: None,
365    });
366    p.workflow.edges.push(adk_studio::schema::Edge {
367        from: "billing_agent".to_string(),
368        to: "END".to_string(),
369        condition: None,
370    });
371    p.workflow.edges.push(adk_studio::schema::Edge {
372        from: "general_agent".to_string(),
373        to: "END".to_string(),
374        condition: None,
375    });
376    p
377}