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)]
44#[allow(
45 unsafe_code,
46 reason = "tests exercise env-driven platform detection and must mutate the process environment"
47)]
48mod tests {
49 use super::*;
50
51 /// The rule, exhaustively, with no global state involved.
52 ///
53 /// These replace seven tests that each set `GOOGLE_GENAI_USE_VERTEXAI` and
54 /// read it back. That worked whenever the scheduler happened to keep them
55 /// apart and failed when it did not — on CI, `vertex_ai_true_lowercase`
56 /// asserted `VertexAi` and got `GeminiApi`, having read the value a
57 /// concurrent test had just written. The helper's own comment argued it was
58 /// safe "because each test uses a unique value"; the values were unique but
59 /// the *variable* was one, shared by every thread in the binary.
60 #[test]
61 fn truthy_values_select_vertex() {
62 for value in ["true", "TRUE", "True", "1"] {
63 assert_eq!(
64 classify(Some(value)),
65 GoogleLlmVariant::VertexAi,
66 "{value:?} should select Vertex"
67 );
68 }
69 }
70
71 #[test]
72 fn everything_else_selects_the_gemini_api() {
73 for value in ["false", "0", "", "yes", "vertex", "TRUEISH"] {
74 assert_eq!(
75 classify(Some(value)),
76 GoogleLlmVariant::GeminiApi,
77 "{value:?} should select the Gemini API"
78 );
79 }
80 }
81
82 #[test]
83 fn an_unset_variable_selects_the_gemini_api() {
84 assert_eq!(classify(None), GoogleLlmVariant::GeminiApi);
85 }
86
87 /// One test that the wiring reads the variable at all.
88 ///
89 /// Deliberately the only test in this module that touches the environment,
90 /// which is what makes it safe: with a single writer there is nobody to
91 /// race. It asserts the plumbing, not the rule — the rule is covered above.
92 #[test]
93 fn the_variable_is_the_one_that_is_read() {
94 let restore = std::env::var("GOOGLE_GENAI_USE_VERTEXAI").ok();
95 unsafe { std::env::set_var("GOOGLE_GENAI_USE_VERTEXAI", "true") };
96 let observed = get_google_llm_variant();
97 match restore {
98 Some(value) => unsafe { std::env::set_var("GOOGLE_GENAI_USE_VERTEXAI", value) },
99 None => unsafe { std::env::remove_var("GOOGLE_GENAI_USE_VERTEXAI") },
100 }
101 assert_eq!(observed, GoogleLlmVariant::VertexAi);
102 }
103}