Skip to content

Commit 95a8b43

Browse files
committed
[FirebaseAI] Add support for URL context
1 parent e6d6e25 commit 95a8b43

File tree

12 files changed

+480
-14
lines changed

12 files changed

+480
-14
lines changed

firebase-ai/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Unreleased
22

33
- [changed] **Breaking Change**: Removed the `candidateCount` option from `LiveGenerationConfig`
4+
- [changed] Added support for URL context.
45

56
# 17.3.0
67

firebase-ai/api.txt

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,13 @@ package com.google.firebase.ai.type {
191191
method public com.google.firebase.ai.type.FinishReason? getFinishReason();
192192
method public com.google.firebase.ai.type.GroundingMetadata? getGroundingMetadata();
193193
method public java.util.List<com.google.firebase.ai.type.SafetyRating> getSafetyRatings();
194+
method public com.google.firebase.ai.type.UrlContextMetadata? getUrlContextMetadata();
194195
property public final com.google.firebase.ai.type.CitationMetadata? citationMetadata;
195196
property public final com.google.firebase.ai.type.Content content;
196197
property public final com.google.firebase.ai.type.FinishReason? finishReason;
197198
property public final com.google.firebase.ai.type.GroundingMetadata? groundingMetadata;
198199
property public final java.util.List<com.google.firebase.ai.type.SafetyRating> safetyRatings;
200+
property public final com.google.firebase.ai.type.UrlContextMetadata? urlContextMetadata;
199201
}
200202

