Skip to content
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

[Vertex AI] Fix caching of Vertex AI instances #14007

Merged
merged 3 commits into from
Oct 31, 2024
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
5 changes: 5 additions & 0 deletions FirebaseVertexAI/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 11.5.0
- [fixed] Fixed an issue where `VertexAI.vertexAI(app: app1)` and
`VertexAI.vertexAI(app: app2)` would return the same instance if their
`location` was the same, including the default `us-central1`. (#14007)

# 11.4.0
- [feature] Vertex AI in Firebase is now Generally Available (GA) and can be
used in production apps. (#13725)
Expand Down
9 changes: 5 additions & 4 deletions FirebaseVertexAI/Sources/VertexAI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ public class VertexAI {
// Unlock before the function returns.
defer { os_unfair_lock_unlock(&instancesLock) }

if let instance = instances[location] {
let instanceKey = "\(app.name):\(location)"
if let instance = instances[instanceKey] {
return instance
}
let newInstance = VertexAI(app: app, location: location)
instances[location] = newInstance
instances[instanceKey] = newInstance
return newInstance
}

Expand Down Expand Up @@ -116,8 +117,8 @@ public class VertexAI {

private let auth: AuthInterop?

/// A map of active `VertexAI` instances for `app`, keyed by model resource names
/// (e.g., "projects/my-project-id/locations/us-central1/publishers/google/models/gemini-pro").
/// A map of active `VertexAI` instances keyed by the `FirebaseApp` name and the `location`, in
/// the format `appName:location`.
private static var instances: [String: VertexAI] = [:]

/// Lock to manage access to the `instances` array to avoid race conditions.
Expand Down
67 changes: 47 additions & 20 deletions FirebaseVertexAI/Tests/Unit/VertexComponentTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,21 @@ import XCTest
class VertexComponentTests: XCTestCase {
static let projectID = "test-project-id"
static let apiKey = "test-api-key"
static let options = {
let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000",
gcmSenderID: "00000000000000000-00000000000-000000000")
options.projectID = VertexComponentTests.projectID
options.apiKey = VertexComponentTests.apiKey

static var app: FirebaseApp?
return options
}()

let location = "test-location"
static let app = {
FirebaseApp.configure(options: options)
return FirebaseApp(instanceWithName: "test", options: options)
}()

override class func setUp() {
super.setUp()
if app == nil {
let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000",
gcmSenderID: "00000000000000000-00000000000-000000000")
options.projectID = VertexComponentTests.projectID
options.apiKey = VertexComponentTests.apiKey
FirebaseApp.configure(options: options)
app = FirebaseApp(instanceWithName: "test", options: options)
}
}
let location = "test-location"

/// Test that the objc class is available for the component system to update the user agent.
func testComponentsBeingRegistered() throws {
Expand All @@ -48,27 +47,55 @@ class VertexComponentTests: XCTestCase {

/// Tests that a vertex instance can be created properly.
func testVertexInstanceCreation() throws {
let app = try XCTUnwrap(VertexComponentTests.app)

let vertex = VertexAI.vertexAI(app: app, location: location)
let vertex = VertexAI.vertexAI(app: VertexComponentTests.app, location: location)

XCTAssertNotNil(vertex)
XCTAssertEqual(vertex.projectID, VertexComponentTests.projectID)
XCTAssertEqual(vertex.apiKey, VertexComponentTests.apiKey)
XCTAssertEqual(vertex.location, location)
}

/// Tests that a vertex instances are reused properly.
func testMultipleComponentInstancesCreated() throws {
/// Tests that Vertex instances are reused properly.
func testSameAppAndLocation_instanceReused() throws {
let app = try XCTUnwrap(VertexComponentTests.app)

let vertex1 = VertexAI.vertexAI(app: app, location: location)
let vertex2 = VertexAI.vertexAI(app: app, location: location)

// Ensure they're the same instance.
XCTAssert(vertex1 === vertex2)
}

func testSameAppAndDifferentLocation_newInstanceCreated() throws {
let vertex1 = VertexAI.vertexAI(app: VertexComponentTests.app, location: location)
let vertex2 = VertexAI.vertexAI(app: VertexComponentTests.app, location: "differentLocation")

// Ensure they are different instances.
XCTAssert(vertex1 !== vertex2)
}

func testDifferentAppAndSameLocation_newInstanceCreated() throws {
FirebaseApp.configure(name: "test-2", options: VertexComponentTests.options)
let app2 = FirebaseApp(instanceWithName: "test-2", options: VertexComponentTests.options)
addTeardownBlock { await app2.delete() }

let vertex1 = VertexAI.vertexAI(app: VertexComponentTests.app, location: location)
let vertex2 = VertexAI.vertexAI(app: app2, location: location)

XCTAssert(VertexComponentTests.app != app2)
XCTAssert(vertex1 !== vertex2) // Ensure they are different instances.
}

func testDifferentAppAndDifferentLocation_newInstanceCreated() throws {
FirebaseApp.configure(name: "test-2", options: VertexComponentTests.options)
let app2 = FirebaseApp(instanceWithName: "test-2", options: VertexComponentTests.options)
addTeardownBlock { await app2.delete() }

let vertex1 = VertexAI.vertexAI(app: VertexComponentTests.app, location: location)
let vertex2 = VertexAI.vertexAI(app: app2, location: "differentLocation")

let vertex3 = VertexAI.vertexAI(app: app, location: "differentLocation")
XCTAssert(vertex1 !== vertex3)
XCTAssert(VertexComponentTests.app != app2)
XCTAssert(vertex1 !== vertex2) // Ensure they are different instances.
}

/// Test that vertex instances get deallocated.
Expand Down
Loading