Skip to content
Merged
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/FirebaseAI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,28 @@ public final class FirebaseAI: Sendable {
)
}

// TODO: Remove the `#if compiler(>=6.2)` when Xcode 26 is the minimum supported version.
#if compiler(>=6.2)
/// Creates a new `GenerativeModelSession` with the given model.
///
/// - Important: **Public Preview** - This API is a public preview and may be subject to change.
///
/// - Parameters:
/// - model: The name of the model to use; see [available model names
/// ](https://firebase.google.com/docs/vertex-ai/gemini-models#available-model-names)
/// for a list of supported model names.
/// - instructions: System instructions that direct the model's behavior.
public func generativeModelSession(model: String,
instructions: String? = nil) -> GenerativeModelSession {
let model = generativeModel(
modelName: model,
systemInstruction: instructions.map { ModelContent(role: "system", parts: $0) }
)

return GenerativeModelSession(model: model)
}
#endif // compiler(>=6.2)

/// Initializes an ``ImagenModel`` with the given parameters.
///
/// - Note: Refer to [Imagen models](https://firebase.google.com/docs/vertex-ai/models) for
Expand Down
30 changes: 16 additions & 14 deletions FirebaseAI/Sources/GenerativeModelSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@
import FoundationModels
#endif // canImport(FoundationModels)

