LiveSnippets.swift 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // Copyright 2025 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import FirebaseAILogic
  15. import FirebaseCore
  16. import XCTest
  17. // These snippet tests are intentionally skipped in CI jobs; see the README file in this directory
  18. // for instructions on running them manually.
  19. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  20. @available(watchOS, unavailable)
  21. final class LiveSnippets: XCTestCase {
  22. override func setUpWithError() throws {
  23. try FirebaseApp.configureDefaultAppForSnippets()
  24. }
  25. override func tearDown() async throws {
  26. await FirebaseApp.deleteDefaultAppForSnippets()
  27. }
  28. func sendAudioReceiveAudio() async throws {
  29. // Initialize the Vertex AI Gemini API backend service
  30. // Set the location to `us-central1` (the flash-live model is only supported in that location)
  31. // Create a `LiveGenerativeModel` instance with the flash-live model (only model that supports
  32. // the Live API)
  33. let model = FirebaseAI.firebaseAI(backend: .vertexAI(location: "us-central1")).liveModel(
  34. modelName: "gemini-2.0-flash-exp",
  35. // Configure the model to respond with audio
  36. generationConfig: LiveGenerationConfig(
  37. responseModalities: [.audio]
  38. )
  39. )
  40. do {
  41. let session = try await model.connect()
  42. // Load the audio file, or tap a microphone
  43. guard let audioFile = NSDataAsset(name: "audio.pcm") else {
  44. fatalError("Failed to load audio file")
  45. }
  46. // Provide the audio data
  47. await session.sendAudioRealtime(audioFile.data)
  48. for try await message in session.responses {
  49. if case let .content(content) = message.payload {
  50. content.modelTurn?.parts.forEach { part in
  51. if let part = part as? InlineDataPart, part.mimeType.starts(with: "audio/pcm") {
  52. // Handle 16bit pcm audio data at 24khz
  53. playAudio(part.data)
  54. }
  55. }
  56. // Optional: if you don't require to send more requests.
  57. if content.isTurnComplete {
  58. await session.close()
  59. }
  60. }
  61. }
  62. } catch {
  63. fatalError(error.localizedDescription)
  64. }
  65. }
  66. func sendAudioReceiveText() async throws {
  67. // Initialize the Vertex AI Gemini API backend service
  68. // Set the location to `us-central1` (the flash-live model is only supported in that location)
  69. // Create a `LiveGenerativeModel` instance with the flash-live model (only model that supports
  70. // the Live API)
  71. let model = FirebaseAI.firebaseAI(backend: .googleAI()).liveModel(
  72. modelName: "gemini-live-2.5-flash-preview",
  73. // Configure the model to respond with text
  74. generationConfig: LiveGenerationConfig(
  75. responseModalities: [.text]
  76. )
  77. )
  78. do {
  79. let session = try await model.connect()
  80. // Load the audio file, or tap a microphone
  81. guard let audioFile = NSDataAsset(name: "audio.pcm") else {
  82. fatalError("Failed to load audio file")
  83. }
  84. // Provide the audio data
  85. await session.sendAudioRealtime(audioFile.data)
  86. var outputText = ""
  87. for try await message in session.responses {
  88. if case let .content(content) = message.payload {
  89. content.modelTurn?.parts.forEach { part in
  90. if let part = part as? TextPart {
  91. outputText += part.text
  92. }
  93. }
  94. // Optional: if you don't require to send more requests.
  95. if content.isTurnComplete {
  96. await session.close()
  97. }
  98. }
  99. }
  100. // Output received from the server.
  101. print(outputText)
  102. } catch {
  103. fatalError(error.localizedDescription)
  104. }
  105. }
  106. func sendTextReceiveAudio() async throws {
  107. // Initialize the Gemini Developer API backend service
  108. // Create a `LiveModel` instance with the flash-live model (only model that supports the Live
  109. // API)
  110. let model = FirebaseAI.firebaseAI(backend: .googleAI()).liveModel(
  111. modelName: "gemini-live-2.5-flash-preview",
  112. // Configure the model to respond with audio
  113. generationConfig: LiveGenerationConfig(
  114. responseModalities: [.audio]
  115. )
  116. )
  117. do {
  118. let session = try await model.connect()
  119. // Provide a text prompt
  120. let text = "tell a short story"
  121. await session.sendTextRealtime(text)
  122. for try await message in session.responses {
  123. if case let .content(content) = message.payload {
  124. content.modelTurn?.parts.forEach { part in
  125. if let part = part as? InlineDataPart, part.mimeType.starts(with: "audio/pcm") {
  126. // Handle 16bit pcm audio data at 24khz
  127. playAudio(part.data)
  128. }
  129. }
  130. // Optional: if you don't require to send more requests.
  131. if content.isTurnComplete {
  132. await session.close()
  133. }
  134. }
  135. }
  136. } catch {
  137. fatalError(error.localizedDescription)
  138. }
  139. }
  140. func sendTextReceiveText() async throws {
  141. // Initialize the Gemini Developer API backend service
  142. // Create a `LiveModel` instance with the flash-live model (only model that supports the Live
  143. // API)
  144. let model = FirebaseAI.firebaseAI(backend: .googleAI()).liveModel(
  145. modelName: "gemini-live-2.5-flash-preview",
  146. // Configure the model to respond with audio
  147. generationConfig: LiveGenerationConfig(
  148. responseModalities: [.audio]
  149. )
  150. )
  151. do {
  152. let session = try await model.connect()
  153. // Provide a text prompt
  154. let text = "tell a short story"
  155. await session.sendTextRealtime(text)
  156. for try await message in session.responses {
  157. if case let .content(content) = message.payload {
  158. content.modelTurn?.parts.forEach { part in
  159. if let part = part as? InlineDataPart, part.mimeType.starts(with: "audio/pcm") {
  160. // Handle 16bit pcm audio data at 24khz
  161. playAudio(part.data)
  162. }
  163. }
  164. // Optional: if you don't require to send more requests.
  165. if content.isTurnComplete {
  166. await session.close()
  167. }
  168. }
  169. }
  170. } catch {
  171. fatalError(error.localizedDescription)
  172. }
  173. }
  174. func changeVoiceAndLanguage() {
  175. let model = FirebaseAI.firebaseAI(backend: .googleAI()).liveModel(
  176. modelName: "gemini-live-2.5-flash-preview",
  177. // Configure the model to use a specific voice for its audio response
  178. generationConfig: LiveGenerationConfig(
  179. responseModalities: [.audio],
  180. speech: SpeechConfig(voiceName: "Fenrir")
  181. )
  182. )
  183. // Not part of snippet
  184. silenceWarning(model)
  185. }
  186. func modelParameters() {
  187. // ...
  188. // Set parameter values in a `LiveGenerationConfig` (example values shown here)
  189. let config = LiveGenerationConfig(
  190. temperature: 0.9,
  191. topP: 0.1,
  192. topK: 16,
  193. maxOutputTokens: 200,
  194. responseModalities: [.audio],
  195. speech: SpeechConfig(voiceName: "Fenrir")
  196. )
  197. // Initialize the Vertex AI Gemini API backend service
  198. // Specify the config as part of creating the `LiveGenerativeModel` instance
  199. let model = FirebaseAI.firebaseAI(backend: .googleAI()).liveModel(
  200. modelName: "gemini-live-2.5-flash-preview",
  201. generationConfig: config
  202. )
  203. // ...
  204. // Not part of snippet
  205. silenceWarning(model)
  206. }
  207. func systemInstructions() {
  208. // Specify the system instructions as part of creating the `LiveGenerativeModel` instance
  209. let model = FirebaseAI.firebaseAI(backend: .googleAI()).liveModel(
  210. modelName: "gemini-live-2.5-flash-preview",
  211. systemInstruction: ModelContent(role: "system", parts: "You are a cat. Your name is Neko.")
  212. )
  213. // Not part of snippet
  214. silenceWarning(model)
  215. }
  216. private func playAudio(_ data: Data) {
  217. // Use AVAudioPlayerNode or something akin to play back audio
  218. }
  219. /// This function only exists to silence the "unused value" warnings.
  220. ///
  221. /// This allows us to ensure the snippets match devsite.
  222. private func silenceWarning(_ model: LiveGenerativeModel) {}
  223. }