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}