Skip to content

Commit ecdf1f0

Browse files
authored
[Firebase AI] Add support for URL Context (#1340)
* [Firebase AI] Add support for URL Context * Update URLContext.cs * Update UIHandlerAutomated.cs
1 parent 47bba71 commit ecdf1f0

File tree

9 files changed

+402
-12
lines changed

9 files changed

+402
-12
lines changed

docs/readme.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ Support
109109

110110
Release Notes
111111
-------------
112+
### Upcoming
113+
- Changes
114+
- Firebase AI: Add support for Gemini's URL context tool.
115+
112116
### 13.3.0
113117
- Changes
114118
- Firebase AI: Add support for enabling the model to use Code Execution.

firebaseai/src/Candidate.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,22 @@ public IReadOnlyList<SafetyRating> SafetyRatings {
101101
/// Grounding metadata for the response, if any.
102102
/// </summary>
103103
public GroundingMetadata? GroundingMetadata { get; }
104+
105+
/// <summary>
106+
/// Metadata related to the `URLContext` tool.
107+
/// </summary>
108+
public UrlContextMetadata? UrlContextMetadata { get; }
104109

105110
// Hidden constructor, users don't need to make this.
106111
private Candidate(ModelContent content, List<SafetyRating> safetyRatings,
107112
FinishReason? finishReason, CitationMetadata? citationMetadata,
108-
GroundingMetadata? groundingMetadata) {
113+
GroundingMetadata? groundingMetadata, UrlContextMetadata? urlContextMetadata) {
109114
Content = content;
110115
_safetyRatings = safetyRatings ?? new List<SafetyRating>();
111116
FinishReason = finishReason;
112117
CitationMetadata = citationMetadata;
113118
GroundingMetadata = groundingMetadata;
119+
UrlContextMetadata = urlContextMetadata;
114120
}
115121

116122
private static FinishReason ParseFinishReason(string str) {
@@ -141,7 +147,9 @@ internal static Candidate FromJson(Dictionary<string, object> jsonDict,
141147
jsonDict.ParseNullableObject("citationMetadata",
142148
(d) => Firebase.AI.CitationMetadata.FromJson(d, backend)),
143149
jsonDict.ParseNullableObject("groundingMetadata",
144-
Firebase.AI.GroundingMetadata.FromJson));
150+
Firebase.AI.GroundingMetadata.FromJson),
151+
jsonDict.ParseNullableObject("urlContextMetadata",
152+
Firebase.AI.UrlContextMetadata.FromJson));
145153
}
146154
}
147155

firebaseai/src/FunctionCalling.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public readonly struct Tool {
102102
private List<FunctionDeclaration> FunctionDeclarations { get; }
103103
private GoogleSearch? GoogleSearch { get; }
104104
private CodeExecution? CodeExecution { get; }
105+
private UrlContext? UrlContext { get; }
105106

106107
/// <summary>
107108
/// Creates a tool that allows the model to perform function calling.
@@ -112,6 +113,7 @@ public Tool(params FunctionDeclaration[] functionDeclarations) {
112113
FunctionDeclarations = new List<FunctionDeclaration>(functionDeclarations);
113114
GoogleSearch = null;
114115
CodeExecution = null;
116+
UrlContext = null;
115117
}
116118
/// <summary>
117119
/// Creates a tool that allows the model to perform function calling.
@@ -122,6 +124,7 @@ public Tool(IEnumerable<FunctionDeclaration> functionDeclarations) {
122124
FunctionDeclarations = new List<FunctionDeclaration>(functionDeclarations);
123125
GoogleSearch = null;
124126
CodeExecution = null;
127+
UrlContext = null;
125128
}
126129

127130
/// <summary>
@@ -133,6 +136,7 @@ public Tool(GoogleSearch googleSearch) {
133136
FunctionDeclarations = null;
134137
GoogleSearch = googleSearch;
135138
CodeExecution = null;
139+
UrlContext = null;
136140
}
137141

138142
/// <summary>
@@ -144,6 +148,20 @@ public Tool(CodeExecution codeExecution) {
144148
FunctionDeclarations = null;
145149
GoogleSearch = null;
146150
CodeExecution = codeExecution;
151+
UrlContext = null;
152+
}
153+
154+
/// <summary>
155+
/// Creates a tool that allows you to provide additional context to the models in the form of
156+
/// public web URLs.
157+
/// </summary>
158+
/// <param name="urlContext">An empty `UrlContext` object. The presence of this object
159+
/// in the list of tools enables the model to use Url Contexts.</param>
160+
public Tool(UrlContext urlContext) {
161+
FunctionDeclarations = null;
162+
GoogleSearch = null;
163+
CodeExecution = null;
164+
UrlContext = urlContext;
147165
}
148166

149167
/// <summary>
@@ -161,6 +179,9 @@ internal Dictionary<string, object> ToJson() {
161179
if (CodeExecution.HasValue) {
162180
json["codeExecution"] = new Dictionary<string, object>();
163181
}
182+
if (UrlContext.HasValue) {
183+
json["urlContext"] = new Dictionary<string, object>();
184+
}
164185
return json;
165186
}
166187
}

