gemini_adk_fluent_rs/live/
contract.rs1use gemini_adk_rs::live::{
2 ControlContract, ExtractorContract, PhaseContract, PreparationContract, PromotionContract,
3 RuntimeContract, ToolContract, TransitionContract,
4};
5use gemini_genai_rs::prelude::Tool;
6
7use super::Live;
8
9impl Live {
10 pub fn describe_contract(&self) -> RuntimeContract {
16 let mut tools = describe_tools(&self.config.tools);
17 if let Some(dispatcher) = &self.dispatcher {
18 tools.extend(describe_tools(&dispatcher.to_tool_declarations()));
19 }
20 tools.extend(self.deferred_agent_tools.iter().map(|tool| ToolContract {
21 name: tool.name.clone(),
22 description: tool.description.clone(),
23 behavior: Some("AgentTool".into()),
24 }));
25
26 RuntimeContract {
27 version: 1,
28 model: self.config.model.to_string(),
29 tools,
30 phases: self.phases.iter().map(describe_phase).collect(),
31 initial_phase: self.initial_phase.clone(),
32 extractors: self
33 .extractors
34 .iter()
35 .map(|extractor| ExtractorContract {
36 name: extractor.name().to_string(),
37 window_size: extractor.window_size(),
38 trigger: format!("{:?}", extractor.trigger()),
39 promotions: extractor
40 .promotion_rules()
41 .iter()
42 .map(|rule| PromotionContract {
43 field: rule.field.clone(),
44 state_key: rule.state_key.clone(),
45 merge: format!("{:?}", rule.merge),
46 has_predicate: rule.accept.is_some(),
47 })
48 .collect(),
49 })
50 .collect(),
51 computed: self.computed.describe(),
52 watchers: self.watchers.describe(),
53 controls: ControlContract {
54 soft_turn_timeout_ms: self
55 .soft_turn_timeout
56 .map(|timeout| timeout.as_millis() as u64),
57 steering_mode: format!("{:?}", self.steering_mode),
58 context_delivery: format!("{:?}", self.context_delivery),
59 tool_advisory: self.tool_advisory,
60 telemetry_interval_ms: self
61 .telemetry_interval
62 .map(|interval| interval.as_millis() as u64),
63 repair_enabled: self.repair_config.is_some(),
64 persistence_enabled: self.persistence.is_some(),
65 },
66 }
67 }
68}
69
70fn describe_phase(phase: &gemini_adk_rs::live::Phase) -> PhaseContract {
71 PhaseContract {
72 name: phase.name.clone(),
73 terminal: phase.terminal,
74 tools_enabled: phase.tools_enabled.clone(),
75 needs: phase.needs.clone(),
76 requires: phase.requires.clone(),
77 preparations: phase
78 .preparations
79 .iter()
80 .map(|prep| PreparationContract {
81 name: prep.name.clone(),
82 produces: prep.produces.clone(),
83 })
84 .collect(),
85 presents: phase.presents.clone(),
86 clear_on_enter: phase.clear_on_enter.clone(),
87 transitions: phase
88 .transitions
89 .iter()
90 .map(|transition| TransitionContract {
91 target: transition.target.clone(),
92 description: transition.description.clone(),
93 has_guard: true,
94 })
95 .collect(),
96 has_guard: phase.guard.is_some(),
97 prompt_on_enter: phase.prompt_on_enter,
98 }
99}
100
101fn describe_tools(tools: &[Tool]) -> Vec<ToolContract> {
102 let mut described = Vec::new();
103 for tool in tools {
104 if let Some(functions) = &tool.function_declarations {
105 described.extend(functions.iter().map(|function| {
106 ToolContract {
107 name: function.name.clone(),
108 description: function.description.clone(),
109 behavior: function
110 .behavior
111 .as_ref()
112 .map(|behavior| format!("{behavior:?}")),
113 }
114 }));
115 }
116 if tool.google_search.is_some() {
117 described.push(ToolContract {
118 name: "google_search".into(),
119 description: "Google Search grounding".into(),
120 behavior: None,
121 });
122 }
123 if tool.code_execution.is_some() {
124 described.push(ToolContract {
125 name: "code_execution".into(),
126 description: "Code execution".into(),
127 behavior: None,
128 });
129 }
130 if tool.url_context.is_some() {
131 described.push(ToolContract {
132 name: "url_context".into(),
133 description: "URL context retrieval".into(),
134 behavior: None,
135 });
136 }
137 if tool.google_search_retrieval.is_some() {
138 described.push(ToolContract {
139 name: "google_search_retrieval".into(),
140 description: "Google Search retrieval".into(),
141 behavior: None,
142 });
143 }
144 }
145 described
146}