Skip to content

Commit cc4edd6

Browse files
authored
Implement Maps Grounding (#7983)
This reintroduces maps grounding for M180. Not to be merged until after the M179 code freeze next week.
1 parent 4f0f820 commit cc4edd6

12 files changed

Lines changed: 388 additions & 17 deletions

File tree

ai-logic/firebase-ai/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
# Unreleased
22

3+
- [feature] Added support for [Maps Grounding](https://ai.google.dev/gemini-api/docs/maps-grounding) (#7950)
4+
35
- [feature] Added the `turnComplete` argument to multiple `LiveSession.send()` methods.
6+
47
- [deprecated] All Imagen models are deprecated and will shut down as early as June 2026.
58
As a replacement, you can [migrate your apps to use Gemini Image models (the "Nano Banana" models)](https://firebase.google.com/docs/ai-logic/imagen-models-migration).
9+
610
- [feature] Added support for Chat interactions using server prompt templates (#7986)
11+
712
- [feature] Added support for function calling in Chat interactions using server prompt templates (#8004)
13+
814
- [fixed] Fixed an issue causing network timeouts to throw the incorrect exception type, instead of
915
`RequestTimeoutException` (#7966)
16+
1017
- [fixed] Fixed missing `toString()` implementation for `InferenceSource` (#7970)
18+
1119
- [fixed] Fixed an issue causing the SDK to throw an exception if an unknown message was received
1220
from the LiveAPI model, instead of ignoring it (#7975)
1321

ai-logic/firebase-ai/api.txt

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,20 @@ package com.google.firebase.ai.type {
649649
method public com.google.firebase.ai.type.GenerativeBackend vertexAI(String location = "us-central1");
650650
}
651651

652+
public final class GoogleMaps {
653+
ctor public GoogleMaps();
654+
}
655+
656+
public final class GoogleMapsGroundingChunk {
657+
ctor public GoogleMapsGroundingChunk(String? uri, String? title, String? placeId);
658+
method public String? getPlaceId();
659+
method public String? getTitle();
660+
method public String? getUri();
661+
property public final String? placeId;
662+
property public final String? title;
663+
property public final String? uri;
664+
}
665+
652666
public final class GoogleSearch {
653667
ctor public GoogleSearch();
654668
}
@@ -662,8 +676,12 @@ package com.google.firebase.ai.type {
662676
}
663677

664678
public final class GroundingChunk {
665-
ctor public GroundingChunk(com.google.firebase.ai.type.WebGroundingChunk? web);
679+
ctor public GroundingChunk();
680+
ctor public GroundingChunk(com.google.firebase.ai.type.WebGroundingChunk? web = null);
681+
ctor public GroundingChunk(com.google.firebase.ai.type.WebGroundingChunk? web = null, com.google.firebase.ai.type.GoogleMapsGroundingChunk? maps = null);
682+
method public com.google.firebase.ai.type.GoogleMapsGroundingChunk? getMaps();
666683
method public com.google.firebase.ai.type.WebGroundingChunk? getWeb();
684+
property public final com.google.firebase.ai.type.GoogleMapsGroundingChunk? maps;
667685
property public final com.google.firebase.ai.type.WebGroundingChunk? web;
668686
}
669687

@@ -1179,6 +1197,17 @@ package com.google.firebase.ai.type {
11791197
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);
11801198
}
11811199

1200+
public final class LatLng {
1201+
ctor public LatLng(double latitude, double longitude);
1202+
method public double component1();
1203+
method public double component2();
1204+
method public com.google.firebase.ai.type.LatLng copy(double latitude, double longitude);
1205+
method public double getLatitude();
1206+
method public double getLongitude();
1207+
property public final double latitude;
1208+
property public final double longitude;
1209+
}
1210+
11821211
@com.google.firebase.ai.type.PublicPreviewAPI public final class LiveAudioConversationConfig {
11831212
field public static final com.google.firebase.ai.type.LiveAudioConversationConfig.Companion Companion;
11841213
}
@@ -1390,6 +1419,28 @@ package com.google.firebase.ai.type {
13901419
property public final com.google.firebase.ai.type.GenerateContentResponse response;
13911420
}
13921421

1422+
public final class RetrievalConfig {
1423+
method public static com.google.firebase.ai.type.RetrievalConfig.Builder builder();
1424+
field public static final com.google.firebase.ai.type.RetrievalConfig.Companion Companion;
1425+
}
1426+
1427+
public static final class RetrievalConfig.Builder {
1428+
ctor public RetrievalConfig.Builder();
1429+
method public com.google.firebase.ai.type.RetrievalConfig build();
1430+
method public com.google.firebase.ai.type.RetrievalConfig.Builder setLanguageCode(String? languageCode);
1431+
method public com.google.firebase.ai.type.RetrievalConfig.Builder setLatLng(com.google.firebase.ai.type.LatLng? latLng);
1432+
field public String? languageCode;
1433+
field public com.google.firebase.ai.type.LatLng? latLng;
1434+
}
1435+
1436+
public static final class RetrievalConfig.Companion {
1437+
method public com.google.firebase.ai.type.RetrievalConfig.Builder builder();
1438+
}
1439+
1440+
public final class RetrievalConfigKt {
1441+
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);
1442+
}
1443+
13931444
public final class SafetyRating {
13941445
method public Boolean? getBlocked();
13951446
method public com.google.firebase.ai.type.HarmCategory getCategory();
@@ -1671,6 +1722,8 @@ package com.google.firebase.ai.type {
16711722
method public static com.google.firebase.ai.type.Tool codeExecution();
16721723
method public static com.google.firebase.ai.type.Tool functionDeclarations(java.util.List<com.google.firebase.ai.type.FunctionDeclaration> functionDeclarations);
16731724
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);
1725+
method public static com.google.firebase.ai.type.Tool googleMaps();
1726+
method public static com.google.firebase.ai.type.Tool googleMaps(com.google.firebase.ai.type.GoogleMaps googleMaps = com.google.firebase.ai.type.GoogleMaps());
16741727
method public static com.google.firebase.ai.type.Tool googleSearch();
16751728
method public static com.google.firebase.ai.type.Tool googleSearch(com.google.firebase.ai.type.GoogleSearch googleSearch = com.google.firebase.ai.type.GoogleSearch());
16761729
method public static com.google.firebase.ai.type.Tool urlContext();
@@ -1682,14 +1735,18 @@ package com.google.firebase.ai.type {
16821735
method public com.google.firebase.ai.type.Tool codeExecution();
16831736
method public com.google.firebase.ai.type.Tool functionDeclarations(java.util.List<com.google.firebase.ai.type.FunctionDeclaration> functionDeclarations);
16841737
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);
1738+
method public com.google.firebase.ai.type.Tool googleMaps();
1739+
method public com.google.firebase.ai.type.Tool googleMaps(com.google.firebase.ai.type.GoogleMaps googleMaps = com.google.firebase.ai.type.GoogleMaps());
16851740
method public com.google.firebase.ai.type.Tool googleSearch();
16861741
method public com.google.firebase.ai.type.Tool googleSearch(com.google.firebase.ai.type.GoogleSearch googleSearch = com.google.firebase.ai.type.GoogleSearch());
16871742
method public com.google.firebase.ai.type.Tool urlContext();
16881743
method public com.google.firebase.ai.type.Tool urlContext(com.google.firebase.ai.type.UrlContext urlContext = com.google.firebase.ai.type.UrlContext());
16891744
}
16901745

16911746
public final class ToolConfig {
1692-
ctor public ToolConfig(com.google.firebase.ai.type.FunctionCallingConfig? functionCallingConfig);
1747+
ctor public ToolConfig();
1748+
ctor public ToolConfig(com.google.firebase.ai.type.FunctionCallingConfig? functionCallingConfig = null);
1749+
ctor public ToolConfig(com.google.firebase.ai.type.FunctionCallingConfig? functionCallingConfig = null, com.google.firebase.ai.type.RetrievalConfig? retrievalConfig = null);
16931750
}
16941751

16951752
public final class Transcription {
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.ai
18+
19+
import com.google.firebase.ai.AIModels.Companion.app
20+
import com.google.firebase.ai.type.GenerativeBackend
21+
import com.google.firebase.ai.type.LatLng
22+
import com.google.firebase.ai.type.RetrievalConfig
23+
import com.google.firebase.ai.type.Tool
24+
import com.google.firebase.ai.type.ToolConfig
25+
import io.kotest.matchers.shouldBe
26+
import kotlinx.coroutines.runBlocking
27+
import org.junit.Test
28+
29+
class GroundingTests {
30+
31+
@Test
32+
fun groundingTests_canRecognizeAreas(): Unit = runBlocking {
33+
val model = setupModel(config = ToolConfig())
34+
val response = model.generateContent("Where is a good place to grab a coffee near Alameda, CA?")
35+
36+
response.candidates.isEmpty() shouldBe false
37+
response.candidates[0].groundingMetadata?.groundingChunks?.any { it.maps != null } shouldBe true
38+
}
39+
40+
@Test
41+
fun groundingTests_canRecognizeLatLng(): Unit = runBlocking {
42+
val model =
43+
setupModel(
44+
config =
45+
ToolConfig(
46+
retrievalConfig =
47+
RetrievalConfig(
48+
latLng = LatLng(latitude = 30.2672, longitude = -97.7431),
49+
languageCode = "en_US",
50+
),
51+
)
52+
)
53+
val response = model.generateContent("Find bookstores in my area.")
54+
55+
response.candidates.isEmpty() shouldBe false
56+
response.candidates[0].groundingMetadata?.groundingChunks?.any { it.maps != null } shouldBe true
57+
}
58+
59+
companion object {
60+
61+
@JvmStatic
62+
fun setupModel(config: ToolConfig): GenerativeModel {
63+
val model =
64+
FirebaseAI.getInstance(app(), GenerativeBackend.vertexAI())
65+
.generativeModel(
66+
modelName = "gemini-2.5-flash",
67+
toolConfig = config,
68+
tools = listOf(Tool.googleMaps()),
69+
)
70+
return model
71+
}
72+
}
73+
}

ai-logic/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Candidate.kt

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,12 @@ public class FinishReason private constructor(public val name: String, public va
336336
* Vertex AI Gemini API (see [Service Terms](https://cloud.google.com/terms/service-terms) section
337337
* within the Service Specific Terms).
338338
*
339+
* If using Grounding with Google Maps, you are required to comply with the "Grounding with Google
340+
* Maps" usage requirements for your chosen API provider:
341+
* [Gemini Developer API](https://ai.google.dev/gemini-api/terms#grounding-with-google-maps) or
342+
* Vertex AI Gemini API (see [Service Terms](https://cloud.google.com/terms/service-terms) section
343+
* within the Service Specific Terms).
344+
*
339345
* @property webSearchQueries The list of web search queries that the model performed to gather the
340346
* grounding information. These can be used to allow users to explore the search results themselves.
341347
* @property searchEntryPoint Google Search entry point for web searches. This contains an HTML/CSS
@@ -408,15 +414,40 @@ public class SearchEntryPoint(
408414
* Represents a chunk of retrieved data that supports a claim in the model's response.
409415
*
410416
* @property web Contains details if the grounding chunk is from a web source.
417+
* @property maps Contains details if the grounding chunk is from a Google Maps source.
411418
*/
412-
public class GroundingChunk(
413-
public val web: WebGroundingChunk?,
419+
public class GroundingChunk
420+
@JvmOverloads
421+
constructor(
422+
public val web: WebGroundingChunk? = null,
423+
public val maps: GoogleMapsGroundingChunk? = null,
414424
) {
425+
415426
@Serializable
416427
internal data class Internal(
417428
val web: WebGroundingChunk.Internal?,
429+
val maps: GoogleMapsGroundingChunk.Internal?,
418430
) {
419-
internal fun toPublic() = GroundingChunk(web = web?.toPublic())
431+
internal fun toPublic() = GroundingChunk(web = web?.toPublic(), maps?.toPublic())
432+
}
433+
}
434+
435+
/**
436+
* A grounding chunk from Google Maps.
437+
*
438+
* @property uri The URI of the place.
439+
* @property title The title of the place.
440+
* @property placeId This Place's resource name, in `places/{place_id}` format. This can be used to
441+
* look up the place using the Google Maps API.
442+
*/
443+
public class GoogleMapsGroundingChunk(
444+
public val uri: String?,
445+
public val title: String?,
446+
public val placeId: String?,
447+
) {
448+
@Serializable
449+
internal data class Internal(val uri: String?, val title: String?, val placeId: String?) {
450+
fun toPublic() = GoogleMapsGroundingChunk(uri, title, placeId)
420451
}
421452
}
422453

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.ai.type
18+
19+
import kotlinx.serialization.Serializable
20+
21+
/**
22+
* A tool that allows a Gemini model to connect to Google Maps to access and incorporate
23+
* location-based information into its responses.
24+
*
25+
* Important: If using Grounding with Google Maps, you are required to comply with the "Grounding
26+
* with Google Maps" usage requirements for your chosen API provider: {@link
27+
* https://ai.google.dev/gemini-api/terms#grounding-with-google-maps | Gemini Developer API} or
28+
* Vertex AI Gemini API (see {@link https://cloud.google.com/terms/service-terms | Service Terms}
29+
* section within the Service Specific Terms).
30+
*/
31+
public class GoogleMaps {
32+
@Serializable internal class Internal()
33+
34+
internal fun toInternal() = Internal()
35+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.ai.type
18+
19+
import kotlinx.serialization.Serializable
20+
21+
/**
22+
* An object that represents a latitude/longitude pair.
23+
*
24+
* @param latitude The latitude in degrees. It must be in the range [-90.0, +90.0].
25+
* @param longitude The longitude in degrees. It must be in the range [-180.0, +180.0].
26+
*/
27+
public data class LatLng(val latitude: Double, val longitude: Double) {
28+
@Serializable
29+
internal data class Internal(val latitude: Double, val longitude: Double) {
30+
fun toPublic() = LatLng(latitude, longitude)
31+
}
32+
33+
internal fun toInternal() = Internal(latitude, longitude)
34+
}

0 commit comments

Comments
 (0)