/// A session that simplifies interaction with a generative model, particularly for generating
/// structured data.
/// A session that handles multi-turn interactions with a generative model, similar to ``Chat``.
///
/// A `GenerativeModelSession` is ideal for single-turn requests to a model, where you want to
/// decode the model's output into a specific Swift type that conforms to the `Generable`
/// protocol.
/// A `GenerativeModelSession` retains history between requests. For single-turn requests to a
/// model, use ``FirebaseAI/generativeModelSession(model:instructions:)`` to start a new session.
/// `GenerativeModelSession` is particularly useful for generating structured data.
///
/// **Public Preview**: This API is a public preview and may be subject to change.
///
Expand All @@ -42,8 +41,8 @@
/// var favoriteTopics: [String]
/// }
///
/// let model = // ... a GenerativeModel instance
/// let session = GenerativeModelSession(model: model)
/// let firebaseAI = // ... a `FirebaseAI` instance
/// let session = firebaseAI.generativeModelSession(model: "gemini-model-name")
/// let prompt = "Generate a user profile for a cat lover who enjoys hiking."
/// let response = try await session.respond(to: prompt, generating: UserProfile.self)
///
Expand All @@ -59,7 +58,7 @@
///
/// **Public Preview**: This API is a public preview and may be subject to change.
/// - Parameter model: The `GenerativeModel` to use for generating content.
public init(model: GenerativeModel) {
init(model: GenerativeModel) {
session = model.startChat()
}

Expand Down Expand Up @@ -103,11 +102,13 @@
@available(tvOS, unavailable)
@available(watchOS, unavailable)
@discardableResult
public nonisolated(nonsending)
func respond(to prompt: PartsRepresentable..., schema: GenerationSchema,
nonisolated(nonsending)
func respond(to prompt: PartsRepresentable..., schema: FoundationModels.GenerationSchema,
includeSchemaInPrompt: Bool = true,
options: GenerationConfig? = nil) async throws
-> GenerativeModelSession.Response<FirebaseAI.GeneratedContent> {
// TODO: Replace `FoundationModels.GenerationSchema` and make this method public when
// `FirebaseAI.GenerationSchema`'s public API is ready.
return try await respond(
to: prompt,
schema: FirebaseAI.GenerationSchema(schema),
Expand Down Expand Up @@ -163,13 +164,14 @@
@available(iOS 26.0, macOS 26.0, *)
@available(tvOS, unavailable)
@available(watchOS, unavailable)
public func streamResponse(to prompt: PartsRepresentable...,
schema: GenerationSchema,
includeSchemaInPrompt: Bool = true,
options: GenerationConfig? = nil)
func streamResponse(to prompt: PartsRepresentable...,
schema: FoundationModels.GenerationSchema,
includeSchemaInPrompt: Bool = true, options: GenerationConfig? = nil)
-> sending GenerativeModelSession.ResponseStream<
FirebaseAI.GeneratedContent, FirebaseAI.GeneratedContent
> {
// TODO: Replace `FoundationModels.GenerationSchema` and make this method public when
// `FirebaseAI.GenerationSchema`'s public API is ready.
return streamResponse(
to: prompt,
schema: FirebaseAI.GenerationSchema(schema),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

// TODO: Remove the `#if compiler(>=6.2)` when Xcode 26 is the minimum supported version.
#if compiler(>=6.2)
import FirebaseAILogic
@testable import FirebaseAILogic
import FirebaseAITestApp
#if canImport(FoundationModels)
import FoundationModels
Expand All @@ -25,10 +25,8 @@
struct GenerativeModelSessionTests {
@Test(arguments: [InstanceConfig.vertexAI_v1beta_global, InstanceConfig.googleAI_v1beta])
func respondText(_ config: InstanceConfig) async throws {
let model = FirebaseAI.componentInstance(config).generativeModel(
modelName: ModelNames.gemini2_5_FlashLite,
)
let session = GenerativeModelSession(model: model)
let firebaseAI = FirebaseAI.componentInstance(config)
let session = firebaseAI.generativeModelSession(model: ModelNames.gemini2_5_FlashLite)
let prompt = "Why is the sky blue?"

let response = try await session.respond(to: prompt)
Expand Down Expand Up @@ -64,10 +62,8 @@
@available(tvOS, unavailable)
@available(watchOS, unavailable)
func respondGeneratedContent(_ config: InstanceConfig) async throws {
let model = FirebaseAI.componentInstance(config).generativeModel(
modelName: ModelNames.gemini2_5_FlashLite,
)
let session = GenerativeModelSession(model: model)
let firebaseAI = FirebaseAI.componentInstance(config)
let session = firebaseAI.generativeModelSession(model: ModelNames.gemini2_5_FlashLite)
let prompt = "Generate a cute rescue cat"

let response = try await session.respond(to: prompt, schema: CatProfile.generationSchema)
Expand All @@ -90,10 +86,8 @@
@available(tvOS, unavailable)
@available(watchOS, unavailable)
func respondGenerable(_ config: InstanceConfig) async throws {
let model = FirebaseAI.componentInstance(config).generativeModel(
modelName: ModelNames.gemini2_5_FlashLite,
)
let session = GenerativeModelSession(model: model)
let firebaseAI = FirebaseAI.componentInstance(config)
let session = firebaseAI.generativeModelSession(model: ModelNames.gemini2_5_FlashLite)
let prompt = "Generate a Ragdoll kitten"

let response = try await session.respond(to: prompt, generating: CatProfile.self)
Expand Down Expand Up @@ -168,10 +162,8 @@
@available(tvOS, unavailable)
@available(watchOS, unavailable)
func respondGenerableRecipe(_ config: InstanceConfig) async throws {
let model = FirebaseAI.componentInstance(config).generativeModel(
modelName: ModelNames.gemini2_5_FlashLite,
)
let session = GenerativeModelSession(model: model)
let firebaseAI = FirebaseAI.componentInstance(config)
let session = firebaseAI.generativeModelSession(model: ModelNames.gemini2_5_FlashLite)
let prompt = "Generate a recipe for a pasta dish with meat."

let response = try await session.respond(to: prompt, generating: Recipe.self)
Expand All @@ -194,10 +186,8 @@
@available(tvOS, unavailable)
@available(watchOS, unavailable)
func respondGenerableRecipeList(_ config: InstanceConfig) async throws {
let model = FirebaseAI.componentInstance(config).generativeModel(
modelName: ModelNames.gemini2_5_FlashLite,
)
let session = GenerativeModelSession(model: model)
let firebaseAI = FirebaseAI.componentInstance(config)
let session = firebaseAI.generativeModelSession(model: ModelNames.gemini2_5_FlashLite)
let prompt =
"Generate three recipes for a full-course vegetarian meal (appetizer, main, dessert)."

Expand Down Expand Up @@ -226,10 +216,8 @@

@Test(arguments: [InstanceConfig.vertexAI_v1beta_global, InstanceConfig.googleAI_v1beta])
func streamResponseText(_ config: InstanceConfig) async throws {
let model = FirebaseAI.componentInstance(config).generativeModel(
modelName: ModelNames.gemini2_5_FlashLite,
)
let session = GenerativeModelSession(model: model)
let firebaseAI = FirebaseAI.componentInstance(config)
let session = firebaseAI.generativeModelSession(model: ModelNames.gemini2_5_FlashLite)
let prompt = "Why is the sky blue?"

let stream = session.streamResponse(to: prompt)
Expand Down Expand Up @@ -272,10 +260,8 @@
@available(tvOS, unavailable)
@available(watchOS, unavailable)
func streamResponseGeneratedContent(_ config: InstanceConfig) async throws {
let model = FirebaseAI.componentInstance(config).generativeModel(
modelName: ModelNames.gemini2_5_FlashLite,
)
let session = GenerativeModelSession(model: model)
let firebaseAI = FirebaseAI.componentInstance(config)
let session = firebaseAI.generativeModelSession(model: ModelNames.gemini2_5_FlashLite)
let prompt = "Generate a friendly Persian cat"

let stream = session.streamResponse(
Expand Down Expand Up @@ -328,10 +314,8 @@
@available(tvOS, unavailable)
@available(watchOS, unavailable)
func streamResponseGenerable(_ config: InstanceConfig) async throws {
let model = FirebaseAI.componentInstance(config).generativeModel(
modelName: ModelNames.gemini2_5_FlashLite,
)
let session = GenerativeModelSession(model: model)
let firebaseAI = FirebaseAI.componentInstance(config)
let session = firebaseAI.generativeModelSession(model: ModelNames.gemini2_5_FlashLite)
let prompt = "Generate a Ragdoll kitten"

let stream = session.streamResponse(
Expand Down
Loading