Skip to content

Commit 0b97b88

Browse files
andrewheardpaulb777
authored andcommitted
[Firebase AI] Add URLContext tool
1 parent a5bc774 commit 0b97b88

File tree

2 files changed

+62
-8
lines changed

2 files changed

+62
-8
lines changed

FirebaseAI/Sources/Tool.swift

Lines changed: 24 additions & 8 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
@@ -76,14 +81,17 @@ public struct Tool: Sendable {
7681
let googleSearch: GoogleSearch?
7782

7883
let codeExecution: CodeExecution?
79-
80-
init(functionDeclarations: [FunctionDeclaration]? = nil,
81-
googleSearch: GoogleSearch? = nil,
82-
codeExecution: CodeExecution? = nil) {
83-
self.functionDeclarations = functionDeclarations
84-
self.googleSearch = googleSearch
85-
self.codeExecution = codeExecution
86-
}
84+
let urlContext: URLContext?
85+
86+
init(functionDeclarations: [FunctionDeclaration]? = nil,
87+
googleSearch: GoogleSearch? = nil,
88+
urlContext: URLContext? = nil,
89+
codeExecution: CodeExecution? = nil) {
90+
self.functionDeclarations = functionDeclarations
91+
self.googleSearch = googleSearch
92+
self.urlContext = urlContext
93+
self.codeExecution = codeExecution
94+
}
8795

8896
/// Creates a tool that allows the model to perform function calling.
8997
///
@@ -128,6 +136,11 @@ public struct Tool: Sendable {
128136
return self.init(googleSearch: googleSearch)
129137
}
130138

139+
140+
public static func urlContext(_ urlContext: URLContext = URLContext()) -> Tool {
141+
return self.init(urlContext: urlContext)
142+
}
143+
131144
/// Creates a tool that allows the model to execute code.
132145
///
133146
/// For more details, see ``CodeExecution``.
@@ -222,5 +235,8 @@ extension FunctionCallingConfig.Mode: Encodable {}
222235
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
223236
extension GoogleSearch: Encodable {}
224237

238+
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
239+
extension URLContext: Encodable {}
240+
225241
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
226242
extension ToolConfig: Encodable {}

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,44 @@ struct GenerateContentIntegrationTests {
424424
}
425425
}
426426

427+
@Test(
428+
"generateContent with URL Context",
429+
arguments: InstanceConfig.allConfigs
430+
)
431+
func generateContent_withURLContext_succeeds(_ config: InstanceConfig) async throws {
432+
let model = FirebaseAI.componentInstance(config).generativeModel(
433+
modelName: ModelNames.gemini2_5_Flash,
434+
tools: [.urlContext()]
435+
)
436+
let prompt = """
437+
Write a one paragraph summary of this blog post: \
438+
https://developers.googleblog.com/en/introducing-gemma-3-270m/
439+
"""
440+
441+
let response = try await model.generateContent(prompt)
442+
443+
let candidate = try #require(response.candidates.first)
444+
let groundingMetadata = try #require(candidate.groundingMetadata)
445+
#expect(!groundingMetadata.groundingChunks.isEmpty)
446+
#expect(!groundingMetadata.groundingSupports.isEmpty)
447+
448+
for chunk in groundingMetadata.groundingChunks {
449+
#expect(chunk.web != nil)
450+
}
451+
452+
for support in groundingMetadata.groundingSupports {
453+
let segment = support.segment
454+
#expect(segment.endIndex > segment.startIndex)
455+
#expect(!segment.text.isEmpty)
456+
#expect(!support.groundingChunkIndices.isEmpty)
457+
458+
// Ensure indices point to valid chunks
459+
for index in support.groundingChunkIndices {
460+
#expect(index < groundingMetadata.groundingChunks.count)
461+
}
462+
}
463+
}
464+
427465
@Test(arguments: InstanceConfig.allConfigs)
428466
func generateContent_codeExecution_succeeds(_ config: InstanceConfig) async throws {
429467
let model = FirebaseAI.componentInstance(config).generativeModel(

0 commit comments

Comments
 (0)