CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-dev-langchain4j--langchain4j-google-ai-gemini

LangChain4j integration for Google AI Gemini models providing chat, streaming, embeddings, image generation, and batch processing capabilities

Overview
Eval results
Files

response-metadata.mddocs/

Response Metadata

Response metadata classes providing detailed information about chat responses including token usage, grounding sources, search queries, and retrieval metadata. Essential for understanding model behavior, tracking costs, and accessing grounding information.

Capabilities

GoogleAiGeminiChatResponseMetadata

Extended chat response metadata providing Gemini-specific information including grounding metadata.

/**
 * Chat response metadata for Google AI Gemini.
 * Extends standard ChatResponseMetadata with grounding information.
 */
public class GoogleAiGeminiChatResponseMetadata {
    /**
     * Creates a new builder for response metadata.
     * @return Builder instance
     */
    public static Builder builder();

    /**
     * Gets the grounding metadata containing source information.
     * @return GroundingMetadata with grounding details (null if no grounding used)
     */
    public GroundingMetadata groundingMetadata();

    /**
     * Creates a builder from this metadata instance.
     * @return Builder populated with current values
     */
    public Builder toBuilder();

    /**
     * Checks equality with another object.
     * @param o Object to compare
     * @return True if equal
     */
    public boolean equals(Object o);

    /**
     * Returns hash code for this metadata.
     * @return Hash code integer
     */
    public int hashCode();

    /**
     * Returns string representation of this metadata.
     * @return String representation
     */
    public String toString();
}

GoogleAiGeminiChatResponseMetadata.Builder

Builder for constructing GoogleAiGeminiChatResponseMetadata instances.

/**
 * Builder for GoogleAiGeminiChatResponseMetadata.
 * Extends ChatResponseMetadata.Builder for standard metadata fields.
 */
public static class Builder extends ChatResponseMetadata.Builder<Builder> {
    /**
     * Sets the grounding metadata.
     * @param groundingMetadata GroundingMetadata instance
     * @return Builder instance for chaining
     */
    public Builder groundingMetadata(GroundingMetadata groundingMetadata);

    /**
     * Builds the GoogleAiGeminiChatResponseMetadata instance.
     * @return Configured GoogleAiGeminiChatResponseMetadata
     */
    public GoogleAiGeminiChatResponseMetadata build();
}

GroundingMetadata Record

Record containing detailed grounding information from various sources.

/**
 * Metadata about grounding sources used in response generation.
 * Includes web search, retrieval, and Google Maps grounding information.
 */
public record GroundingMetadata(
    /**
     * Chunks of grounding data from various sources.
     * Each chunk represents a piece of evidence from web, retrieval, or maps.
     */
    List<GroundingChunk> groundingChunks,

    /**
     * Support evidence linking generated content to grounding chunks.
     * Shows which parts of the response are grounded in which sources.
     */
    List<GroundingSupport> groundingSupports,

    /**
     * Web search queries executed for grounding.
     * List of search queries used to find grounding information.
     */
    List<String> webSearchQueries,

    /**
     * Search entry point information.
     * Contains rendered search widget or link for user access.
     */
    SearchEntryPoint searchEntryPoint,

    /**
     * Metadata about retrieval operations.
     * Information about retrieval-based grounding.
     */
    RetrievalMetadata retrievalMetadata,

    /**
     * Google Maps widget context token.
     * Token for embedding Google Maps widget with grounding locations.
     */
    String googleMapsWidgetContextToken
) {
    /**
     * Creates a new builder for grounding metadata.
     * @return Builder instance
     */
    public static Builder builder();
}

GroundingChunk Record

Sealed interface representing different types of grounding chunks.

/**
 * Represents a chunk of grounding data from a source.
 * Can be from web search, retrieval, or Google Maps.
 */
public sealed interface GroundingChunk
    permits GroundingChunk.Web, GroundingChunk.RetrievedContext, GroundingChunk.Maps {
}

/**
 * Grounding chunk from web search.
 */