firebaseai/src/GenerateContentResponse.cs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,33 +471,57 @@ public readonly struct UsageMetadata {
471471
/// </summary>
472472
public int ThoughtsTokenCount { get; }
473473
/// <summary>
474+
/// The number of tokens used by any enabled tools.
475+
/// </summary>
476+
public int ToolUsePromptTokenCount { get; }
477+
/// <summary>
474478
/// The total number of tokens in both the request and response.
475479
/// </summary>
476480
public int TotalTokenCount { get; }
477481

478482
private readonly IReadOnlyList<ModalityTokenCount> _promptTokensDetails;
483+
/// <summary>
484+
/// The breakdown, by modality, of how many tokens are consumed by the prompt.
485+
/// </summary>
479486
public IReadOnlyList<ModalityTokenCount> PromptTokensDetails {
480487
get {
481488
return _promptTokensDetails ?? new List<ModalityTokenCount>();
482489
}
483490
}
484491

485492
private readonly IReadOnlyList<ModalityTokenCount> _candidatesTokensDetails;
493+
/// <summary>
494+
/// The breakdown, by modality, of how many tokens are consumed by the candidates.
495+
/// </summary>
486496
public IReadOnlyList<ModalityTokenCount> CandidatesTokensDetails {
487497
get {
488498
return _candidatesTokensDetails ?? new List<ModalityTokenCount>();
489499
}
490500
}
491501

502+
private readonly IReadOnlyList<ModalityTokenCount> _toolUsePromptTokensDetails;
503+
/// <summary>
504+
/// The breakdown, by modality, of how many tokens were consumed by the tools used to process
505+
/// the request.
506+
/// </summary>
507+
public IReadOnlyList<ModalityTokenCount> ToolUsePromptTokensDetails {
508+
get {
509+
return _toolUsePromptTokensDetails ?? new List<ModalityTokenCount>();
510+
}
511+
}
512+
492513
// Hidden constructor, users don't need to make this.
493-
private UsageMetadata(int promptTC, int candidatesTC, int thoughtsTC, int totalTC,
494-
List<ModalityTokenCount> promptDetails, List<ModalityTokenCount> candidateDetails) {
514+
private UsageMetadata(int promptTC, int candidatesTC, int thoughtsTC, int toolUseTC, int totalTC,
515+
List<ModalityTokenCount> promptDetails, List<ModalityTokenCount> candidateDetails,
516+
List<ModalityTokenCount> toolUseDetails) {
495517
PromptTokenCount = promptTC;
496518
CandidatesTokenCount = candidatesTC;
497519
ThoughtsTokenCount = thoughtsTC;
520+
ToolUsePromptTokenCount = toolUseTC;
498521
TotalTokenCount = totalTC;
499522
_promptTokensDetails = promptDetails;
500523
_candidatesTokensDetails = candidateDetails;
524+
_toolUsePromptTokensDetails = toolUseDetails;
501525
}
502526

503527
/// <summary>
@@ -509,9 +533,11 @@ internal static UsageMetadata FromJson(Dictionary<string, object> jsonDict) {
509533
jsonDict.ParseValue<int>("promptTokenCount"),
510534
jsonDict.ParseValue<int>("candidatesTokenCount"),
511535
jsonDict.ParseValue<int>("thoughtsTokenCount"),
536+
jsonDict.ParseValue<int>("toolUsePromptTokenCount"),
512537
jsonDict.ParseValue<int>("totalTokenCount"),
513538
jsonDict.ParseObjectList("promptTokensDetails", ModalityTokenCount.FromJson),
514-
jsonDict.ParseObjectList("candidatesTokensDetails", ModalityTokenCount.FromJson));
539+
jsonDict.ParseObjectList("candidatesTokensDetails", ModalityTokenCount.FromJson),
540+
jsonDict.ParseObjectList("toolUsePromptTokensDetails", ModalityTokenCount.FromJson));
515541
}
516542
}
517543

