structured_output/
structured_output.rs1use antigravity_sdk_rust::agent::{Agent, AgentConfig};
2use antigravity_sdk_rust::policy;
3use antigravity_sdk_rust::tools::Tool;
4use antigravity_sdk_rust::types::GeminiConfig;
5use serde_json::Value;
6use std::sync::Arc;
7use tracing_subscriber::EnvFilter;
8
9struct FetchNotesTool;
10
11impl Tool for FetchNotesTool {
12 fn name(&self) -> &'static str {
13 "fetch_unstructured_meeting_notes"
14 }
15
16 fn description(&self) -> &'static str {
17 "Retrieves the raw unstructured notes for a given meeting ID."
18 }
19
20 fn parameters_json_schema(&self) -> &'static str {
21 r#"{
22 "type": "object",
23 "properties": {
24 "meeting_id": {
25 "type": "string"
26 }
27 },
28 "required": ["meeting_id"]
29 }"#
30 }
31
32 async fn call(&self, args: Value) -> Result<Value, anyhow::Error> {
33 let meeting_id = args
34 .get("meeting_id")
35 .and_then(Value::as_str)
36 .ok_or_else(|| anyhow::anyhow!("Missing meeting_id"))?;
37
38 if meeting_id == "meeting-2026-05" {
39 Ok(Value::String(
40 "Discussed launch timeline for project X. Alice agreed to update the textproto tests by Monday. \
41 Bob mentioned he will run the final E2E benchmarks tomorrow. I will push the release build \
42 once the tests are green."
43 .to_string(),
44 ))
45 } else {
46 Ok(Value::String("Error: Meeting notes not found.".to_string()))
47 }
48 }
49}
50
51#[tokio::main]
52async fn main() -> Result<(), anyhow::Error> {
53 tracing_subscriber::fmt()
55 .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
56 .init();
57
58 dotenvy::dotenv().ok();
60
61 let mut config = AgentConfig::default();
62
63 if let Ok(harness_path) = std::env::var("ANTIGRAVITY_HARNESS_PATH") {
64 config.binary_path = Some(harness_path);
65 }
66
67 let mut gemini_config = GeminiConfig::default();
68 if let Ok(api_key) = std::env::var("GEMINI_API_KEY") {
69 gemini_config.api_key = Some(api_key);
70 }
71 gemini_config.models.default.name = "gemini-3.5-flash".to_string();
72 config.gemini_config = gemini_config;
73
74 let response_schema = r#"{
76 "type": "object",
77 "properties": {
78 "action_items": {
79 "type": "array",
80 "items": {
81 "type": "object",
82 "properties": {
83 "assignee": { "type": "string" },
84 "task": { "type": "string" },
85 "deadline": { "type": "string" }
86 },
87 "required": ["assignee", "task", "deadline"]
88 }
89 }
90 },
91 "required": ["action_items"]
92 }"#;
93 config.response_schema = Some(response_schema.to_string());
94
95 config.tools = vec![Arc::new(FetchNotesTool)];
97
98 config.policies = Some(vec![
100 policy::deny_all(),
101 policy::allow("fetch_unstructured_meeting_notes"),
102 ]);
103
104 let mut agent = Agent::new(config);
105 println!("Starting agent...");
106 agent.start().await?;
107
108 let prompt = "Use the fetch_unstructured_meeting_notes tool to retrieve notes for \
109 'meeting-2026-05' and return the meeting summary with the appropriate \
110 action item list. Ensure each action item includes 'assignee', \
111 'task', and 'deadline'.";
112
113 println!("\n Sending prompt to agent...\n {}", prompt);
114 let response = agent.chat(prompt).await?;
115
116 println!("\n Extracting structured meeting action items...");
117
118 let mut found = false;
120 for step in &response.steps {
121 if let Some(ref structured) = step.structured_output {
122 println!("\n === Structured Meeting Action Items ===");
123 if let Some(items) = structured.get("action_items").and_then(Value::as_array) {
124 for item in items {
125 println!(
126 " - Assignee: {:?}",
127 item.get("assignee").and_then(Value::as_str).unwrap_or("")
128 );
129 println!(
130 " Task: {:?}",
131 item.get("task").and_then(Value::as_str).unwrap_or("")
132 );
133 println!(
134 " Deadline: {:?}",
135 item.get("deadline").and_then(Value::as_str).unwrap_or("")
136 );
137 println!();
138 }
139 } else {
140 println!("No action_items field or not an array: {:?}", structured);
141 }
142 found = true;
143 break;
144 }
145 }
146
147 if !found {
148 println!("\n Failed to extract structured summary natively.");
149 println!(" Final Text Response: {}", response.text);
150 }
151
152 agent.stop().await?;
153 Ok(())
154}