public record Web(
    /**
     * URI of the web page.
     */
    String uri,

    /**
     * Title of the web page.
     */
    String title
) implements GroundingChunk {}

/**
 * Grounding chunk from retrieval system.
 */
public record RetrievedContext(
    /**
     * URI or identifier of the retrieved context.
     */
    String uri,

    /**
     * Title or description of the context.
     */
    String title,

    /**
     * Retrieved text content.
     */
    String text
) implements GroundingChunk {}

/**
 * Grounding chunk from Google Maps.
 */
public record Maps(
    /**
     * URI of the Maps place.
     */
    String uri,

    /**
     * Title/name of the place.
     */
    String title,

    /**
     * Text description of the place.
     */
    String text,

    /**
     * Place ID from Google Maps.
     */
    String placeId,

    /**
     * Answer sources from place reviews.
     */
    PlaceAnswerSources placeAnswerSources
) implements GroundingChunk {}

/**
 * Answer sources from Google Maps place reviews.
 */
public record PlaceAnswerSources(
    /**
     * List of review snippets.
     */
    List<ReviewSnippet> reviewSnippets
) {}

/**
 * Review snippet from Google Maps.
 */
public record ReviewSnippet(
    /**
     * Review ID.
     */
    String reviewId,

    /**
     * Google Maps URI for the review.
     */
    String googleMapsUri,

    /**
     * Title/excerpt of the review.
     */
    String title
) {}

GroundingSupport Record

Record linking generated content segments to grounding sources.

/**
 * Support evidence linking generated content to grounding chunks.
 * Shows which parts of the response are backed by which sources.
 */
public record GroundingSupport(
    /**
     * Indices of grounding chunks supporting this segment.
     */
    List<Integer> groundingChunkIndices,

    /**
     * Confidence scores for each grounding chunk (0.0 to 1.0).
     */
    List<Double> confidenceScores,

    /**
     * Segment of generated text.
     */
    Segment segment
) {}

Segment Record

Record representing a segment of text with start and end indices.

/**
 * Text segment with indices in the generated response.
 */
public record Segment(
    /**
     * Part index in the multi-part response (optional).
     */
    Integer partIndex,

    /**
     * Start index in the response text.
     */
    Integer startIndex,

    /**
     * End index in the response text.
     */
    Integer endIndex,

    /**
     * The actual text content of the segment.
     */
    String text
) {}

SearchEntryPoint Record

Record containing search entry point information.

/**
 * Search entry point for user access to grounding sources.
 * Contains rendered HTML content and SDK blob for search widget.
 */
public record SearchEntryPoint(
    /**
     * Rendered HTML content for search entry point widget.
     */
    String renderedContent,

    /**
     * SDK blob data for search widget integration.
     */
    String sdkBlob
) {}

RetrievalMetadata Record

Record containing metadata about retrieval operations.

/**
 * Metadata about retrieval-based grounding.
 * Contains Google Search dynamic retrieval score.
 */
public record RetrievalMetadata(
    /**
     * Score indicating Google Search dynamic retrieval quality (0.0 to 1.0).
     */
    Double googleSearchDynamicRetrievalScore
) {}

Usage Examples

Accessing Response Metadata

import dev.langchain4j.model.googleai.GoogleAiGeminiChatModel;
import dev.langchain4j.model.googleai.GoogleAiGeminiChatResponseMetadata;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.data.message.AiMessage;

GoogleAiGeminiChatModel model = GoogleAiGeminiChatModel.builder()
    .apiKey(System.getenv("GOOGLE_AI_API_KEY"))
    .modelName("gemini-2.5-pro")
    .allowGoogleSearch(true)
    .build();

Response<AiMessage> response = model.generate("What are the latest developments in AI?");

// Access standard metadata
System.out.println("Token usage: " + response.tokenUsage());
System.out.println("Finish reason: " + response.finishReason());

