VertexComponentTests.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // Copyright 2024 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. @preconcurrency import FirebaseCore
  15. internal import FirebaseCoreExtension
  16. import Foundation
  17. import XCTest
  18. @testable import FirebaseAILogic
  19. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  20. class VertexComponentTests: XCTestCase {
  21. static let projectID = "test-project-id"
  22. static let apiKey = "test-api-key"
  23. static let options = {
  24. let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000",
  25. gcmSenderID: "00000000000000000-00000000000-000000000")
  26. options.projectID = VertexComponentTests.projectID
  27. options.apiKey = VertexComponentTests.apiKey
  28. return options
  29. }()
  30. static let app = FirebaseApp(instanceWithName: "test", options: options)
  31. let location = "test-location"
  32. let modelName = "test-model-name"
  33. let systemInstruction = ModelContent(role: "system", parts: "test-system-instruction-prompt")
  34. override class func setUp() {
  35. FirebaseApp.configure(options: options)
  36. guard FirebaseApp.app() != nil else {
  37. fatalError("The default app does not exist.")
  38. }
  39. }
  40. /// Test that the objc class is available for the component system to update the user agent.
  41. func testComponentsBeingRegistered() throws {
  42. XCTAssertNotNil(NSClassFromString("FIRVertexAIComponent"))
  43. }
  44. /// Tests that a vertex instance can be created properly using the default Firebase app.
  45. func testVertexInstanceCreation_defaultApp() throws {
  46. let vertex = FirebaseAI.firebaseAI(backend: .vertexAI())
  47. XCTAssertNotNil(vertex)
  48. XCTAssertEqual(vertex.firebaseInfo.projectID, VertexComponentTests.projectID)
  49. XCTAssertEqual(vertex.firebaseInfo.apiKey, VertexComponentTests.apiKey)
  50. XCTAssertEqual(
  51. vertex.apiConfig.service, .vertexAI(endpoint: .firebaseProxyProd, location: "us-central1")
  52. )
  53. XCTAssertEqual(vertex.apiConfig.service.endpoint, .firebaseProxyProd)
  54. XCTAssertEqual(vertex.apiConfig.version, .v1beta)
  55. }
  56. /// Tests that a vertex instance can be created properly using the default Firebase app and custom
  57. /// location.
  58. func testVertexInstanceCreation_defaultApp_customLocation() throws {
  59. let vertex = FirebaseAI.firebaseAI(backend: .vertexAI(location: location))
  60. XCTAssertNotNil(vertex)
  61. XCTAssertEqual(vertex.firebaseInfo.projectID, VertexComponentTests.projectID)
  62. XCTAssertEqual(vertex.firebaseInfo.apiKey, VertexComponentTests.apiKey)
  63. XCTAssertEqual(
  64. vertex.apiConfig.service, .vertexAI(endpoint: .firebaseProxyProd, location: location)
  65. )
  66. XCTAssertEqual(vertex.apiConfig.service.endpoint, .firebaseProxyProd)
  67. XCTAssertEqual(vertex.apiConfig.version, .v1beta)
  68. }
  69. /// Tests that a vertex instance can be created properly.
  70. func testVertexInstanceCreation_customApp() throws {
  71. let vertex = FirebaseAI.firebaseAI(
  72. app: VertexComponentTests.app,
  73. backend: .vertexAI(location: location)
  74. )
  75. XCTAssertNotNil(vertex)
  76. XCTAssertEqual(vertex.firebaseInfo.projectID, VertexComponentTests.projectID)
  77. XCTAssertEqual(vertex.firebaseInfo.apiKey, VertexComponentTests.apiKey)
  78. XCTAssertEqual(
  79. vertex.apiConfig.service, .vertexAI(endpoint: .firebaseProxyProd, location: location)
  80. )
  81. XCTAssertEqual(vertex.apiConfig.service.endpoint, .firebaseProxyProd)
  82. XCTAssertEqual(vertex.apiConfig.version, .v1beta)
  83. }
  84. /// Tests that Vertex instances are reused properly.
  85. func testSameAppAndLocation_instanceReused() throws {
  86. let app = try XCTUnwrap(VertexComponentTests.app)
  87. let vertex1 = FirebaseAI.firebaseAI(app: app, backend: .vertexAI(location: location))
  88. let vertex2 = FirebaseAI.firebaseAI(app: app, backend: .vertexAI(location: location))
  89. // Ensure they're the same instance.
  90. XCTAssert(vertex1 === vertex2)
  91. }
  92. func testSameAppAndDifferentLocation_newInstanceCreated() throws {
  93. let vertex1 = FirebaseAI.firebaseAI(
  94. app: VertexComponentTests.app,
  95. backend: .vertexAI(location: location)
  96. )
  97. let vertex2 = FirebaseAI.firebaseAI(
  98. app: VertexComponentTests.app,
  99. backend: .vertexAI(location: "differentLocation")
  100. )
  101. // Ensure they are different instances.
  102. XCTAssert(vertex1 !== vertex2)
  103. }
  104. func testDifferentAppAndSameLocation_newInstanceCreated() throws {
  105. FirebaseApp.configure(name: "test-2", options: VertexComponentTests.options)
  106. let app2 = FirebaseApp(instanceWithName: "test-2", options: VertexComponentTests.options)
  107. addTeardownBlock { await app2.delete() }
  108. let vertex1 = FirebaseAI.firebaseAI(
  109. app: VertexComponentTests.app,
  110. backend: .vertexAI(location: location)
  111. )
  112. let vertex2 = FirebaseAI.firebaseAI(app: app2, backend: .vertexAI(location: location))
  113. XCTAssert(VertexComponentTests.app != app2)
  114. XCTAssert(vertex1 !== vertex2) // Ensure they are different instances.
  115. }
  116. func testDifferentAppAndDifferentLocation_newInstanceCreated() throws {
  117. FirebaseApp.configure(name: "test-2", options: VertexComponentTests.options)
  118. let app2 = FirebaseApp(instanceWithName: "test-2", options: VertexComponentTests.options)
  119. addTeardownBlock { await app2.delete() }
  120. let vertex1 = FirebaseAI.firebaseAI(
  121. app: VertexComponentTests.app,
  122. backend: .vertexAI(location: location)
  123. )
  124. let vertex2 = FirebaseAI.firebaseAI(
  125. app: app2,
  126. backend: .vertexAI(location: "differentLocation")
  127. )
  128. XCTAssert(VertexComponentTests.app != app2)
  129. XCTAssert(vertex1 !== vertex2) // Ensure they are different instances.
  130. }
  131. func testSameAppAndDifferentAPI_newInstanceCreated() throws {
  132. let vertex1 = FirebaseAI.createInstance(
  133. app: VertexComponentTests.app,
  134. apiConfig: APIConfig(
  135. service: .vertexAI(endpoint: .firebaseProxyProd, location: location),
  136. version: .v1beta
  137. ),
  138. useLimitedUseAppCheckTokens: false
  139. )
  140. let vertex2 = FirebaseAI.createInstance(
  141. app: VertexComponentTests.app,
  142. apiConfig: APIConfig(
  143. service: .vertexAI(endpoint: .firebaseProxyProd, location: location), version: .v1
  144. ),
  145. useLimitedUseAppCheckTokens: false
  146. )
  147. // Ensure they are different instances.
  148. XCTAssert(vertex1 !== vertex2)
  149. }
  150. /// Test that vertex instances get deallocated.
  151. func testVertexLifecycle() throws {
  152. weak var weakApp: FirebaseApp?
  153. weak var weakVertex: FirebaseAI?
  154. try autoreleasepool {
  155. let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000",
  156. gcmSenderID: "00000000000000000-00000000000-000000000")
  157. options.projectID = VertexComponentTests.projectID
  158. options.apiKey = VertexComponentTests.apiKey
  159. let app1 = FirebaseApp(instanceWithName: "transitory app", options: options)
  160. weakApp = try XCTUnwrap(app1)
  161. let vertex = FirebaseAI(
  162. app: app1,
  163. apiConfig: APIConfig(
  164. service: .vertexAI(endpoint: .firebaseProxyProd, location: "transitory location"),
  165. version: .v1beta
  166. ),
  167. useLimitedUseAppCheckTokens: false
  168. )
  169. weakVertex = vertex
  170. XCTAssertNotNil(weakVertex)
  171. }
  172. XCTAssertNil(weakApp)
  173. XCTAssertNil(weakVertex)
  174. }
  175. func testModelResourceName_vertexAI() throws {
  176. let app = try XCTUnwrap(VertexComponentTests.app)
  177. let location = "test-location"
  178. let vertex = FirebaseAI.firebaseAI(app: app, backend: .vertexAI(location: location))
  179. let model = "test-model-name"
  180. let projectID = vertex.firebaseInfo.projectID
  181. let modelResourceName = vertex.modelResourceName(modelName: model)
  182. XCTAssertEqual(
  183. modelResourceName,
  184. "projects/\(projectID)/locations/\(location)/publishers/google/models/\(model)"
  185. )
  186. }
  187. func testModelResourceName_developerAPI_generativeLanguage() throws {
  188. let app = try XCTUnwrap(VertexComponentTests.app)
  189. let apiConfig = APIConfig(service: .googleAI(endpoint: .googleAIBypassProxy), version: .v1beta)
  190. let vertex = FirebaseAI.createInstance(
  191. app: app, apiConfig: apiConfig, useLimitedUseAppCheckTokens: false
  192. )
  193. let model = "test-model-name"
  194. let modelResourceName = vertex.modelResourceName(modelName: model)
  195. XCTAssertEqual(modelResourceName, "models/\(model)")
  196. }
  197. func testModelResourceName_developerAPI_firebaseVertexAI() throws {
  198. let app = try XCTUnwrap(VertexComponentTests.app)
  199. let apiConfig = APIConfig(
  200. service: .googleAI(endpoint: .firebaseProxyStaging),
  201. version: .v1beta
  202. )
  203. let vertex = FirebaseAI.createInstance(
  204. app: app, apiConfig: apiConfig, useLimitedUseAppCheckTokens: false
  205. )
  206. let model = "test-model-name"
  207. let projectID = vertex.firebaseInfo.projectID
  208. let modelResourceName = vertex.modelResourceName(modelName: model)
  209. XCTAssertEqual(modelResourceName, "projects/\(projectID)/models/\(model)")
  210. }
  211. func testGenerativeModel_vertexAI_defaultLocation() async throws {
  212. let app = try XCTUnwrap(VertexComponentTests.app)
  213. let vertex = FirebaseAI.firebaseAI(app: app, backend: .vertexAI())
  214. let modelResourceName = vertex.modelResourceName(modelName: modelName)
  215. let expectedSystemInstruction = ModelContent(role: nil, parts: systemInstruction.parts)
  216. let generativeModel = vertex.generativeModel(
  217. modelName: modelName, systemInstruction: systemInstruction
  218. )
  219. XCTAssertEqual(generativeModel.modelResourceName, modelResourceName)
  220. XCTAssertEqual(generativeModel.systemInstruction, expectedSystemInstruction)
  221. XCTAssertEqual(generativeModel.apiConfig, FirebaseAI.defaultVertexAIAPIConfig)
  222. }
  223. func testGenerativeModel_vertexAI_customLocation() async throws {
  224. let app = try XCTUnwrap(VertexComponentTests.app)
  225. let vertex = FirebaseAI.firebaseAI(app: app, backend: .vertexAI(location: location))
  226. let modelResourceName = vertex.modelResourceName(modelName: modelName)
  227. let expectedAPIConfig = APIConfig(
  228. service: .vertexAI(endpoint: .firebaseProxyProd, location: location), version: .v1beta
  229. )
  230. let expectedSystemInstruction = ModelContent(role: nil, parts: systemInstruction.parts)
  231. let generativeModel = vertex.generativeModel(
  232. modelName: modelName, systemInstruction: systemInstruction
  233. )
  234. XCTAssertEqual(generativeModel.modelResourceName, modelResourceName)
  235. XCTAssertEqual(generativeModel.systemInstruction, expectedSystemInstruction)
  236. XCTAssertEqual(generativeModel.apiConfig, expectedAPIConfig)
  237. }
  238. func testGenerativeModel_developerAPI() async throws {
  239. let app = try XCTUnwrap(VertexComponentTests.app)
  240. let apiConfig = APIConfig(
  241. service: .googleAI(endpoint: .firebaseProxyStaging),
  242. version: .v1beta
  243. )
  244. let vertex = FirebaseAI.createInstance(
  245. app: app, apiConfig: apiConfig, useLimitedUseAppCheckTokens: false
  246. )
  247. let modelResourceName = vertex.modelResourceName(modelName: modelName)
  248. let expectedSystemInstruction = ModelContent(role: nil, parts: systemInstruction.parts)
  249. let generativeModel = vertex.generativeModel(
  250. modelName: modelName,
  251. systemInstruction: systemInstruction
  252. )
  253. XCTAssertEqual(generativeModel.modelResourceName, modelResourceName)
  254. XCTAssertEqual(generativeModel.systemInstruction, expectedSystemInstruction)
  255. XCTAssertEqual(generativeModel.apiConfig, apiConfig)
  256. }
  257. }