gemini_adk_rs/utils/variant.rs
1/// The backend variant for Google LLM access.
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub enum GoogleLlmVariant {
4 /// Vertex AI (enterprise, project-based).
5 VertexAi,
6 /// Gemini API (API-key based, consumer).
7 GeminiApi,
8}
9
10/// Determine the Google LLM variant from the environment.
11///
12/// Reads the `GOOGLE_GENAI_USE_VERTEXAI` environment variable.
13/// Returns [`GoogleLlmVariant::VertexAi`] when the variable is set to a
14/// truthy value (`"true"`, `"1"`, case-insensitive), and
15/// [`GoogleLlmVariant::GeminiApi`] otherwise (including when the variable
16/// is unset).
17pub fn get_google_llm_variant() -> GoogleLlmVariant {
18 classify(std::env::var("GOOGLE_GENAI_USE_VERTEXAI").ok().as_deref())
19}
20
21/// The rule itself, separated from where the value comes from.
22///
23/// This exists so the rule can be tested without touching the process
24/// environment. Environment variables are global mutable state shared by every
25/// thread in the test binary, and `cargo test` runs tests in parallel: a set of
26/// tests that each set the same variable and then read it back will pass
27/// locally for months and fail on a busier machine, having read a value another
28/// test wrote microseconds earlier.
29fn classify(value: Option<&str>) -> GoogleLlmVariant {
30 match value {
31 Some(value) => {
32 let lower = value.to_lowercase();
33 if lower == "true" || lower == "1" {
34 GoogleLlmVariant::VertexAi
35 } else {
36 GoogleLlmVariant::GeminiApi
37 }
38 }
39 None => GoogleLlmVariant::GeminiApi,
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46
47 /// The rule, exhaustively, with no global state involved.
48 ///
49 /// These replace seven tests that each set `GOOGLE_GENAI_USE_VERTEXAI` and
50 /// read it back. That worked whenever the scheduler happened to keep them
51 /// apart and failed when it did not — on CI, `vertex_ai_true_lowercase`
52 /// asserted `VertexAi` and got `GeminiApi`, having read the value a
53 /// concurrent test had just written. The helper's own comment argued it was
54 /// safe "because each test uses a unique value"; the values were unique but
55 /// the *variable* was one, shared by every thread in the binary.
56 #[test]
57 fn truthy_values_select_vertex() {
58 for value in ["true", "TRUE", "True", "1"] {
59 assert_eq!(
60 classify(Some(value)),
61 GoogleLlmVariant::VertexAi,
62 "{value:?} should select Vertex"
63 );
64 }
65 }
66
67 #[test]
68 fn everything_else_selects_the_gemini_api() {
69 for value in ["false", "0", "", "yes", "vertex", "TRUEISH"] {
70 assert_eq!(
71 classify(Some(value)),
72 GoogleLlmVariant::GeminiApi,
73 "{value:?} should select the Gemini API"
74 );
75 }
76 }
77
78 #[test]
79 fn an_unset_variable_selects_the_gemini_api() {
80 assert_eq!(classify(None), GoogleLlmVariant::GeminiApi);
81 }
82
83 /// One test that the wiring reads the variable at all.
84 ///
85 /// Deliberately the only test in this module that touches the environment,
86 /// which is what makes it safe: with a single writer there is nobody to
87 /// race. It asserts the plumbing, not the rule — the rule is covered above.
88 #[test]
89 fn the_variable_is_the_one_that_is_read() {
90 let restore = std::env::var("GOOGLE_GENAI_USE_VERTEXAI").ok();
91 std::env::set_var("GOOGLE_GENAI_USE_VERTEXAI", "true");
92 let observed = get_google_llm_variant();
93 match restore {
94 Some(value) => std::env::set_var("GOOGLE_GENAI_USE_VERTEXAI", value),
95 None => std::env::remove_var("GOOGLE_GENAI_USE_VERTEXAI"),
96 }
97 assert_eq!(observed, GoogleLlmVariant::VertexAi);
98 }
99}