firebaseai/src/URLContext.cs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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+
using System;
18+
using System.Collections.Generic;
19+
using Firebase.AI.Internal;
20+
21+
namespace Firebase.AI {
22+
23+
/// <summary>
24+
/// A tool that allows you to provide additional context to the models in the form of
25+
/// public web URLs.
26+
///
27+
/// By including URLs in your request, the Gemini model will access the content from those pages
28+
/// to inform and enhance its response.
29+
/// </summary>
30+
public readonly struct UrlContext { }
31+
32+
/// <summary>
33+
/// Metadata for a single URL retrieved by the UrlContext tool.
34+
/// </summary>
35+
public readonly struct UrlMetadata {
36+
/// <summary>
37+
/// Status of the URL retrieval.
38+
/// </summary>
39+
public enum UrlRetrievalStatus {
40+
/// <summary>
41+
/// Unspecified retrieval status
42+
/// </summary>
43+
Unspecified = 0,
44+
/// <summary>
45+
/// The URL retrieval was successful.
46+
/// </summary>
47+
Success,
48+
/// <summary>
49+
/// The URL retrieval failed.
50+
/// </summary>
51+
Error,
52+
/// <summary>
53+
/// The URL retrieval failed because the content is behind a paywall.
54+
/// </summary>
55+
Paywall,
56+
/// <summary>
57+
/// The URL retrieval failed because the content is unsafe.
58+
/// </summary>
59+
Unsafe,
60+
}
61+
62+
/// <summary>
63+
/// The retrieved URL.
64+
/// </summary>
65+
public System.Uri Url { get; }
66+
/// <summary>
67+
/// The status of the URL retrieval.
68+
/// </summary>
69+
public UrlRetrievalStatus RetrievalStatus { get; }
70+
71+
private UrlMetadata(string urlString, UrlRetrievalStatus? retrievalStatus) {
72+
if (string.IsNullOrEmpty(urlString)) {
73+
Url = null;
74+
}
75+
else {
76+
Url = new Uri(urlString);
77+
}
78+
RetrievalStatus = retrievalStatus ?? UrlRetrievalStatus.Unspecified;
79+
}
80+
81+
private static UrlRetrievalStatus ParseUrlRetrievalStatus(string str) {
82+
return str switch {
83+
"URL_RETRIEVAL_STATUS_SUCCESS" => UrlRetrievalStatus.Success,
84+
"URL_RETRIEVAL_STATUS_ERROR" => UrlRetrievalStatus.Error,
85+
"URL_RETRIEVAL_STATUS_PAYWALL" => UrlRetrievalStatus.Paywall,
86+
"URL_RETRIEVAL_STATUS_UNSAFE" => UrlRetrievalStatus.Unsafe,
87+
_ => UrlRetrievalStatus.Unspecified,
88+
};
89+
}
90+
91+
/// <summary>
92+
/// Intended for internal use only.
93+
/// This method is used for deserializing JSON responses and should not be called directly.
94+
/// </summary>
95+
internal static UrlMetadata FromJson(Dictionary<string, object> jsonDict) {
96+
return new UrlMetadata(
97+
jsonDict.ParseValue<string>("retrievedUrl"),
98+
jsonDict.ParseNullableEnum("urlRetrievalStatus", ParseUrlRetrievalStatus)
99+
);
100+
}
101+
}
102+
103+
/// <summary>
104+
/// Metadata related to the UrlContext tool.
105+
/// </summary>
106+
public readonly struct UrlContextMetadata {
107+
private readonly IReadOnlyList<UrlMetadata> _urlMetadata;
108+
/// <summary>
109+
/// List of URL metadata used to provide context to the Gemini model.
110+
/// </summary>
111+
public IReadOnlyList<UrlMetadata> UrlMetadata {
112+
get {
113+
return _urlMetadata ?? new List<UrlMetadata>();
114+
}
115+
}
116+
117+
private UrlContextMetadata(List<UrlMetadata> urlMetadata) {
118+
_urlMetadata = urlMetadata;
119+
}
120+
121+
/// <summary>
122+
/// Intended for internal use only.
123+
/// This method is used for deserializing JSON responses and should not be called directly.
124+
/// </summary>
125+
internal static UrlContextMetadata FromJson(Dictionary<string, object> jsonDict) {
126+
return new UrlContextMetadata(
127+
jsonDict.ParseObjectList("urlMetadata", Firebase.AI.UrlMetadata.FromJson)
128+
);
129+
}
130+
}
131+
132+
}

firebaseai/src/URLContext.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)