From e9e741e6b4a02aec1d101fb902e67599429fdf84 Mon Sep 17 00:00:00 2001 From: Arnab Nandy Date: Fri, 17 Jul 2026 18:56:29 +0530 Subject: [PATCH] fix: lazily initialize tokenizer HTTP client Signed-off-by: Arnab Nandy --- .../com/google/genai/LocalTokenizerLoader.java | 15 +++++++++++++-- .../google/genai/LocalTokenizerLoaderTest.java | 12 ++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/google/genai/LocalTokenizerLoader.java b/src/main/java/com/google/genai/LocalTokenizerLoader.java index c5c9131983a..41391d39d51 100644 --- a/src/main/java/com/google/genai/LocalTokenizerLoader.java +++ b/src/main/java/com/google/genai/LocalTokenizerLoader.java @@ -129,10 +129,21 @@ public String toString() { private static final Map modelProtoCache = new ConcurrentHashMap<>(); private static final Map localTokenizerProcessorCache = new ConcurrentHashMap<>(); - private static OkHttpClient httpClient = new OkHttpClient(); + // Non-null only when replaced by tests. + private static OkHttpClient httpClient; private LocalTokenizerLoader() {} + private static final class HttpClientHolder { + private static final OkHttpClient INSTANCE = new OkHttpClient(); + + private HttpClientHolder() {} + } + + private static OkHttpClient getHttpClient() { + return httpClient != null ? httpClient : HttpClientHolder.INSTANCE; + } + /** Gets the tokenizer name for the given model name. */ public static String getTokenizerName(String modelName) { if (GEMINI_MODELS_TO_TOKENIZER_NAMES.containsKey(modelName)) { @@ -240,7 +251,7 @@ private static void maybeSaveToCache(Path cacheDir, Path cachePath, byte[] conte private static byte[] loadFromUrl(String fileUrl, String expectedHash) throws IOException { Request request = new Request.Builder().url(fileUrl).build(); - try (Response response = httpClient.newCall(request).execute()) { + try (Response response = getHttpClient().newCall(request).execute()) { if (response == null) { throw new GenAiIOException("HTTP request failed: response is null"); } diff --git a/src/test/java/com/google/genai/LocalTokenizerLoaderTest.java b/src/test/java/com/google/genai/LocalTokenizerLoaderTest.java index 8376c8d7326..f24cfbc3b2d 100644 --- a/src/test/java/com/google/genai/LocalTokenizerLoaderTest.java +++ b/src/test/java/com/google/genai/LocalTokenizerLoaderTest.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -98,11 +99,22 @@ void setUp() throws Exception { @AfterEach void tearDown() throws Exception { System.setProperty("java.io.tmpdir", originalTmpDir); + Field clientField = LocalTokenizerLoader.class.getDeclaredField("httpClient"); + setStatic(clientField, null); // Restore original tokenizers map Field tokenizersField = LocalTokenizerLoader.class.getDeclaredField("TOKENIZERS"); setStatic(tokenizersField, originalTokenizers); } + @Test + void getTokenizerName_doesNotInitializeHttpClient() throws Exception { + Field clientField = LocalTokenizerLoader.class.getDeclaredField("httpClient"); + setStatic(clientField, null); + + assertEquals("gemma3", LocalTokenizerLoader.getTokenizerName("gemini-2.5-pro")); + assertNull(clientField.get(null)); + } + @Test void getTokenizerName_supportedModels() { assertEquals("gemma3", LocalTokenizerLoader.getTokenizerName("gemini-2.5-pro"));