// Access Gemini-specific metadata
if (response.metadata() instanceof GoogleAiGeminiChatResponseMetadata geminiMetadata) {
    GroundingMetadata grounding = geminiMetadata.groundingMetadata();
    if (grounding != null) {
        System.out.println("Response includes grounding information");
    }
}

Working with Grounding Metadata

GoogleAiGeminiChatModel model = GoogleAiGeminiChatModel.builder()
    .apiKey(System.getenv("GOOGLE_AI_API_KEY"))
    .modelName("gemini-2.5-pro")
    .allowGoogleSearch(true)
    .build();

Response<AiMessage> response = model.generate("What is the current price of gold?");

if (response.metadata() instanceof GoogleAiGeminiChatResponseMetadata metadata) {
    GroundingMetadata grounding = metadata.groundingMetadata();

    if (grounding != null) {
        // Check web search queries
        List<String> queries = grounding.webSearchQueries();
        if (queries != null && !queries.isEmpty()) {
            System.out.println("Search queries used:");
            queries.forEach(q -> System.out.println("  - " + q));
        }

        // Check grounding chunks
        List<GroundingChunk> chunks = grounding.groundingChunks();
        if (chunks != null && !chunks.isEmpty()) {
            System.out.println("\nGrounding sources (" + chunks.size() + "):");
            for (GroundingChunk chunk : chunks) {
                if (chunk instanceof GroundingChunk.Web web) {
                    System.out.println("  - " + web.title() + " (" + web.uri() + ")");
                }
            }
        }
    }
}

Processing Web Grounding Sources

GoogleAiGeminiChatModel model = GoogleAiGeminiChatModel.builder()
    .apiKey(System.getenv("GOOGLE_AI_API_KEY"))
    .modelName("gemini-2.5-flash")
    .allowGoogleSearch(true)
    .build();

Response<AiMessage> response = model.generate("Explain recent breakthroughs in quantum computing");

if (response.metadata() instanceof GoogleAiGeminiChatResponseMetadata metadata) {
    GroundingMetadata grounding = metadata.groundingMetadata();

    if (grounding != null && grounding.groundingChunks() != null) {
        List<GroundingChunk.Web> webSources = grounding.groundingChunks().stream()
            .filter(chunk -> chunk instanceof GroundingChunk.Web)
            .map(chunk -> (GroundingChunk.Web) chunk)
            .toList();

        System.out.println("Web sources used:");
        for (GroundingChunk.Web source : webSources) {
            System.out.println("Title: " + source.title());
            System.out.println("URL: " + source.uri());
            System.out.println();
        }
    }
}

Analyzing Grounding Support

GoogleAiGeminiChatModel model = GoogleAiGeminiChatModel.builder()
    .apiKey(System.getenv("GOOGLE_AI_API_KEY"))
    .modelName("gemini-2.5-pro")
    .allowGoogleSearch(true)
    .build();

Response<AiMessage> response = model.generate("What are the health benefits of green tea?");

if (response.metadata() instanceof GoogleAiGeminiChatResponseMetadata metadata) {
    GroundingMetadata grounding = metadata.groundingMetadata();

    if (grounding != null && grounding.groundingSupports() != null) {
        System.out.println("Grounding support analysis:");

        for (GroundingSupport support : grounding.groundingSupports()) {
            Segment segment = support.segment();
            System.out.println("\nText: \"" + segment.text() + "\"");
            System.out.println("Position: " + segment.startIndex() + "-" + segment.endIndex());
            System.out.println("Supported by " + support.groundingChunkIndices().size() + " sources");

            // Show confidence scores
            List<Double> scores = support.confidenceScores();
            if (scores != null && !scores.isEmpty()) {
                System.out.print("Confidence: ");
                scores.forEach(score -> System.out.printf("%.2f ", score));
                System.out.println();
            }
        }
    }
}

Search Entry Point

GoogleAiGeminiChatModel model = GoogleAiGeminiChatModel.builder()
    .apiKey(System.getenv("GOOGLE_AI_API_KEY"))
    .modelName("gemini-2.5-pro")
    .allowGoogleSearch(true)
    .build();

