Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ai-logic/firebase-ai/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased

- [feature] Added support for [Maps Grounding](https://ai.google.dev/gemini-api/docs/maps-grounding) (#7950)
- [fixed] Fixed an issue causing network timeouts to throw the incorrect exception type, instead of
`RequestTimeoutException` (#7966)
- [fixed] Fixed an issue causing the SDK to throw an exception if an unknown message was received
Expand Down
61 changes: 59 additions & 2 deletions ai-logic/firebase-ai/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,20 @@ package com.google.firebase.ai.type {
method public com.google.firebase.ai.type.GenerativeBackend vertexAI(String location = "us-central1");
}

public final class GoogleMaps {
ctor public GoogleMaps();
}

public final class GoogleMapsGroundingChunk {
ctor public GoogleMapsGroundingChunk(String? uri, String? title, String? placeId);
method public String? getPlaceId();
method public String? getTitle();
method public String? getUri();
property public final String? placeId;
property public final String? title;
property public final String? uri;
}

public final class GoogleSearch {
ctor public GoogleSearch();
}
Expand All @@ -650,8 +664,12 @@ package com.google.firebase.ai.type {
}

public final class GroundingChunk {
ctor public GroundingChunk(com.google.firebase.ai.type.WebGroundingChunk? web);
ctor public GroundingChunk();
ctor public GroundingChunk(com.google.firebase.ai.type.WebGroundingChunk? web = null);
ctor public GroundingChunk(com.google.firebase.ai.type.WebGroundingChunk? web = null, com.google.firebase.ai.type.GoogleMapsGroundingChunk? maps = null);
method public com.google.firebase.ai.type.GoogleMapsGroundingChunk? getMaps();
method public com.google.firebase.ai.type.WebGroundingChunk? getWeb();
property public final com.google.firebase.ai.type.GoogleMapsGroundingChunk? maps;
property public final com.google.firebase.ai.type.WebGroundingChunk? web;
}

Expand Down Expand Up @@ -1167,6 +1185,17 @@ package com.google.firebase.ai.type {
method public com.google.firebase.ai.type.JsonSchema<java.lang.String> string(String? description = null, boolean nullable = false, com.google.firebase.ai.type.StringFormat? format = null, String? title = null);
}

public final class LatLng {
ctor public LatLng(double latitude, double longitude);
method public double component1();
method public double component2();
method public com.google.firebase.ai.type.LatLng copy(double latitude, double longitude);
method public double getLatitude();
method public double getLongitude();
property public final double latitude;
property public final double longitude;
}

@com.google.firebase.ai.type.PublicPreviewAPI public final class LiveAudioConversationConfig {
field public static final com.google.firebase.ai.type.LiveAudioConversationConfig.Companion Companion;
}
Expand Down Expand Up @@ -1376,6 +1405,28 @@ package com.google.firebase.ai.type {
property public final com.google.firebase.ai.type.GenerateContentResponse response;
}

public final class RetrievalConfig {
method public static com.google.firebase.ai.type.RetrievalConfig.Builder builder();
field public static final com.google.firebase.ai.type.RetrievalConfig.Companion Companion;
}

public static final class RetrievalConfig.Builder {
ctor public RetrievalConfig.Builder();
method public com.google.firebase.ai.type.RetrievalConfig build();
method public com.google.firebase.ai.type.RetrievalConfig.Builder setLanguageCode(String? languageCode);
method public com.google.firebase.ai.type.RetrievalConfig.Builder setLatLng(com.google.firebase.ai.type.LatLng? latLng);
field public String? languageCode;
field public com.google.firebase.ai.type.LatLng? latLng;
}

public static final class RetrievalConfig.Companion {
method public com.google.firebase.ai.type.RetrievalConfig.Builder builder();
}

public final class RetrievalConfigKt {
method public static com.google.firebase.ai.type.RetrievalConfig retrievalConfig(kotlin.jvm.functions.Function1<? super com.google.firebase.ai.type.RetrievalConfig.Builder,kotlin.Unit> init);
}

public final class SafetyRating {
method public Boolean? getBlocked();
method public com.google.firebase.ai.type.HarmCategory getCategory();
Expand Down Expand Up @@ -1618,6 +1669,8 @@ package com.google.firebase.ai.type {
method public static com.google.firebase.ai.type.Tool codeExecution();
method public static com.google.firebase.ai.type.Tool functionDeclarations(java.util.List<com.google.firebase.ai.type.FunctionDeclaration> functionDeclarations);
method public static com.google.firebase.ai.type.Tool functionDeclarations(java.util.List<com.google.firebase.ai.type.FunctionDeclaration>? functionDeclarations = null, java.util.List<? extends com.google.firebase.ai.type.AutoFunctionDeclaration<? extends java.lang.Object?,? extends java.lang.Object?>>? autoFunctionDeclarations);
method public static com.google.firebase.ai.type.Tool googleMaps();
method public static com.google.firebase.ai.type.Tool googleMaps(com.google.firebase.ai.type.GoogleMaps googleMaps = com.google.firebase.ai.type.GoogleMaps());
method public static com.google.firebase.ai.type.Tool googleSearch();
method public static com.google.firebase.ai.type.Tool googleSearch(com.google.firebase.ai.type.GoogleSearch googleSearch = com.google.firebase.ai.type.GoogleSearch());
method public static com.google.firebase.ai.type.Tool urlContext();
Expand All @@ -1629,14 +1682,18 @@ package com.google.firebase.ai.type {
method public com.google.firebase.ai.type.Tool codeExecution();
method public com.google.firebase.ai.type.Tool functionDeclarations(java.util.List<com.google.firebase.ai.type.FunctionDeclaration> functionDeclarations);
method public com.google.firebase.ai.type.Tool functionDeclarations(java.util.List<com.google.firebase.ai.type.FunctionDeclaration>? functionDeclarations = null, java.util.List<? extends com.google.firebase.ai.type.AutoFunctionDeclaration<? extends java.lang.Object?,? extends java.lang.Object?>>? autoFunctionDeclarations);
method public com.google.firebase.ai.type.Tool googleMaps();
method public com.google.firebase.ai.type.Tool googleMaps(com.google.firebase.ai.type.GoogleMaps googleMaps = com.google.firebase.ai.type.GoogleMaps());
method public com.google.firebase.ai.type.Tool googleSearch();
method public com.google.firebase.ai.type.Tool googleSearch(com.google.firebase.ai.type.GoogleSearch googleSearch = com.google.firebase.ai.type.GoogleSearch());
method public com.google.firebase.ai.type.Tool urlContext();
method public com.google.firebase.ai.type.Tool urlContext(com.google.firebase.ai.type.UrlContext urlContext = com.google.firebase.ai.type.UrlContext());
}

public final class ToolConfig {
ctor public ToolConfig(com.google.firebase.ai.type.FunctionCallingConfig? functionCallingConfig);
ctor public ToolConfig();
ctor public ToolConfig(com.google.firebase.ai.type.FunctionCallingConfig? functionCallingConfig = null);
ctor public ToolConfig(com.google.firebase.ai.type.FunctionCallingConfig? functionCallingConfig = null, com.google.firebase.ai.type.RetrievalConfig? retrievalConfig = null);
}

public final class Transcription {
Expand Down
2 changes: 1 addition & 1 deletion ai-logic/firebase-ai/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.

version=17.10.2
version=17.11.0
latestReleasedVersion=17.10.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.firebase.ai

import com.google.firebase.ai.AIModels.Companion.app
import com.google.firebase.ai.type.GenerativeBackend
import com.google.firebase.ai.type.LatLng
import com.google.firebase.ai.type.RetrievalConfig
import com.google.firebase.ai.type.Tool
import com.google.firebase.ai.type.ToolConfig
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.runBlocking
import org.junit.Test

class GroundingTests {

@Test
fun groundingTests_canRecognizeAreas(): Unit = runBlocking {
val model = setupModel(config = ToolConfig())
val response = model.generateContent("Where is a good place to grab a coffee near Alameda, CA?")

response.candidates.isEmpty() shouldBe false
response.candidates[0].groundingMetadata?.groundingChunks?.any { it.maps != null } shouldBe true
}

@Test
fun groundingTests_canRecognizeLatLng(): Unit = runBlocking {
val model =
setupModel(
config =
ToolConfig(
retrievalConfig =
RetrievalConfig(
latLng = LatLng(latitude = 30.2672, longitude = -97.7431),
languageCode = "en_US",
),
)
)
val response = model.generateContent("Find bookstores in my area.")

response.candidates.isEmpty() shouldBe false
response.candidates[0].groundingMetadata?.groundingChunks?.any { it.maps != null } shouldBe true
}

companion object {

@JvmStatic
fun setupModel(config: ToolConfig): GenerativeModel {
val model =
FirebaseAI.getInstance(app(), GenerativeBackend.vertexAI())
.generativeModel(
modelName = "gemini-2.5-flash",
toolConfig = config,
tools = listOf(Tool.googleMaps()),
)
return model
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,12 @@ public class FinishReason private constructor(public val name: String, public va
* Vertex AI Gemini API (see [Service Terms](https://cloud.google.com/terms/service-terms) section
* within the Service Specific Terms).
*
* If using Grounding with Google Maps, you are required to comply with the "Grounding with Google
* Maps" usage requirements for your chosen API provider:
* [Gemini Developer API](https://ai.google.dev/gemini-api/terms#grounding-with-google-maps) or
* Vertex AI Gemini API (see [Service Terms](https://cloud.google.com/terms/service-terms) section
* within the Service Specific Terms).
*
* @property webSearchQueries The list of web search queries that the model performed to gather the
* grounding information. These can be used to allow users to explore the search results themselves.
* @property searchEntryPoint Google Search entry point for web searches. This contains an HTML/CSS
Expand Down Expand Up @@ -408,15 +414,40 @@ public class SearchEntryPoint(
* Represents a chunk of retrieved data that supports a claim in the model's response.
*
* @property web Contains details if the grounding chunk is from a web source.
* @property maps Contains details if the grounding chunk is from a Google Maps source.
*/
public class GroundingChunk(
public val web: WebGroundingChunk?,
public class GroundingChunk
@JvmOverloads
constructor(
public val web: WebGroundingChunk? = null,
public val maps: GoogleMapsGroundingChunk? = null,
) {

@Serializable
internal data class Internal(
val web: WebGroundingChunk.Internal?,
val maps: GoogleMapsGroundingChunk.Internal?,
) {
internal fun toPublic() = GroundingChunk(web = web?.toPublic())
internal fun toPublic() = GroundingChunk(web = web?.toPublic(), maps?.toPublic())
}
}

/**
* A grounding chunk from Google Maps.
*
* @property uri The URI of the place.
* @property title The title of the place.
* @property placeId This Place's resource name, in `places/{place_id}` format. This can be used to
* look up the place using the Google Maps API.
*/
public class GoogleMapsGroundingChunk(
public val uri: String?,
public val title: String?,
public val placeId: String?,
) {
@Serializable
internal data class Internal(val uri: String?, val title: String?, val placeId: String?) {
fun toPublic() = GoogleMapsGroundingChunk(uri, title, placeId)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.firebase.ai.type

import kotlinx.serialization.Serializable

/**
* A tool that allows a Gemini model to connect to Google Maps to access and incorporate
* location-based information into its responses.
*
* Important: If using Grounding with Google Maps, you are required to comply with the "Grounding
* with Google Maps" usage requirements for your chosen API provider: {@link
* https://ai.google.dev/gemini-api/terms#grounding-with-google-maps | Gemini Developer API} or
* Vertex AI Gemini API (see {@link https://cloud.google.com/terms/service-terms | Service Terms}
* section within the Service Specific Terms).
*/
public class GoogleMaps {
@Serializable internal class Internal()

internal fun toInternal() = Internal()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.firebase.ai.type

import kotlinx.serialization.Serializable

/**
* An object that represents a latitude/longitude pair.
*
* @param latitude The latitude in degrees. It must be in the range [-90.0, +90.0].
* @param longitude The longitude in degrees. It must be in the range [-180.0, +180.0].
*/
public data class LatLng(val latitude: Double, val longitude: Double) {
@Serializable
internal data class Internal(val latitude: Double, val longitude: Double) {
fun toPublic() = LatLng(latitude, longitude)
}

internal fun toInternal() = Internal(latitude, longitude)
}
Loading
Loading