Skip to content

Commit 801cbb3

Browse files
authored
[Vertex AI] Add Vertex AI in Firebase API enablement logging (#13724)
1 parent 7bb68ad commit 801cbb3

File tree

6 files changed

+87
-6
lines changed

6 files changed

+87
-6
lines changed

FirebaseVertexAI/Sources/Constants.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,8 @@ import Foundation
2121
/// Constants associated with the Vertex AI for Firebase SDK.
2222
enum Constants {
2323
/// The Vertex AI backend endpoint URL.
24+
///
25+
/// TODO(andrewheard): Update to "https://firebasevertexai.googleapis.com" after the Vertex AI in
26+
/// Firebase API launch.
2427
static let baseURL = "https://firebaseml.googleapis.com"
2528
}

FirebaseVertexAI/Sources/Errors.swift

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,14 @@ struct RPCError: Error {
3131
self.details = details
3232
}
3333

34+
// TODO(andrewheard): Remove this method after the Vertex AI in Firebase API launch.
3435
func isFirebaseMLServiceDisabledError() -> Bool {
3536
return details.contains { $0.isFirebaseMLServiceDisabledErrorDetails() }
3637
}
38+
39+
func isVertexAIInFirebaseServiceDisabledError() -> Bool {
40+
return details.contains { $0.isVertexAIInFirebaseServiceDisabledErrorDetails() }
41+
}
3742
}
3843

