Skip to content

[Firebase AI] Add URLContext tool #15221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions FirebaseAI/Sources/Tool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ public struct GoogleSearch: Sendable {
public init() {}
}

@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
public struct URLContext: Sendable {
public init() {}
}

/// A helper tool that the model may use when generating responses.
///
/// A `Tool` is a piece of code that enables the system to interact with external systems to perform
Expand All @@ -74,14 +79,24 @@ public struct Tool: Sendable {
/// Specifies the Google Search configuration.
let googleSearch: GoogleSearch?

let urlContext: URLContext?

init(functionDeclarations: [FunctionDeclaration]?) {
self.functionDeclarations = functionDeclarations
googleSearch = nil
urlContext = nil
}

init(googleSearch: GoogleSearch) {
self.googleSearch = googleSearch
functionDeclarations = nil
urlContext = nil
}

init(urlContext: URLContext) {
self.urlContext = urlContext
functionDeclarations = nil
googleSearch = nil
}

/// Creates a tool that allows the model to perform function calling.
Expand Down Expand Up @@ -126,6 +141,10 @@ public struct Tool: Sendable {
public static func googleSearch(_ googleSearch: GoogleSearch = GoogleSearch()) -> Tool {
return self.init(googleSearch: googleSearch)
}

public static func urlContext(_ urlContext: URLContext = URLContext()) -> Tool {
return self.init(urlContext: urlContext)
}
}

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

@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
extension URLContext: Encodable {}

@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
extension ToolConfig: Encodable {}
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,44 @@ struct GenerateContentIntegrationTests {
}
}

@Test(
"generateContent with URL Context",
arguments: InstanceConfig.allConfigs
)
func generateContent_withURLContext_succeeds(_ config: InstanceConfig) async throws {
let model = FirebaseAI.componentInstance(config).generativeModel(
modelName: ModelNames.gemini2_5_Flash,
tools: [.urlContext()]
)
let prompt = """
Write a one paragraph summary of this blog post: \
https://developers.googleblog.com/en/introducing-gemma-3-270m/
"""

let response = try await model.generateContent(prompt)

let candidate = try #require(response.candidates.first)
let groundingMetadata = try #require(candidate.groundingMetadata)
#expect(!groundingMetadata.groundingChunks.isEmpty)
#expect(!groundingMetadata.groundingSupports.isEmpty)

for chunk in groundingMetadata.groundingChunks {
#expect(chunk.web != nil)
}

for support in groundingMetadata.groundingSupports {
let segment = support.segment
#expect(segment.endIndex > segment.startIndex)
#expect(!segment.text.isEmpty)
#expect(!support.groundingChunkIndices.isEmpty)

// Ensure indices point to valid chunks
for index in support.groundingChunkIndices {
#expect(index < groundingMetadata.groundingChunks.count)
}
}
}

// MARK: Streaming Tests

@Test(arguments: [
Expand Down
Loading