Skip to content

Commit 0021c71

Browse files
committed
[Firebase AI] Add URLContext tool
1 parent 939cf64 commit 0021c71

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

FirebaseAI/Sources/Tool.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ public struct GoogleSearch: Sendable {
6363
public init() {}
6464
}
6565

66+
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
67+
public struct URLContext: Sendable {
68+
public init() {}
69+
}
70+
6671
/// A helper tool that the model may use when generating responses.
6772
///
6873
/// A `Tool` is a piece of code that enables the system to interact with external systems to perform
@@ -74,14 +79,24 @@ public struct Tool: Sendable {
7479
/// Specifies the Google Search configuration.
7580
let googleSearch: GoogleSearch?
7681

82+
let urlContext: URLContext?
83+
7784
init(functionDeclarations: [FunctionDeclaration]?) {
7885
self.functionDeclarations = functionDeclarations
7986
googleSearch = nil
87+
urlContext = nil
8088
}
8189

8290
init(googleSearch: GoogleSearch) {
8391
self.googleSearch = googleSearch
8492
functionDeclarations = nil
93+
urlContext = nil
94+
}
95+
96+
init(urlContext: URLContext) {
97+
self.urlContext = urlContext
98+
functionDeclarations = nil
99+
googleSearch = nil
85100
}
86101

87102
/// Creates a tool that allows the model to perform function calling.
@@ -126,6 +141,10 @@ public struct Tool: Sendable {
126141
public static func googleSearch(_ googleSearch: GoogleSearch = GoogleSearch()) -> Tool {
127142
return self.init(googleSearch: googleSearch)
128143
}
144+
145+
public static func urlContext(_ urlContext: URLContext = URLContext()) -> Tool {
146+
return self.init(urlContext: urlContext)
147+
}
129148
}
130149

131150
/// Configuration for specifying function calling behavior.
@@ -214,5 +233,8 @@ extension FunctionCallingConfig.Mode: Encodable {}
214233
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
215234
extension GoogleSearch: Encodable {}
216235

236+
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
237+
extension URLContext: Encodable {}
238+
217239
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
218240
extension ToolConfig: Encodable {}

FirebaseAI/Tests/TestApp/Tests/Integration/GenerateContentIntegrationTests.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,44 @@ struct GenerateContentIntegrationTests {
291291
}
292292
}
293293

294+
@Test(
295+
"generateContent with URL Context",
296+
arguments: InstanceConfig.allConfigs
297+
)
298+
func generateContent_withURLContext_succeeds(_ config: InstanceConfig) async throws {
299+
let model = FirebaseAI.componentInstance(config).generativeModel(
300+
modelName: ModelNames.gemini2_5_Flash,
301+
tools: [.urlContext()]
302+
)
303+
let prompt = """
304+
Write a one paragraph summary of this blog post: \
305+
https://developers.googleblog.com/en/introducing-gemma-3-270m/
306+
"""
307+
308+
let response = try await model.generateContent(prompt)
309+
310+
let candidate = try #require(response.candidates.first)
311+
let groundingMetadata = try #require(candidate.groundingMetadata)
312+
#expect(!groundingMetadata.groundingChunks.isEmpty)
313+
#expect(!groundingMetadata.groundingSupports.isEmpty)
314+
315+
for chunk in groundingMetadata.groundingChunks {
316+
#expect(chunk.web != nil)
317+
}
318+
319+
for support in groundingMetadata.groundingSupports {
320+
let segment = support.segment
321+
#expect(segment.endIndex > segment.startIndex)
322+
#expect(!segment.text.isEmpty)
323+
#expect(!support.groundingChunkIndices.isEmpty)
324+
325+
// Ensure indices point to valid chunks
326+
for index in support.groundingChunkIndices {
327+
#expect(index < groundingMetadata.groundingChunks.count)
328+
}
329+
}
330+
}
331+
294332
// MARK: Streaming Tests
295333

296334
@Test(arguments: [

0 commit comments

Comments
 (0)