Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
6 changes: 3 additions & 3 deletions FirebaseAI/Tests/TestApp/Sources/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ public enum FirebaseAppNames {
public enum ModelNames {
public static let gemini2Flash = "gemini-2.0-flash-001"
public static let gemini2FlashLite = "gemini-2.0-flash-lite-001"
public static let gemini2FlashLive = "gemini-2.0-flash-live-001"
public static let gemini2FlashLivePreview = "gemini-2.0-flash-live-preview-04-09"
public static let gemini2_5_FlashImage = "gemini-2.5-flash-image"
public static let gemini2_5_Flash = "gemini-2.5-flash"
public static let gemini2_5_FlashLite = "gemini-2.5-flash-lite"
public static let gemini2_5_FlashLivePreview = "gemini-live-2.5-flash-preview"
public static let gemini2_5_FlashLive = "gemini-live-2.5-flash-native-audio"
public static let gemini2_5_FlashLivePreview = "gemini-2.5-flash-native-audio-preview-12-2025"
public static let gemini2_5_Pro = "gemini-2.5-pro"
public static let gemini3FlashPreview = "gemini-3-flash-preview"
public static let gemma3_4B = "gemma-3-4b-it"
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ struct GenerateContentIntegrationTests {
(InstanceConfig.vertexAI_v1beta_global_appCheckLimitedUse, ModelNames.gemini2FlashLite),
(InstanceConfig.googleAI_v1beta, ModelNames.gemini2FlashLite),
(InstanceConfig.googleAI_v1beta_appCheckLimitedUse, ModelNames.gemini2FlashLite),
(InstanceConfig.googleAI_v1beta, ModelNames.gemini3FlashPreview),
(InstanceConfig.googleAI_v1beta_appCheckLimitedUse, ModelNames.gemini3FlashPreview),
(InstanceConfig.googleAI_v1beta, ModelNames.gemma3_4B),
(InstanceConfig.googleAI_v1beta_freeTier, ModelNames.gemini2FlashLite),
(InstanceConfig.googleAI_v1beta_freeTier, ModelNames.gemma3_4B),
// Note: The following configs are commented out for easy one-off manual testing.
// (InstanceConfig.googleAI_v1beta_freeTier, ModelNames.gemini2FlashLite),
// (InstanceConfig.googleAI_v1beta_staging, ModelNames.gemini2FlashLite),
// (InstanceConfig.googleAI_v1beta_staging, ModelNames.gemma3_4B),
// (InstanceConfig.vertexAI_v1beta_staging, ModelNames.gemini2FlashLite),
Expand All @@ -82,10 +84,17 @@ struct GenerateContentIntegrationTests {
let promptTokensDetails = try #require(usageMetadata.promptTokensDetails.first)
#expect(promptTokensDetails.modality == .text)
#expect(promptTokensDetails.tokenCount == usageMetadata.promptTokenCount)
#expect(usageMetadata.thoughtsTokenCount == 0)
if modelName.hasPrefix("gemini-3") {
#expect(usageMetadata.thoughtsTokenCount == 64)
} else {
#expect(usageMetadata.thoughtsTokenCount == 0)
}
// The fields `candidatesTokenCount` and `candidatesTokensDetails` are not included when using
// Gemma models.
if modelName.hasPrefix("gemma") {
if modelName.hasPrefix("gemini-3") {
#expect(usageMetadata.candidatesTokenCount == 2)
#expect(usageMetadata.candidatesTokensDetails.isEmpty)
} else if modelName.hasPrefix("gemma") {
#expect(usageMetadata.candidatesTokenCount == 0)
#expect(usageMetadata.candidatesTokensDetails.isEmpty)
} else {
Expand All @@ -95,9 +104,9 @@ struct GenerateContentIntegrationTests {
#expect(candidatesTokensDetails.modality == .text)
#expect(candidatesTokensDetails.tokenCount == usageMetadata.candidatesTokenCount)
}
#expect(usageMetadata.totalTokenCount > 0)
#expect(usageMetadata.totalTokenCount ==
(usageMetadata.promptTokenCount + usageMetadata.candidatesTokenCount))
#expect(usageMetadata.totalTokenCount == (usageMetadata.promptTokenCount +
usageMetadata.candidatesTokenCount +
usageMetadata.thoughtsTokenCount))
}

@Test(
Expand Down Expand Up @@ -161,16 +170,21 @@ struct GenerateContentIntegrationTests {
(.googleAI_v1beta, ModelNames.gemini2_5_Pro, ThinkingConfig(
thinkingBudget: 32768, includeThoughts: true
)),
(.googleAI_v1beta_freeTier, ModelNames.gemini2_5_Flash, ThinkingConfig(thinkingBudget: 0)),
(
.googleAI_v1beta_freeTier,
ModelNames.gemini2_5_Flash,
ThinkingConfig(thinkingBudget: 24576)
),
(.googleAI_v1beta_freeTier, ModelNames.gemini2_5_Flash, ThinkingConfig(
thinkingBudget: 24576, includeThoughts: true
(.googleAI_v1beta, ModelNames.gemini3FlashPreview, ThinkingConfig(thinkingBudget: 128)),
(.googleAI_v1beta, ModelNames.gemini3FlashPreview, ThinkingConfig(thinkingBudget: 32768)),
(.googleAI_v1beta, ModelNames.gemini3FlashPreview, ThinkingConfig(
thinkingBudget: 32768, includeThoughts: true
)),
// Note: The following configs are commented out for easy one-off manual testing.
// (.googleAI_v1beta_freeTier, ModelNames.gemini2_5_Flash, ThinkingConfig(thinkingBudget: 0)),
// (
// .googleAI_v1beta_freeTier,
// ModelNames.gemini2_5_Flash,
// ThinkingConfig(thinkingBudget: 24576)
// ),
// (.googleAI_v1beta_freeTier, ModelNames.gemini2_5_Flash, ThinkingConfig(
// thinkingBudget: 24576, includeThoughts: true
// )),
// (.googleAI_v1beta_freeTier_bypassProxy, ModelNames.gemini2_5_Flash, ThinkingConfig(
// thinkingBudget: 0
// )),
Expand Down Expand Up @@ -255,6 +269,10 @@ struct GenerateContentIntegrationTests {
(.googleAI_v1beta, ModelNames.gemini2_5_Pro, ThinkingConfig(
thinkingBudget: -1, includeThoughts: true
)),
(.googleAI_v1beta, ModelNames.gemini3FlashPreview, ThinkingConfig(thinkingBudget: -1)),
(.googleAI_v1beta, ModelNames.gemini3FlashPreview, ThinkingConfig(
thinkingBudget: -1, includeThoughts: true
)),
] as [(InstanceConfig, String, ThinkingConfig)]
)
func generateContentThinkingFunctionCalling(_ config: InstanceConfig, modelName: String,
Expand Down Expand Up @@ -470,19 +488,19 @@ struct GenerateContentIntegrationTests {

@Test(arguments: [
(InstanceConfig.vertexAI_v1beta, ModelNames.gemini2FlashLite),
(InstanceConfig.vertexAI_v1beta_global, ModelNames.gemini2FlashLite),
(InstanceConfig.vertexAI_v1beta_global_appCheckLimitedUse, ModelNames.gemini2FlashLite),
(InstanceConfig.vertexAI_v1beta_global, ModelNames.gemini3FlashPreview),
(InstanceConfig.vertexAI_v1beta_global_appCheckLimitedUse, ModelNames.gemini3FlashPreview),
(InstanceConfig.googleAI_v1beta, ModelNames.gemini2FlashLite),
(InstanceConfig.googleAI_v1beta_appCheckLimitedUse, ModelNames.gemini2FlashLite),
(InstanceConfig.googleAI_v1beta, ModelNames.gemma3_4B),
(InstanceConfig.googleAI_v1beta_freeTier, ModelNames.gemini2FlashLite),
(InstanceConfig.googleAI_v1beta_freeTier, ModelNames.gemma3_4B),
// Note: The following configs are commented out for easy one-off manual testing.
// (InstanceConfig.vertexAI_v1beta_staging, ModelNames.gemini2FlashLite),
// (InstanceConfig.googleAI_v1beta_staging, ModelNames.gemini2FlashLite),
// (InstanceConfig.googleAI_v1beta_staging, ModelNames.gemma3_4B),
// (InstanceConfig.googleAI_v1beta_freeTier_bypassProxy, ModelNames.gemini2FlashLite),
// (InstanceConfig.googleAI_v1beta_freeTier_bypassProxy, ModelNames.gemma3_4B),
// (InstanceConfig.googleAI_v1beta_freeTier, ModelNames.gemini2FlashLite),
// (InstanceConfig.googleAI_v1beta_freeTier, ModelNames.gemma3_4B),
])
func generateContentStream(_ config: InstanceConfig, modelName: String) async throws {
let expectedResponse = [
Expand Down
89 changes: 6 additions & 83 deletions FirebaseAI/Tests/TestApp/Tests/Integration/LiveSessionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ struct LiveSessionTests {
switch config.apiConfig.service {
case .vertexAI:
[
(config, ModelNames.gemini2FlashLivePreview),
(config, ModelNames.gemini2_5_FlashLive),
]
case .googleAI:
[
(config, ModelNames.gemini2FlashLive),
(config, ModelNames.gemini2_5_FlashLivePreview),
]
}
Expand Down Expand Up @@ -91,51 +90,6 @@ struct LiveSessionTests {
)
}

@Test(arguments: arguments)
func sendTextRealtime_receiveText(_ config: InstanceConfig, modelName: String) async throws {
let model = FirebaseAI.componentInstance(config).liveModel(
modelName: modelName,
generationConfig: textConfig,
systemInstruction: SystemInstructions.yesOrNo
)

let session = try await model.connect()
await session.sendTextRealtime("Does five plus five equal ten?")

let text = try await session.collectNextTextResponse()

await session.close()
let modelResponse = text
.trimmingCharacters(in: .whitespacesAndNewlines)
.trimmingCharacters(in: .punctuationCharacters)
.lowercased()

#expect(modelResponse == "yes")
}

@Test(arguments: arguments)
func sendTextRealtime_receiveAudioOutputTranscripts(_ config: InstanceConfig,
modelName: String) async throws {
let model = FirebaseAI.componentInstance(config).liveModel(
modelName: modelName,
generationConfig: audioConfig,
systemInstruction: SystemInstructions.yesOrNo
)

let session = try await model.connect()
await session.sendTextRealtime("Does five plus five equal ten?")

let text = try await session.collectNextAudioOutputTranscript()

await session.close()
let modelResponse = text
.trimmingCharacters(in: .whitespacesAndNewlines)
.trimmingCharacters(in: .punctuationCharacters)
.lowercased()

#expect(modelResponse == "yes")
}

@Test(arguments: arguments)
func sendAudioRealtime_receiveAudioOutputTranscripts(_ config: InstanceConfig,
modelName: String) async throws {
Expand Down Expand Up @@ -165,36 +119,7 @@ struct LiveSessionTests {
#expect(modelResponse == "goodbye")
}

@Test(arguments: arguments)
func sendAudioRealtime_receiveText(_ config: InstanceConfig, modelName: String) async throws {
let model = FirebaseAI.componentInstance(config).liveModel(
modelName: modelName,
generationConfig: textConfig,
systemInstruction: SystemInstructions.helloGoodbye
)

let session = try await model.connect()

let audioFile = try #require(
NSDataAsset(name: "hello"), "Missing audio file 'hello.wav' in Assets"
)
await session.sendAudioRealtime(audioFile.data)
await session.sendAudioRealtime(Data(repeating: 0, count: audioFile.data.count))

let text = try await session.collectNextTextResponse()

await session.close()
let modelResponse = text
.trimmingCharacters(in: .whitespacesAndNewlines)
.trimmingCharacters(in: .punctuationCharacters)
.lowercased()

#expect(modelResponse == "goodbye")
}
Comment thread
andrewheard marked this conversation as resolved.

@Test(arguments: arguments.filter { $0.1 != ModelNames.gemini2FlashLive })
// gemini-2.0-flash-live-001 is buggy and likes to respond to the audio or system instruction
// (eg; it will say 'okay' or 'hello', instead of following the instructions)
@Test(.disabled("Temporarily disabled"), arguments: arguments)
func sendVideoRealtime_receiveText(_ config: InstanceConfig, modelName: String) async throws {
let model = FirebaseAI.componentInstance(config).liveModel(
modelName: modelName,
Expand Down Expand Up @@ -235,7 +160,7 @@ struct LiveSessionTests {
#expect(["kitten", "cat", "kitty"].contains(modelResponse))
}

@Test(arguments: arguments)
@Test(.disabled("Temporarily disabled"), arguments: arguments)
func realtime_functionCalling(_ config: InstanceConfig, modelName: String) async throws {
let model = FirebaseAI.componentInstance(config).liveModel(
modelName: modelName,
Expand Down Expand Up @@ -283,7 +208,7 @@ struct LiveSessionTests {
#expect(modelResponse == "smith")
}

@Test(arguments: arguments.filter {
@Test(.disabled("Temporarily disabled"), arguments: arguments.filter {
// TODO: (b/450982184) Remove when Vertex AI adds support for Function IDs and Cancellation
switch $0.0.apiConfig.service {
case .googleAI:
Expand Down Expand Up @@ -324,9 +249,7 @@ struct LiveSessionTests {
await session.close()
}

@Test(
arguments: arguments.filter { !$0.0.useLimitedUseAppCheckTokens }
)
@Test(arguments: arguments.filter { !$0.0.useLimitedUseAppCheckTokens })
// Getting a limited use token adds too much of an overhead; we can't interrupt the model in time
func realtime_interruption(_ config: InstanceConfig, modelName: String) async throws {
let model = FirebaseAI.componentInstance(config).liveModel(
Expand Down Expand Up @@ -360,7 +283,7 @@ struct LiveSessionTests {
}
}

@Test(arguments: arguments)
@Test(.disabled("Temporarily disabled"), arguments: arguments)
func incremental_works(_ config: InstanceConfig, modelName: String) async throws {
let model = FirebaseAI.componentInstance(config).liveModel(
modelName: modelName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ struct InstanceConfig: Equatable, Encodable {
vertexAI_v1beta_global_appCheckLimitedUse,
googleAI_v1beta,
googleAI_v1beta_appCheckLimitedUse,
googleAI_v1beta_freeTier,
// Note: The following configs are commented out for easy one-off manual testing.
// googleAI_v1beta_freeTier,
// vertexAI_v1beta_staging,
// vertexAI_v1beta_staging_global_bypassProxy,
// googleAI_v1beta_staging,
Expand Down
Loading