Response<AiMessage> response = model.generate("Find restaurants near Times Square");

if (response.metadata() instanceof GoogleAiGeminiChatResponseMetadata metadata) {
    GroundingMetadata grounding = metadata.groundingMetadata();

    if (grounding != null) {
        SearchEntryPoint entryPoint = grounding.searchEntryPoint();

        if (entryPoint != null) {
            System.out.println("Search entry point available:");

            if (entryPoint.renderedContent() != null) {
                System.out.println("Rendered widget HTML available");
                // Can embed in web page: entryPoint.renderedContent()
            }

            if (entryPoint.sdkBlob() != null) {
                System.out.println("SDK blob available for search widget");
            }
        }
    }
}

Google Maps Grounding

GoogleAiGeminiChatModel model = GoogleAiGeminiChatModel.builder()
    .apiKey(System.getenv("GOOGLE_AI_API_KEY"))
    .modelName("gemini-2.5-pro")
    .allowGoogleMaps(true)
    .retrieveGoogleMapsWidgetToken(true)
    .build();

Response<AiMessage> response = model.generate("Tell me about the Eiffel Tower location");

if (response.metadata() instanceof GoogleAiGeminiChatResponseMetadata metadata) {
    GroundingMetadata grounding = metadata.groundingMetadata();

    if (grounding != null) {
        // Check for Maps grounding chunks
        if (grounding.groundingChunks() != null) {
            List<GroundingChunk.Maps> mapChunks = grounding.groundingChunks().stream()
                .filter(chunk -> chunk instanceof GroundingChunk.Maps)
                .map(chunk -> (GroundingChunk.Maps) chunk)
                .toList();

            System.out.println("Places found:");
            for (GroundingChunk.Maps place : mapChunks) {
                System.out.println("Name: " + place.title());
                System.out.println("URI: " + place.uri());
                System.out.println("Description: " + place.text());
                System.out.println("Place ID: " + place.placeId());

                // Check for review snippets
                if (place.placeAnswerSources() != null &&
                    place.placeAnswerSources().reviewSnippets() != null) {
                    System.out.println("Reviews: " +
                        place.placeAnswerSources().reviewSnippets().size());
                }
                System.out.println();
            }
        }

        // Get Maps widget token
        String widgetToken = grounding.googleMapsWidgetContextToken();
        if (widgetToken != null) {
            System.out.println("Maps widget token: " + widgetToken);
            // Use token to embed interactive map
        }
    }
}

Retrieval Metadata

GoogleAiGeminiChatModel model = GoogleAiGeminiChatModel.builder()
    .apiKey(System.getenv("GOOGLE_AI_API_KEY"))
    .modelName("gemini-2.5-pro")
    .build();

Response<AiMessage> response = model.generate("Your query here");

if (response.metadata() instanceof GoogleAiGeminiChatResponseMetadata metadata) {
    GroundingMetadata grounding = metadata.groundingMetadata();

    if (grounding != null) {
        RetrievalMetadata retrieval = grounding.retrievalMetadata();

        if (retrieval != null) {
            System.out.println("Retrieval information:");
            Double score = retrieval.googleSearchDynamicRetrievalScore();
            if (score != null) {
                System.out.printf("Google Search dynamic retrieval score: %.2f%n", score);
            }
        }
    }
}

Retrieved Context Chunks

GoogleAiGeminiChatModel model = GoogleAiGeminiChatModel.builder()
    .apiKey(System.getenv("GOOGLE_AI_API_KEY"))
    .modelName("gemini-2.5-pro")
    .build();

Response<AiMessage> response = model.generate("Query requiring retrieval");

