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] Migrate logging to FirebaseLogger #13638

Merged
merged 4 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
24 changes: 16 additions & 8 deletions FirebaseVertexAI/Sources/GenerateContentResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ public struct GenerateContentResponse: Sendable {
/// The response's content as text, if it exists.
public var text: String? {
guard let candidate = candidates.first else {
Logging.default
.error("[FirebaseVertexAI] Could not get text from a response that had no candidates.")
VertexLog.error(
code: .generateContentResponseNoCandidates,
"Could not get text from a response that had no candidates."
)
return nil
}
let textValues: [String] = candidate.content.parts.compactMap { part in
Expand All @@ -53,8 +55,10 @@ public struct GenerateContentResponse: Sendable {
return text
}
guard textValues.count > 0 else {
Logging.default
.error("[FirebaseVertexAI] Could not get a text part from the first candidate.")
VertexLog.error(
code: .generateContentResponseNoText,
"Could not get a text part from the first candidate."
)
return nil
}
return textValues.joined(separator: " ")
Expand Down Expand Up @@ -330,8 +334,10 @@ extension FinishReason: Decodable {
public init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer().decode(String.self)
guard let decodedFinishReason = FinishReason(rawValue: value) else {
Logging.default
.error("[FirebaseVertexAI] Unrecognized FinishReason with value \"\(value)\".")
VertexLog.error(
code: .generateContentResponseUnrecognizedFinishReason,
"Unrecognized FinishReason with value \"\(value)\"."
)
self = .unknown
return
}
Expand All @@ -345,8 +351,10 @@ extension PromptFeedback.BlockReason: Decodable {
public init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer().decode(String.self)
guard let decodedBlockReason = PromptFeedback.BlockReason(rawValue: value) else {
Logging.default
.error("[FirebaseVertexAI] Unrecognized BlockReason with value \"\(value)\".")
VertexLog.error(
code: .generateContentResponseUnrecognizedBlockReason,
"Unrecognized BlockReason with value \"\(value)\"."
)
self = .unknown
return
}
Expand Down
54 changes: 37 additions & 17 deletions FirebaseVertexAI/Sources/GenerativeAIService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import FirebaseAppCheckInterop
import FirebaseAuthInterop
import FirebaseCore
import Foundation
import os.log

@available(iOS 15.0, macOS 11.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
struct GenerativeAIService {
Expand Down Expand Up @@ -60,9 +61,15 @@ struct GenerativeAIService {

// Verify the status code is 200
guard response.statusCode == 200 else {
Logging.network.error("[FirebaseVertexAI] The server responded with an error: \(response)")
VertexLog.error(
code: .loadRequestResponseError,
"The server responded with an error: \(response)"
)
if let responseString = String(data: data, encoding: .utf8) {
Logging.default.error("[FirebaseVertexAI] Response payload: \(responseString)")
VertexLog.error(
code: .loadRequestResponseErrorPayload,
"Response payload: \(responseString)"
)
}

throw parseError(responseData: data)
Expand Down Expand Up @@ -108,14 +115,19 @@ struct GenerativeAIService {

// Verify the status code is 200
guard response.statusCode == 200 else {
Logging.network
.error("[FirebaseVertexAI] The server responded with an error: \(response)")
VertexLog.error(
code: .loadRequestStreamResponseError,
"The server responded with an error: \(response)"
)
var responseBody = ""
for try await line in stream.lines {
responseBody += line + "\n"
}

Logging.default.error("[FirebaseVertexAI] Response payload: \(responseBody)")
VertexLog.error(
code: .loadRequestStreamResponseErrorPayload,
"Response payload: \(responseBody)"
)
continuation.finish(throwing: parseError(responseBody: responseBody))

return
Expand All @@ -127,7 +139,7 @@ struct GenerativeAIService {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
for try await line in stream.lines {
Logging.network.debug("[FirebaseVertexAI] Stream response: \(line)")
VertexLog.debug(code: .loadRequestStreamResponseLine, "Stream response: \(line)")

if line.hasPrefix("data:") {
// We can assume 5 characters since it's utf-8 encoded, removing `data:`.
Expand Down Expand Up @@ -179,8 +191,10 @@ struct GenerativeAIService {
let tokenResult = await appCheck.getToken(forcingRefresh: false)
urlRequest.setValue(tokenResult.token, forHTTPHeaderField: "X-Firebase-AppCheck")
if let error = tokenResult.error {
Logging.default
.debug("[FirebaseVertexAI] Failed to fetch AppCheck token. Error: \(error)")
VertexLog.error(
code: .appCheckTokenFetchFailed,
"Failed to fetch AppCheck token. Error: \(error)"
)
}
}

Expand All @@ -202,10 +216,10 @@ struct GenerativeAIService {
private func httpResponse(urlResponse: URLResponse) throws -> HTTPURLResponse {
// Verify the status code is 200
guard let response = urlResponse as? HTTPURLResponse else {
Logging.default
.error(
"[FirebaseVertexAI] Response wasn't an HTTP response, internal error \(urlResponse)"
)
VertexLog.error(
code: .generativeAIServiceNonHTTPResponse,
"Response wasn't an HTTP response, internal error \(urlResponse)"
)
throw NSError(
domain: "com.google.generative-ai",
code: -1,
Expand Down Expand Up @@ -253,7 +267,7 @@ struct GenerativeAIService {
// These errors do not produce specific GenerateContentError or CountTokensError cases.
private func logRPCError(_ error: RPCError) {
if error.isFirebaseMLServiceDisabledError() {
Logging.default.error("""
VertexLog.error(code: .firebaseMLAPIDisabled, """
The Vertex AI for Firebase SDK requires the Firebase ML API `firebaseml.googleapis.com` to \
be enabled for your project. Get started in the Firebase Console \
(https://console.firebase.google.com/project/\(projectID)/genai/vertex) or verify that the \
Expand All @@ -269,9 +283,12 @@ struct GenerativeAIService {
return try JSONDecoder().decode(type, from: data)
} catch {
if let json = String(data: data, encoding: .utf8) {
Logging.network.error("[FirebaseVertexAI] JSON response: \(json)")
VertexLog.error(code: .loadRequestParseResponseFailedJSON, "JSON response: \(json)")
}
Logging.default.error("[FirebaseVertexAI] Error decoding server JSON: \(error)")
VertexLog.error(
code: .loadRequestParseResponseFailedJSONError,
"Error decoding server JSON: \(error)"
)
throw error
}
}
Expand All @@ -297,9 +314,12 @@ struct GenerativeAIService {
}

private func printCURLCommand(from request: URLRequest) {
guard VertexLog.additionalLoggingEnabled() else {
return
}
let command = cURLCommand(from: request)
Logging.verbose.debug("""
[FirebaseVertexAI] Creating request with the equivalent cURL command:
os_log(.debug, log: VertexLog.logObject, """
\(VertexLog.service) Creating request with the equivalent cURL command:
----- cURL command -----
\(command, privacy: .private)
------------------------
Expand Down
18 changes: 5 additions & 13 deletions FirebaseVertexAI/Sources/GenerativeModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,23 +85,15 @@ public final class GenerativeModel {
self.systemInstruction = systemInstruction
self.requestOptions = requestOptions

if Logging.additionalLoggingEnabled() {
if ProcessInfo.processInfo.arguments.contains(Logging.migrationEnableArgumentKey) {
Logging.verbose.debug("""
[FirebaseVertexAI] Verbose logging enabled with the \
\(Logging.migrationEnableArgumentKey, privacy: .public) launch argument; please migrate to \
the \(Logging.enableArgumentKey, privacy: .public) argument to ensure future compatibility.
""")
} else {
Logging.verbose.debug("[FirebaseVertexAI] Verbose logging enabled.")
}
if VertexLog.additionalLoggingEnabled() {
VertexLog.debug(code: .verboseLoggingEnabled, "Verbose logging enabled.")
} else {
Logging.default.info("""
VertexLog.info(code: .verboseLoggingDisabled, """
[FirebaseVertexAI] To enable additional logging, add \
`\(Logging.enableArgumentKey, privacy: .public)` as a launch argument in Xcode.
`\(VertexLog.enableArgumentKey)` as a launch argument in Xcode.
""")
}
Logging.default.debug("[FirebaseVertexAI] Model \(name, privacy: .public) initialized.")
VertexLog.debug(code: .generativeModelInitialized, "Model \(name) initialized.")
}

/// Generates content from String and/or image inputs, given to the model as a prompt, that are
Expand Down
71 changes: 0 additions & 71 deletions FirebaseVertexAI/Sources/Logging.swift

This file was deleted.

12 changes: 8 additions & 4 deletions FirebaseVertexAI/Sources/Safety.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,10 @@ extension SafetyRating.HarmProbability: Decodable {
public init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer().decode(String.self)
guard let decodedProbability = SafetyRating.HarmProbability(rawValue: value) else {
Logging.default
.error("[FirebaseVertexAI] Unrecognized HarmProbability with value \"\(value)\".")
VertexLog.error(
code: .generateContentResponseUnrecognizedHarmProbability,
"Unrecognized HarmProbability with value \"\(value)\"."
)
self = .unknown
return
}
Expand All @@ -139,8 +141,10 @@ extension HarmCategory: Codable {
public init(from decoder: Decoder) throws {
let value = try decoder.singleValueContainer().decode(String.self)
guard let decodedCategory = HarmCategory(rawValue: value) else {
Logging.default
.error("[FirebaseVertexAI] Unrecognized HarmCategory with value \"\(value)\".")
VertexLog.error(
code: .generateContentResponseUnrecognizedHarmCategory,
"Unrecognized HarmCategory with value \"\(value)\"."
)
self = .unknown
return
}
Expand Down
Loading
Loading