3944
extension RPCError: Decodable {
@@ -86,17 +91,26 @@ struct ErrorDetails {
8691
return type == ErrorDetails.errorInfoType
8792
}
8893

94+
func isServiceDisabledError() -> Bool {
95+
return isErrorInfo() && reason == "SERVICE_DISABLED" && domain == "googleapis.com"
96+
}
97+
98+
// TODO(andrewheard): Remove this method after the Vertex AI in Firebase API launch.
8999
func isFirebaseMLServiceDisabledErrorDetails() -> Bool {
90-
guard isErrorInfo() else {
100+
guard isServiceDisabledError() else {
91101
return false
92102
}
93-
guard reason == "SERVICE_DISABLED" else {
103+
guard let metadata, metadata["service"] == "firebaseml.googleapis.com" else {
94104
return false
95105
}
96-
guard domain == "googleapis.com" else {
106+
return true
107+
}
108+
109+
func isVertexAIInFirebaseServiceDisabledErrorDetails() -> Bool {
110+
guard isServiceDisabledError() else {
97111
return false
98112
}
99-
guard let metadata, metadata["service"] == "firebaseml.googleapis.com" else {
113+
guard let metadata, metadata["service"] == "firebasevertexai.googleapis.com" else {
100114
return false
101115
}
102116
return true

FirebaseVertexAI/Sources/GenerativeAIRequest.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public struct RequestOptions {
3131
let timeout: TimeInterval?
3232

3333
/// The API version to use in requests to the backend.
34+
///
35+
/// TODO(andrewheard): Update to "v1beta" after the Vertex AI in Firebase API launch.
3436
let apiVersion = "v2beta"
3537

3638
/// Initializes a request options object.

FirebaseVertexAI/Sources/GenerativeAIService.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,9 @@ struct GenerativeAIService {
266266
// Log specific RPC errors that cannot be mitigated or handled by user code.
267267
// These errors do not produce specific GenerateContentError or CountTokensError cases.
268268
private func logRPCError(_ error: RPCError) {
269+
// TODO(andrewheard): Remove this check after the Vertex AI in Firebase API launch.
269270
if error.isFirebaseMLServiceDisabledError() {
270-
VertexLog.error(code: .firebaseMLAPIDisabled, """
271+
VertexLog.error(code: .vertexAIInFirebaseAPIDisabled, """
271272
The Vertex AI for Firebase SDK requires the Firebase ML API `firebaseml.googleapis.com` to \
272273
be enabled for your project. Get started in the Firebase Console \
273274
(https://console.firebase.google.com/project/\(projectID)/genai/vertex) or verify that the \
@@ -276,6 +277,15 @@ struct GenerativeAIService {
276277
\(projectID)).
277278
""")
278279
}
280+
281+
if error.isVertexAIInFirebaseServiceDisabledError() {
282+
VertexLog.error(code: .vertexAIInFirebaseAPIDisabled, """
283+
The Vertex AI for Firebase SDK requires the Firebase Vertex AI API \
284+
`firebasevertexai.googleapis.com` to be enabled for your project. Get started by visiting \
285+
the Firebase Console at: \
286+
https://console.firebase.google.com/project/\(projectID)/genai/vertex
287+
""")
288+
}
279289
}
280290

281291
private func parseResponse<T: Decodable>(_ type: T.Type, from data: Data) throws -> T {

FirebaseVertexAI/Sources/VertexLog.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ enum VertexLog {
2929
case verboseLoggingEnabled = 101
3030

3131
// API Enablement Errors
32-
case firebaseMLAPIDisabled = 200
32+
case vertexAIInFirebaseAPIDisabled = 200
3333

3434
// Model Configuration
3535
case generativeModelInitialized = 1000

FirebaseVertexAI/Tests/Unit/GenerativeModelTests.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ final class GenerativeModelTests: XCTestCase {
453453
}
454454
}
455455

456+
// TODO(andrewheard): Remove this test case after the Vertex AI in Firebase API launch.
456457
func testGenerateContent_failure_firebaseMLAPINotEnabled() async throws {
457458
let expectedStatusCode = 403
458459
MockURLProtocol
@@ -476,6 +477,30 @@ final class GenerativeModelTests: XCTestCase {
476477
}
477478
}
478479

480+
func testGenerateContent_failure_firebaseVertexAIAPINotEnabled() async throws {
481+
let expectedStatusCode = 403
482+
MockURLProtocol
483+
.requestHandler = try httpRequestHandler(
484+
forResource: "unary-failure-firebasevertexai-api-not-enabled",
485+
withExtension: "json",
486+
statusCode: expectedStatusCode
487+
)
488+
489+
do {
490+
_ = try await model.generateContent(testPrompt)
491+
XCTFail("Should throw GenerateContentError.internalError; no error thrown.")
492+
} catch let GenerateContentError.internalError(error as RPCError) {
493+
XCTAssertEqual(error.httpResponseCode, expectedStatusCode)
494+
XCTAssertEqual(error.status, .permissionDenied)
495+
XCTAssertTrue(error.message
496+
.starts(with: "Vertex AI in Firebase API has not been used in project"))
497+
XCTAssertTrue(error.isVertexAIInFirebaseServiceDisabledError())
498+
return
499+
} catch {
500+
XCTFail("Should throw GenerateContentError.internalError(RPCError); error thrown: \(error)")
501+
}
502+
}
503+
479504
func testGenerateContent_failure_emptyContent() async throws {
480505
MockURLProtocol
481506
.requestHandler = try httpRequestHandler(
@@ -774,6 +799,7 @@ final class GenerativeModelTests: XCTestCase {
774799
XCTFail("Should have caught an error.")
775800
}
776801

802+
// TODO(andrewheard): Remove this test case after the Vertex AI in Firebase API launch.
777803
func testGenerateContentStream_failure_firebaseMLAPINotEnabled() async throws {
778804
let expectedStatusCode = 403
779805
MockURLProtocol
@@ -799,6 +825,32 @@ final class GenerativeModelTests: XCTestCase {
799825
XCTFail("Should have caught an error.")
800826
}
801827

828+
func testGenerateContentStream_failure_vertexAIInFirebaseAPINotEnabled() async throws {
829+
let expectedStatusCode = 403
830+
MockURLProtocol
831+
.requestHandler = try httpRequestHandler(
832+
forResource: "unary-failure-firebasevertexai-api-not-enabled",
833+
withExtension: "json",
834+
statusCode: expectedStatusCode
835+
)
836+
837+
do {
838+
let stream = try model.generateContentStream(testPrompt)
839+
for try await _ in stream {
840+
XCTFail("No content is there, this shouldn't happen.")
841+
}
842+
} catch let GenerateContentError.internalError(error as RPCError) {
843+
XCTAssertEqual(error.httpResponseCode, expectedStatusCode)
844+
XCTAssertEqual(error.status, .permissionDenied)
845+
XCTAssertTrue(error.message
846+
.starts(with: "Vertex AI in Firebase API has not been used in project"))
847+
XCTAssertTrue(error.isVertexAIInFirebaseServiceDisabledError())
848+
return
849+
}
850+
851+
XCTFail("Should have caught an error.")
852+
}
853+
802854
func testGenerateContentStream_failureEmptyContent() async throws {
803855
MockURLProtocol
804856
.requestHandler = try httpRequestHandler(

0 commit comments

Comments
 (0)