Google AI Gemini
https://ai.google.dev/gemini-api/docs
Table of Contents
- Maven Dependency
- API Key
- Models Available
- GoogleAiGeminiChatModel
- GoogleAiGeminiStreamingChatModel
- Tools
- Structured Outputs
- Python Code Execution
- Multimodality
- Thinking
- Gemini Files API
- Batch Processing
Maven Dependency
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-google-ai-gemini</artifactId>
<version>1.19.0</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-pro-previewgemini-2.5-progemini-2.5-flashgemini-2.5-flash-litegemini-2.0-flashgemini-2.0-flash-lite
GoogleAiGeminiChatModel
The usual chat(...) methods are available:
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
...
.build();
String response = gemini.chat("Hello Gemini!");
As well, as the ChatResponse chat(ChatRequest req) method:
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_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 = GoogleAiGeminiChatModel.builder()
.httpClientBuilder(...)
.defaultRequestParameters(...)
.apiKey(System.getenv("GEMINI_AI_KEY"))
.baseUrl(...)
.modelName("gemini-2.5-flash")
.maxRetries(...)
.temperature(1.0)
.topP(0.95)
.topK(64)
.seed(42)
.frequencyPenalty(...)
.presencePenalty(...)
.maxOutputTokens(8192)
.timeout(Duration.ofSeconds(60))
.responseFormat(ResponseFormat.JSON) // or .responseFormat(ResponseFormat.builder()...build())
.stopSequences(List.of(...))
.toolConfig(GeminiFunctionCallingConfig.builder()...build()) // or below
.toolConfig(GeminiMode.ANY, List.of("fnOne", "fnTwo"))
.allowCodeExecution(true)
.includeCodeExecution(true)
.logRequestsAndResponses(true)
.safetySettings(List<GeminiSafetySetting> or Map<GeminiHarmCategory, GeminiHarmBlockThreshold>)
.thinkingConfig(...)
.returnThinking(true)
.sendThinking(true)
.responseLogprobs(...)
.logprobs(...)
.enableEnhancedCivicAnswers(...)
.mediaResolution(GeminiMediaResolutionLevel.MEDIA_RESOLUTION_HIGH)
.mediaResolutionPerPartEnabled(true)
.listeners(...)
.supportedCapabilities(...)
.build();
Default Request Parameters
Instead of (or in addition to) the individual builder methods shown above, you can supply a single
ChatRequestParameters object via defaultRequestParameters(...). These parameters are applied to every
request issued by the model, unless they are overridden by the parameters of an individual ChatRequest.
You can pass either common ChatRequestParameters or Gemini-specific GoogleAiGeminiChatRequestParameters.
The latter additionally exposes Gemini-only options such as aspectRatio and imageSize:
GoogleAiGeminiChatRequestParameters parameters = GoogleAiGeminiChatRequestParameters.builder()
.modelName("gemini-2.5-flash")
.temperature(1.0)
.maxOutputTokens(8192)
.aspectRatio("16:9") // Gemini-specific
.imageSize("2K") // Gemini-specific
.build();
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.defaultRequestParameters(parameters)
.build();
When the same parameter is set both via defaultRequestParameters(...) and via an individual builder method
(e.g., modelName(String)), the value set via the individual builder method takes precedence:
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.defaultRequestParameters(GoogleAiGeminiChatRequestParameters.builder()
.modelName("gemini-2.5-flash")
.temperature(1.0)
.build())
.temperature(0.0) // overrides temperature from defaultRequestParameters
.build();
// effective parameters: modelName=gemini-2.5-flash, temperature=0.0
GoogleAiGeminiStreamingChatModel
The GoogleAiGeminiStreamingChatModel allows streaming the text of a response token by token.
The response must be handled by a StreamingChatResponseHandler.
StreamingChatModel gemini = GoogleAiGeminiStreamingChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_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();
Tools
Tools (aka Function Calling) is supported, including parallel calls.
You can either use the chat(ChatRequest) method that accepts a ChatRequest that can be configured with
one or more ToolSpecifications to let Gemini know it can request a function to be called.
Or you can use LangChain4j's AiServices to define them.
Here is an example of a weather tool, using AiServices:
record WeatherForecast(
String location,
String forecast,
int temperature) {}
class WeatherForecastService {
@Tool("Get the weather forecast for a location")
WeatherForecast getForecast(
@P("Location to get the forecast for") String location) {
if (location.equals("Paris")) {
return new WeatherForecast("Paris", "sunny", 20);
} else if (location.equals("London")) {
return new WeatherForecast("London", "rainy", 15);
} else if (location.equals("Tokyo")) {
return new WeatherForecast("Tokyo", "warm", 32);
} else {
return new WeatherForecast("Unknown", "unknown", 0);
}
}
}
interface WeatherAssistant {
String chat(String userMessage);
}
WeatherForecastService weatherForecastService =
new WeatherForecastService();
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.temperature(0.0)
.build();
WeatherAssistant weatherAssistant =
AiServices.builder(WeatherAssistant.class)
.chatModel(gemini)
.tools(weatherForecastService)
.build();
String tokyoWeather = weatherAssistant.chat(
"What is the weather forecast for Tokyo?");
System.out.println("Gemini> " + tokyoWeather);
// Gemini> The weather forecast for Tokyo is warm
// with a temperature of 32 degrees.
Tool Parameters Using $ref, $defs Or Raw JSON Schema
Tool parameters are usually described with the Gemini parameters field, which understands a fixed set of
schema keywords. Standard JSON Schema goes further than that: it can point one part of a document at another
with $ref and $defs, and it has keywords such as minimum and maximum that parameters has no place for.
When the tool parameters contain anything of that kind, LangChain4j sends them through parametersJsonSchema
instead, the Gemini field that takes plain JSON Schema, and the schema reaches the API unchanged. There is
nothing to configure and nothing to switch on:
JsonObjectSchema priceRange = JsonObjectSchema.builder()
.addNumberProperty("min")
.addNumberProperty("max")
.build();
ToolSpecification searchProducts = ToolSpecification.builder()
.name("search_products")
.description("Search the catalog")
.parameters(JsonObjectSchema.builder()
.definitions(Map.of("PriceRange", priceRange))
.addStringProperty("query")
// a reference to the definition above, resolved by Gemini
.addProperty("retail_price", JsonReferenceSchema.builder()
.reference("PriceRange")
.build())
// a fragment of JSON Schema, sent exactly as written
.addProperty("max_results", JsonRawSchema.from(
"{\"type\":\"integer\",\"minimum\":1,\"maximum\":50}"))
.required("query")
.build())
.build();
This is not limited to schemas you write by hand. It also covers tools LangChain4j builds for you: a @Tool
method whose parameter type refers to itself, and MCP tools whose schema uses $ref.
Response schemas are treated the same way, see Raw Response Schema.
Gemini rejects the $schema keyword. Documents coming out of a schema generator usually start with
"$schema": "https://json-schema.org/draft/2020-12/schema", so drop that line before passing the document
to JsonRawSchema, otherwise the request fails with a 400.
Structured Outputs
See more info on Structured Outputs here.
Type-safe data extraction from free form text
Large Language Models are great at extracting structured information out of unstructured text.
In the following example, we retrieve a type-safe WeatherForecast object from a weather forecast text, thanks to AiServices:
// A type-safe / strongly-typed object
// representing the weather forecast
record WeatherForecast(
@Description("minimum temperature")
Integer minTemperature,
@Description("maximum temperature")
Integer maxTemperature,
@Description("chances of rain")
boolean rain
) { }
// An interface contract, to interact with Gemini
interface WeatherForecastAssistant {
WeatherForecast extract(String forecast);
}
// Let's extract the data:
ChatModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(System.getenv("GEMINI_AI_KEY"))
.modelName("gemini-2.5-flash")
.supportedCapabilities(RESPONSE_FORMAT_JSON_SCHEMA) // this is required to enable structured outputs feature
.build();
WeatherForecastAssistant forecastAssistant =
AiServices.builder(WeatherForecastAssistant.class)
.chatModel(gemini)
.build();
WeatherForecast forecast = forecastAssistant.extract("""
Morning: The day dawns bright and clear in Osaka, with crisp
autumn air and sunny skies. Expect temperatures to hover
around 18°C (64°F) as you head out for your morning stroll
through Namba.
Afternoon: The sun continues to shine as the city buzzes with
activity. Temperatures climb to a comfortable 22°C (72°F).
Enjoy a leisurely lunch at one of Osaka's many outdoor cafes,
or take a boat ride on the Okawa River to soak in the beautiful
scenery.
Evening: As the day fades, expect clear skies and a slight chill
in the air. Temperatures drop to 15°C (59°F). A cozy dinner at a
traditional Izakaya will be the perfect way to end your day in
Osaka.
Overall: A beautiful autumn day in Osaka awaits, perfect for
exploring the city's vibrant streets, enjoying the local cuisine,
and soaking in the sights.
Don't forget: Pack a light jacket for the evening and wear
comfortable shoes for all the walking you'll be doing.
""");
Response Format / Response Schema
You can specify a ResponseFormat either when creating a GoogleAiGeminiChatModel or when calling it.
Especially, in cases of Json format, you can choose to define schema programmatically by creating the respective java objects or by providing raw json schema.