if (response.metadata() instanceof GoogleAiGeminiChatResponseMetadata metadata) {
    GroundingMetadata grounding = metadata.groundingMetadata();

    if (grounding != null && grounding.groundingChunks() != null) {
        List<GroundingChunk.RetrievedContext> retrievedChunks = grounding.groundingChunks().stream()
            .filter(chunk -> chunk instanceof GroundingChunk.RetrievedContext)
            .map(chunk -> (GroundingChunk.RetrievedContext) chunk)
            .toList();

        System.out.println("Retrieved contexts:");
        for (GroundingChunk.RetrievedContext context : retrievedChunks) {
            System.out.println("\nTitle: " + context.title());
            System.out.println("URI: " + context.uri());
            System.out.println("Text: " + context.text());
        }
    }
}

Complete Grounding Analysis

public void analyzeGrounding(Response<AiMessage> response) {
    if (!(response.metadata() instanceof GoogleAiGeminiChatResponseMetadata metadata)) {
        System.out.println("No Gemini metadata available");
        return;
    }

    GroundingMetadata grounding = metadata.groundingMetadata();
    if (grounding == null) {
        System.out.println("No grounding information");
        return;
    }

    System.out.println("=== Grounding Analysis ===\n");

    // Web search queries
    if (grounding.webSearchQueries() != null) {
        System.out.println("Search queries (" + grounding.webSearchQueries().size() + "):");
        grounding.webSearchQueries().forEach(q -> System.out.println("  - " + q));
        System.out.println();
    }

    // Grounding chunks by type
    if (grounding.groundingChunks() != null) {
        long webCount = grounding.groundingChunks().stream()
            .filter(c -> c instanceof GroundingChunk.Web)
            .count();
        long retrievalCount = grounding.groundingChunks().stream()
            .filter(c -> c instanceof GroundingChunk.RetrievedContext)
            .count();
        long mapsCount = grounding.groundingChunks().stream()
            .filter(c -> c instanceof GroundingChunk.Maps)
            .count();

        System.out.println("Grounding sources:");
        System.out.println("  Web: " + webCount);
        System.out.println("  Retrieval: " + retrievalCount);
        System.out.println("  Maps: " + mapsCount);
        System.out.println();
    }

    // Grounding support
    if (grounding.groundingSupports() != null) {
        System.out.println("Grounding support: " +
            grounding.groundingSupports().size() + " segments");
        double avgConfidence = grounding.groundingSupports().stream()
            .flatMap(s -> s.confidenceScores() != null ?
                s.confidenceScores().stream() : Stream.empty())
            .mapToDouble(Double::doubleValue)
            .average()
            .orElse(0.0);
        System.out.printf("Average confidence: %.2f%n", avgConfidence);
    }
}

// Usage
Response<AiMessage> response = model.generate("Your query");
analyzeGrounding(response);

Extracting Citation Information

public List<String> extractCitations(Response<AiMessage> response) {
    if (!(response.metadata() instanceof GoogleAiGeminiChatResponseMetadata metadata)) {
        return Collections.emptyList();
    }

    GroundingMetadata grounding = metadata.groundingMetadata();
    if (grounding == null || grounding.groundingChunks() == null) {
        return Collections.emptyList();
    }

    return grounding.groundingChunks().stream()
        .filter(chunk -> chunk instanceof GroundingChunk.Web)
        .map(chunk -> (GroundingChunk.Web) chunk)
        .map(web -> web.title() + " - " + web.uri())
        .toList();
}

// Usage
Response<AiMessage> response = model.generate("Explain climate change");
List<String> citations = extractCitations(response);

System.out.println("Response: " + response.content().text());
System.out.println("\nSources:");
citations.forEach(citation -> System.out.println("  " + citation));

Building Response with Citations

public String formatResponseWithCitations(Response<AiMessage> response) {
    StringBuilder formatted = new StringBuilder();
    formatted.append(response.content().text());

    if (response.metadata() instanceof GoogleAiGeminiChatResponseMetadata metadata) {
        GroundingMetadata grounding = metadata.groundingMetadata();

        if (grounding != null && grounding.groundingChunks() != null) {
            List<GroundingChunk.Web> webSources = grounding.groundingChunks().stream()
                .filter(c -> c instanceof GroundingChunk.Web)
                .map(c -> (GroundingChunk.Web) c)
                .toList();

            if (!webSources.isEmpty()) {
                formatted.append("\n\n--- Sources ---\n");
                for (int i = 0; i < webSources.size(); i++) {
                    GroundingChunk.Web source = webSources.get(i);
                    formatted.append(String.format("[%d] %s\n    %s\n",
                        i + 1, source.title(), source.uri()));
                }
            }
        }
    }

    return formatted.toString();
}

