Google Gen AI (Experimental)
https://github.com/googleapis/java-genai
[!WARNING]
This integration is currently marked as Experimental. The API and implementation are subject to change in future releases. It uses the new official Google Gen AI SDK for Java (com.google.genai:google-genai).
Table of Contents
- Maven Dependency
- API Key
- Models Available
- GoogleGenAiChatModel
- GoogleGenAiStreamingChatModel
- Tools
- JSON Schema / Structured Outputs
Maven Dependency
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-google-genai</artifactId>
<version>1.15.0-beta25</version>
</dependency>
API Key
Get an API key for free here: https://ai.google.dev/gemini-api/docs/api-key.
Models available
Check the list of available models in the documentation.
gemini-3.1-pro-previewgemini-3.1-flash-litegemini-3-pro-previewgemini-3-flash-previewgemini-2.5-progemini-2.5-flashgemini-2.5-flash-lite
(See the official documentation for the full list of specialized preview models like -image, -tts, and -live).
GoogleGenAiChatModel
The usual chat(...) methods are available:
ChatModel gemini = GoogleGenAiChatModel.builder()
.apiKey(System.getenv("GOOGLE_AI_GEMINI_API_KEY"))
.modelName("gemini-2.5-flash")
.build();
String response = gemini.chat("Hello Gemini!");
As well as the ChatResponse chat(ChatRequest req) method:
ChatModel gemini = GoogleGenAiChatModel.builder()
.apiKey(System.getenv("GOOGLE_AI_GEMINI_API_KEY"))
.modelName("gemini-2.5-flash")
.build();
ChatResponse chatResponse = gemini.chat(ChatRequest.builder()
.messages(UserMessage.from(
"How many R's are there in the word 'strawberry'?"))
.build());
String response = chatResponse.aiMessage().text();
Configuring
ChatModel gemini = GoogleGenAiChatModel.builder()
.apiKey(System.getenv("GOOGLE_AI_GEMINI_API_KEY"))
// or .googleCredentials(...)
.projectId(...)
.location(...)
.modelName("gemini-2.5-flash")
.temperature(1.0)
.topP(0.95)
.topK(64)
.seed(42)
.maxOutputTokens(8192)
.timeout(Duration.ofSeconds(60))
.maxRetries(2)
.stopSequences(List.of(...))
.safetySettings(List.of(...))
.responseFormat(ResponseFormat.JSON)
.enableGoogleSearch(true)
.enableGoogleMaps(true)
.enableUrlContext(true)
.allowedFunctionNames(List.of("getWeather"))
.thinkingBudget(250)
.listeners(...)
.build();
GoogleGenAiStreamingChatModel
The GoogleGenAiStreamingChatModel allows streaming the text of a response token by token.
The response must be handled by a StreamingChatResponseHandler.
StreamingChatModel gemini = GoogleGenAiStreamingChatModel.builder()
.apiKey(System.getenv("GOOGLE_AI_GEMINI_API_KEY"))
.modelName("gemini-2.5-flash")
.build();
CompletableFuture<ChatResponse> futureResponse = new CompletableFuture<>();
gemini.chat("Tell me a joke about Java", new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
System.out.print(partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
futureResponse.complete(completeResponse);
}
@Override
public void onError(Throwable error) {
futureResponse.completeExceptionally(error);
}
});
futureResponse.join();
Executor
The Google Gen AI SDK exposes streaming as a blocking ResponseStream iterator: each chunk is delivered by a blocking next() call. GoogleGenAiStreamingChatModel therefore needs an ExecutorService to drive that iteration off the caller's thread.
If you don't pass one, a shared default from DefaultExecutorProvider is used (lazily initialized, uses virtual threads when available). This works out of the box but is not recommended for production: the default executor is unbounded, JVM-wide, and not tied to your application lifecycle — so it offers no back-pressure, no graceful shutdown, and no visibility in your metrics.
You should almost always supply your own executor — for example, your framework's managed task executor (Spring TaskExecutor, Quarkus ManagedExecutor, ...), a virtual-thread executor you own, or a bounded pool tuned to your concurrency budget:
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); // or your framework's executor
StreamingChatModel gemini = GoogleGenAiStreamingChatModel.builder()
.apiKey(System.getenv("GOOGLE_AI_GEMINI_API_KEY"))
.modelName("gemini-2.5-flash")
.executor(executor)
.build();
Tools
Tools (aka Function Calling) are supported. You can define them using LangChain4j's AiServices:
class WeatherForecastService {
@Tool("Get the weather forecast for a location")
String getForecast(@P("Location to get the forecast for") String location) {
return "The weather in " + location + " is sunny and 25°C.";
}
}
interface WeatherAssistant {
String chat(String userMessage);
}
WeatherForecastService weatherForecastService = new WeatherForecastService();
ChatModel gemini = GoogleGenAiChatModel.builder()
.apiKey(System.getenv("GOOGLE_AI_GEMINI_API_KEY"))
.modelName("gemini-2.5-flash")
.temperature(0.0)
.build();
WeatherAssistant weatherAssistant = AiServices.builder(WeatherAssistant.class)
.chatModel(gemini)
.tools(weatherForecastService)
.build();
String response = weatherAssistant.chat("What is the weather forecast for Tokyo?");
JSON Schema / Structured Outputs
The langchain4j-google-genai integration maps LangChain4j JSON schemas (ResponseFormat.jsonSchema()) directly into the ResponseSchema of the official Google Gen AI SDK. This allows natively extracting strongly-typed Java records!
record WeatherForecast(
@Description("minimum temperature") Integer minTemperature,
@Description("maximum temperature") Integer maxTemperature,
@Description("chances of rain") boolean rain
) { }
interface WeatherForecastAssistant {
WeatherForecast extract(String forecast);
}
ChatModel gemini = GoogleGenAiChatModel.builder()
.apiKey(System.getenv("GOOGLE_AI_GEMINI_API_KEY"))
.modelName("gemini-2.5-flash")
.build();
WeatherForecastAssistant forecastAssistant = AiServices.builder(WeatherForecastAssistant.class)
.chatModel(gemini)
.build();
WeatherForecast forecast = forecastAssistant.extract("""
Morning: The day dawns bright and clear in Osaka...
Temperatures climb to a comfortable 22°C (72°F) and
will drop to 15°C (59°F).
""");
[!NOTE]
The Google Gen AI API has some restrictions on advanced JSON schema features (such asanyOf/ polymorphic typing). Simple POJOs, lists, and nested objects are fully supported.