201203
public final class Citation {
@@ -1201,13 +1203,15 @@ package com.google.firebase.ai.type {
12011203
method public static com.google.firebase.ai.type.Tool codeExecution();
12021204
method public static com.google.firebase.ai.type.Tool functionDeclarations(java.util.List<com.google.firebase.ai.type.FunctionDeclaration> functionDeclarations);
12031205
method public static com.google.firebase.ai.type.Tool googleSearch(com.google.firebase.ai.type.GoogleSearch googleSearch = com.google.firebase.ai.type.GoogleSearch());
1206+
method public static com.google.firebase.ai.type.Tool urlContext(com.google.firebase.ai.type.UrlContext urlContext = com.google.firebase.ai.type.UrlContext());
12041207
field public static final com.google.firebase.ai.type.Tool.Companion Companion;
12051208
}
12061209

12071210
public static final class Tool.Companion {
12081211
method public com.google.firebase.ai.type.Tool codeExecution();
12091212
method public com.google.firebase.ai.type.Tool functionDeclarations(java.util.List<com.google.firebase.ai.type.FunctionDeclaration> functionDeclarations);
12101213
method public com.google.firebase.ai.type.Tool googleSearch(com.google.firebase.ai.type.GoogleSearch googleSearch = com.google.firebase.ai.type.GoogleSearch());
1214+
method public com.google.firebase.ai.type.Tool urlContext(com.google.firebase.ai.type.UrlContext urlContext = com.google.firebase.ai.type.UrlContext());
12111215
}
12121216

12131217
public final class ToolConfig {
@@ -1220,19 +1224,57 @@ package com.google.firebase.ai.type {
12201224
public final class UnsupportedUserLocationException extends com.google.firebase.ai.type.FirebaseAIException {
12211225
}
12221226

1227+
public final class UrlContext {
1228+
ctor public UrlContext();
1229+
}
1230+
1231+
public final class UrlContextMetadata {
1232+
ctor public UrlContextMetadata(java.util.List<com.google.firebase.ai.type.UrlMetadata> urlMetadata);
1233+
method public java.util.List<com.google.firebase.ai.type.UrlMetadata> getUrlMetadata();
1234+
property public final java.util.List<com.google.firebase.ai.type.UrlMetadata> urlMetadata;
1235+
}
1236+
1237+
public final class UrlMetadata {
1238+
ctor public UrlMetadata(String? retrievedUrl, com.google.firebase.ai.type.UrlRetrievalStatus urlRetrievalStatus);
1239+
method public String? getRetrievedUrl();
1240+
method public com.google.firebase.ai.type.UrlRetrievalStatus getUrlRetrievalStatus();
1241+
property public final String? retrievedUrl;
1242+
property public final com.google.firebase.ai.type.UrlRetrievalStatus urlRetrievalStatus;
1243+
}
1244+
1245+
public final class UrlRetrievalStatus {
1246+
method public String getName();
1247+
method public int getOrdinal();
1248+
property public final String name;
1249+
property public final int ordinal;
1250+
field public static final com.google.firebase.ai.type.UrlRetrievalStatus.Companion Companion;
1251+
field public static final com.google.firebase.ai.type.UrlRetrievalStatus ERROR;
1252+
field public static final com.google.firebase.ai.type.UrlRetrievalStatus PAYWALL;
1253+
field public static final com.google.firebase.ai.type.UrlRetrievalStatus SUCCESS;
1254+
field public static final com.google.firebase.ai.type.UrlRetrievalStatus UNSAFE;
1255+
field public static final com.google.firebase.ai.type.UrlRetrievalStatus UNSPECIFIED;
1256+
}
1257+
1258+
public static final class UrlRetrievalStatus.Companion {
1259+
}
1260+
12231261
public final class UsageMetadata {
1224-
ctor public UsageMetadata(int promptTokenCount, Integer? candidatesTokenCount, int totalTokenCount, java.util.List<com.google.firebase.ai.type.ModalityTokenCount> promptTokensDetails, java.util.List<com.google.firebase.ai.type.ModalityTokenCount> candidatesTokensDetails, int thoughtsTokenCount);
1262+
ctor public UsageMetadata(int promptTokenCount, Integer? candidatesTokenCount, int totalTokenCount, java.util.List<com.google.firebase.ai.type.ModalityTokenCount> promptTokensDetails, java.util.List<com.google.firebase.ai.type.ModalityTokenCount> candidatesTokensDetails, java.util.List<com.google.firebase.ai.type.ModalityTokenCount> toolUsePromptTokensDetails, int thoughtsTokenCount, int toolUsePromptTokenCount);
12251263
method public Integer? getCandidatesTokenCount();
12261264
method public java.util.List<com.google.firebase.ai.type.ModalityTokenCount> getCandidatesTokensDetails();
12271265
method public int getPromptTokenCount();
12281266
method public java.util.List<com.google.firebase.ai.type.ModalityTokenCount> getPromptTokensDetails();
12291267
method public int getThoughtsTokenCount();
1268+
method public int getToolUsePromptTokenCount();
1269+
method public java.util.List<com.google.firebase.ai.type.ModalityTokenCount> getToolUsePromptTokensDetails();
12301270
method public int getTotalTokenCount();
12311271
property public final Integer? candidatesTokenCount;
12321272
property public final java.util.List<com.google.firebase.ai.type.ModalityTokenCount> candidatesTokensDetails;
12331273
property public final int promptTokenCount;
12341274
property public final java.util.List<com.google.firebase.ai.type.ModalityTokenCount> promptTokensDetails;
12351275
property public final int thoughtsTokenCount;
1276+
property public final int toolUsePromptTokenCount;
1277+
property public final java.util.List<com.google.firebase.ai.type.ModalityTokenCount> toolUsePromptTokensDetails;
12361278
property public final int totalTokenCount;
12371279
}
12381280

firebase-ai/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
version=17.3.1
15+
version=17.4.0
1616
latestReleasedVersion=17.3.0

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

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,18 @@ import kotlinx.serialization.json.JsonNames
3333
* @property safetyRatings A list of [SafetyRating]s describing the generated content.
3434
* @property citationMetadata Metadata about the sources used to generate this content.
3535
* @property finishReason The reason the model stopped generating content, if it exist.
36-
* @property groundingMetadata Metadata returned to the client when grounding is enabled.
36+
* @property groundingMetadata Metadata returned to the client when the model grounds its response.
37+
* @property urlContextMetadata Metadata returned to the client when the [UrlContext] tool is
38+
* enabled.
3739
*/
3840
public class Candidate
3941
internal constructor(
4042
public val content: Content,
4143
public val safetyRatings: List<SafetyRating>,
4244
public val citationMetadata: CitationMetadata?,
4345
public val finishReason: FinishReason?,
44-
public val groundingMetadata: GroundingMetadata?
46+
public val groundingMetadata: GroundingMetadata?,
47+
public val urlContextMetadata: UrlContextMetadata?
4548
) {
4649

4750
@Serializable
@@ -50,20 +53,23 @@ internal constructor(
5053
val finishReason: FinishReason.Internal? = null,
5154
val safetyRatings: List<SafetyRating.Internal>? = null,
5255
val citationMetadata: CitationMetadata.Internal? = null,
53-
val groundingMetadata: GroundingMetadata.Internal? = null
56+
val groundingMetadata: GroundingMetadata.Internal? = null,
57+
val urlContextMetadata: UrlContextMetadata.Internal? = null
5458
) {
5559
internal fun toPublic(): Candidate {
5660
val safetyRatings = safetyRatings?.mapNotNull { it.toPublic() }.orEmpty()
5761
val citations = citationMetadata?.toPublic()
5862
val finishReason = finishReason?.toPublic()
5963
val groundingMetadata = groundingMetadata?.toPublic()
64+
val urlContextMetadata = urlContextMetadata?.toPublic()
6065

6166
return Candidate(
6267
this.content?.toPublic() ?: content("model") {},
6368
safetyRatings,
6469
citations,
6570
finishReason,
66-
groundingMetadata
71+
groundingMetadata,
72+
urlContextMetadata
6773
)
6874
}
6975
}
@@ -372,8 +378,7 @@ public class SearchEntryPoint(
372378
}
373379

374380
/**
375-
* Represents a chunk of retrieved data that supports a claim in the model's response. This is part
376-
* of the grounding information provided when grounding is enabled.
381+
* Represents a chunk of retrieved data that supports a claim in the model's response.
377382
*
378383
* @property web Contains details if the grounding chunk is from a web source.
379384
*/
@@ -492,3 +497,81 @@ public class Segment(
492497
)
493498
}
494499
}
500+
501+
/**
502+
* Metadata related to the [UrlContext] tool.
503+
*
504+
* @property urlMetadata List of [UrlMetadata] used to provide context to the Gemini model.
505+
*/
506+
public class UrlContextMetadata(public val urlMetadata: List<UrlMetadata>) {
507+
@Serializable
508+
internal data class Internal(val urlMetadata: List<UrlMetadata.Internal>) {
509+
internal fun toPublic() = UrlContextMetadata(urlMetadata.map { it.toPublic() })
510+
}
511+
}
512+
513+
/**
514+
* Metadata for a single URL retrieved by the [UrlContext] tool.
515+
*
516+
* @property retrievedUrl The retrieved URL.
517+
* @property urlRetrievalStatus The status of the URL retrieval.
518+
*/
519+
public class UrlMetadata(
520+
public val retrievedUrl: String?,
521+
public val urlRetrievalStatus: UrlRetrievalStatus
522+
) {
523+
@Serializable
524+
internal data class Internal(
525+
val retrievedUrl: String?,
526+
val urlRetrievalStatus: UrlRetrievalStatus.Internal
527+
) {
528+
internal fun toPublic() = UrlMetadata(retrievedUrl, urlRetrievalStatus.toPublic())
529+
}
530+
}
531+
532+
/**
533+
* The status of a URL retrieval.
534+
*
535+
* @property name The name of the retrieval status.
536+
* @property ordinal The ordinal value of the retrieval status.
537+
*/
538+
public class UrlRetrievalStatus
539+
private constructor(public val name: String, public val ordinal: Int) {
540+
541+
@Serializable(Internal.Serializer::class)
542+
internal enum class Internal {
543+
@SerialName("URL_RETRIEVAL_STATUS_UNSPECIFIED") UNSPECIFIED,
544+
@SerialName("URL_RETRIEVAL_STATUS_SUCCESS") SUCCESS,
545+
@SerialName("URL_RETRIEVAL_STATUS_ERROR") ERROR,
546+
@SerialName("URL_RETRIEVAL_STATUS_PAYWALL") PAYWALL,
547+
@SerialName("URL_RETRIEVAL_STATUS_UNSAFE") UNSAFE;
548+
549+
internal object Serializer : KSerializer<Internal> by FirstOrdinalSerializer(Internal::class)
550+
551+
internal fun toPublic() =
552+
when (this) {
553+
SUCCESS -> UrlRetrievalStatus.SUCCESS
554+
ERROR -> UrlRetrievalStatus.ERROR
555+
PAYWALL -> UrlRetrievalStatus.PAYWALL
556+
UNSAFE -> UrlRetrievalStatus.UNSAFE
557+
else -> UrlRetrievalStatus.UNSPECIFIED
558+
}
559+
}
560+
561+
public companion object {
562+
/** Unspecified retrieval status. */
563+
@JvmField public val UNSPECIFIED: UrlRetrievalStatus = UrlRetrievalStatus("UNSPECIFIED", 0)
564+
565+
/** The URL retrieval was successful. */
566+
@JvmField public val SUCCESS: UrlRetrievalStatus = UrlRetrievalStatus("SUCCESS", 1)
567+
568+
/** The URL retrieval failed. */
569+
@JvmField public val ERROR: UrlRetrievalStatus = UrlRetrievalStatus("ERROR", 2)
570+
571+
/** The URL retrieval failed because the content is behind a paywall. */
572+
@JvmField public val PAYWALL: UrlRetrievalStatus = UrlRetrievalStatus("PAYWALL", 3)
573+
574+
/** The URL retrieval failed because the content is unsafe. */
575+
@JvmField public val UNSAFE: UrlRetrievalStatus = UrlRetrievalStatus("UNSAFE", 4)
576+
}
577+
}

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,26 @@ internal constructor(
2828
internal val functionDeclarations: List<FunctionDeclaration>?,
2929
internal val googleSearch: GoogleSearch?,
3030
internal val codeExecution: JsonObject?,
31+
internal val urlContext: UrlContext?,
3132
) {
3233
internal fun toInternal() =
3334
Internal(
3435
functionDeclarations?.map { it.toInternal() } ?: emptyList(),
3536
googleSearch = this.googleSearch?.toInternal(),
36-
codeExecution = this.codeExecution
37+
codeExecution = this.codeExecution,
38+
urlContext = this.urlContext?.toInternal()
3739
)
3840
@Serializable
3941
internal data class Internal(
4042
val functionDeclarations: List<FunctionDeclaration.Internal>? = null,
4143
val googleSearch: GoogleSearch.Internal? = null,
4244
// This is a json object because it is not possible to make a data class with no parameters.
4345
val codeExecution: JsonObject? = null,
46+
val urlContext: UrlContext.Internal? = null,
4447
)
4548
public companion object {
4649

47-
private val codeExecutionInstance by lazy { Tool(null, null, JsonObject(emptyMap())) }
50+
private val codeExecutionInstance by lazy { Tool(null, null, JsonObject(emptyMap()), null) }
4851

4952
/**
5053
* Creates a [Tool] instance that provides the model with access to the [functionDeclarations].
@@ -53,7 +56,7 @@ internal constructor(
5356
*/
5457
@JvmStatic
5558
public fun functionDeclarations(functionDeclarations: List<FunctionDeclaration>): Tool {
56-
return Tool(functionDeclarations, null, null)
59+
return Tool(functionDeclarations, null, null, null)
5760
}
5861

5962
/** Creates a [Tool] instance that allows the model to use Code Execution. */
@@ -62,6 +65,19 @@ internal constructor(
6265
return codeExecutionInstance
6366
}
6467

68+
/**
69+
* Creates a [Tool] instance that allows you to provide additional context to the models in the
70+
* form of public web URLs. By including URLs in your request, the Gemini model will access the
71+
* content from those pages to inform and enhance its response.
72+
*
73+
* @param urlContext Specifies the URL context configuration.
74+
* @return A [Tool] configured for URL context
75+
*/
76+
@JvmStatic
77+
public fun urlContext(urlContext: UrlContext = UrlContext()): Tool {
78+
return Tool(null, null, null, urlContext)
79+
}
80+
6581
/**
6682
* Creates a [Tool] instance that allows the model to use Grounding with Google Search.
6783
*
@@ -80,7 +96,7 @@ internal constructor(
8096
*/
8197
@JvmStatic
8298
public fun googleSearch(googleSearch: GoogleSearch = GoogleSearch()): Tool {
83-
return Tool(null, googleSearch, null)
99+
return Tool(null, googleSearch, null, null)
84100
}
85101
}
86102
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2025 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+
/** Specifies the URL context configuration. */
22+
public class UrlContext {
23+
@Serializable internal class Internal()
24+
25+
internal fun toInternal() = Internal()
26+
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,20 @@ import kotlinx.serialization.Serializable
2828
* prompt.
2929
* @param candidatesTokensDetails The breakdown, by modality, of how many tokens are consumed by the
3030
* candidates.
31+
* @param toolUsePromptTokensDetails The breakdown, by modality, of how many tokens are consumed by
32+
* tools.
3133
* @param thoughtsTokenCount The number of tokens used by the model's internal "thinking" process.
34+
* @param toolUsePromptTokenCount The number of tokens used by tools.
3235
*/
3336
public class UsageMetadata(
3437
public val promptTokenCount: Int,
3538
public val candidatesTokenCount: Int?,
3639
public val totalTokenCount: Int,
3740
public val promptTokensDetails: List<ModalityTokenCount>,
3841
public val candidatesTokensDetails: List<ModalityTokenCount>,
42+
public val toolUsePromptTokensDetails: List<ModalityTokenCount>,
3943
public val thoughtsTokenCount: Int,
44+
public val toolUsePromptTokenCount: Int,
4045
) {
4146

4247
@Serializable
@@ -46,7 +51,9 @@ public class UsageMetadata(
4651
val totalTokenCount: Int? = null,
4752
val promptTokensDetails: List<ModalityTokenCount.Internal>? = null,
4853
val candidatesTokensDetails: List<ModalityTokenCount.Internal>? = null,
54+
val toolUsePromptTokensDetails: List<ModalityTokenCount.Internal>? = null,
4955
val thoughtsTokenCount: Int? = null,
56+
val toolUsePromptTokenCount: Int? = null,
5057
) {
5158

5259
internal fun toPublic(): UsageMetadata =
@@ -56,7 +63,10 @@ public class UsageMetadata(
5663
totalTokenCount ?: 0,
5764
promptTokensDetails = promptTokensDetails?.map { it.toPublic() } ?: emptyList(),
5865
candidatesTokensDetails = candidatesTokensDetails?.map { it.toPublic() } ?: emptyList(),
59-
thoughtsTokenCount ?: 0
66+
toolUsePromptTokensDetails = toolUsePromptTokensDetails?.map { it.toPublic() }
67+
?: emptyList(),
68+
thoughtsTokenCount ?: 0,
69+
toolUsePromptTokenCount ?: 0,
6070
)
6171
}
6272
}

0 commit comments

Comments
 (0)