// Usage
Response<AiMessage> response = model.generate("What is machine learning?");
String formattedResponse = formatResponseWithCitations(response);
System.out.println(formattedResponse);

Token Usage Tracking

import dev.langchain4j.model.output.TokenUsage;

GoogleAiGeminiChatModel model = GoogleAiGeminiChatModel.builder()
    .apiKey(System.getenv("GOOGLE_AI_API_KEY"))
    .modelName("gemini-2.5-flash")
    .build();

Response<AiMessage> response = model.generate("Explain neural networks");

// Access token usage from metadata
TokenUsage tokenUsage = response.tokenUsage();
if (tokenUsage != null) {
    System.out.println("Input tokens: " + tokenUsage.inputTokenCount());
    System.out.println("Output tokens: " + tokenUsage.outputTokenCount());
    System.out.println("Total tokens: " + tokenUsage.totalTokenCount());

    // Calculate cost (example rates)
    double inputCost = tokenUsage.inputTokenCount() * 0.000075 / 1000;
    double outputCost = tokenUsage.outputTokenCount() * 0.0003 / 1000;
    System.out.printf("Estimated cost: $%.6f%n", inputCost + outputCost);
}

Confidence Score Analysis

public void analyzeConfidence(Response<AiMessage> response) {
    if (!(response.metadata() instanceof GoogleAiGeminiChatResponseMetadata metadata)) {
        return;
    }

    GroundingMetadata grounding = metadata.groundingMetadata();
    if (grounding == null || grounding.groundingSupports() == null) {
        return;
    }

    System.out.println("Confidence analysis:");

    for (GroundingSupport support : grounding.groundingSupports()) {
        if (support.confidenceScores() == null || support.confidenceScores().isEmpty()) {
            continue;
        }

        double avgConfidence = support.confidenceScores().stream()
            .mapToDouble(Double::doubleValue)
            .average()
            .orElse(0.0);

        String confidenceLevel = avgConfidence > 0.8 ? "HIGH" :
                                avgConfidence > 0.5 ? "MEDIUM" : "LOW";

        System.out.printf("\nSegment: \"%s\"%n", support.segment().text());
        System.out.printf("Confidence: %.2f (%s)%n", avgConfidence, confidenceLevel);
    }
}

Use Cases

Citation Generation

Use grounding metadata to automatically generate citations for responses.

Fact Verification

Analyze grounding support and confidence scores to assess factual accuracy.

Source Transparency

Display grounding sources to users for transparency and verification.

Cost Tracking

Monitor token usage across requests to manage API costs.

Quality Assurance

Evaluate confidence scores to identify responses that may need review.

Location Services

Extract Google Maps information for location-based applications.

Search Integration

Use search entry points to provide users with direct access to search results.

Best Practices

  1. Null checks: Always check if metadata and grounding information are present
  2. Type checking: Use pattern matching or instanceof for safe type casting
  3. Citation display: Show sources to users when grounding is used
  4. Confidence thresholds: Set minimum confidence levels for accepting responses
  5. Token monitoring: Track token usage to manage costs and optimize prompts
  6. Error handling: Handle cases where expected metadata fields are null
  7. User transparency: Inform users when responses are grounded in external sources

Install with Tessl CLI

npx tessl i tessl/maven-dev-langchain4j--langchain4j-google-ai-gemini@1.11.0

docs

advanced-configuration.md

batch-processing.md

chat-streaming.md

chat-synchronous.md

configuration.md

embeddings.md

file-management.md

images.md

index.md

model-catalog.md

response-metadata.md

token-counting.md

tile.json