From e36744bc34cb34c88f65d8c0bf94356a1f91ac5b Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Fri, 10 May 2024 14:07:49 -0400 Subject: [PATCH] [Vertex AI] Update logging details from Google AI SDK (#12933) --- .../Sources/GenerateContentResponse.swift | 10 +++++--- .../Sources/GenerativeAIService.swift | 20 +++++++-------- .../Sources/GenerativeModel.swift | 25 +++++++++++++------ FirebaseVertexAI/Sources/Logging.swift | 23 ++++++++++++++--- FirebaseVertexAI/Sources/Safety.swift | 6 ++--- 5 files changed, 55 insertions(+), 29 deletions(-) diff --git a/FirebaseVertexAI/Sources/GenerateContentResponse.swift b/FirebaseVertexAI/Sources/GenerateContentResponse.swift index cd53744eeb8c..a152286581b4 100644 --- a/FirebaseVertexAI/Sources/GenerateContentResponse.swift +++ b/FirebaseVertexAI/Sources/GenerateContentResponse.swift @@ -42,7 +42,8 @@ public struct GenerateContentResponse { /// The response's content as text, if it exists. public var text: String? { guard let candidate = candidates.first else { - Logging.default.error("Could not get text from a response that had no candidates.") + Logging.default + .error("[FirebaseVertexAI] Could not get text from a response that had no candidates.") return nil } let textValues: [String] = candidate.content.parts.compactMap { part in @@ -52,7 +53,8 @@ public struct GenerateContentResponse { return text } guard textValues.count > 0 else { - Logging.default.error("Could not get a text part from the first candidate.") + Logging.default + .error("[FirebaseVertexAI] Could not get a text part from the first candidate.") return nil } return textValues.joined(separator: " ") @@ -319,7 +321,7 @@ extension FinishReason: Decodable { let value = try decoder.singleValueContainer().decode(String.self) guard let decodedFinishReason = FinishReason(rawValue: value) else { Logging.default - .error("[GoogleGenerativeAI] Unrecognized FinishReason with value \"\(value)\".") + .error("[FirebaseVertexAI] Unrecognized FinishReason with value \"\(value)\".") self = .unknown return } @@ -334,7 +336,7 @@ extension PromptFeedback.BlockReason: Decodable { let value = try decoder.singleValueContainer().decode(String.self) guard let decodedBlockReason = PromptFeedback.BlockReason(rawValue: value) else { Logging.default - .error("[GoogleGenerativeAI] Unrecognized BlockReason with value \"\(value)\".") + .error("[FirebaseVertexAI] Unrecognized BlockReason with value \"\(value)\".") self = .unknown return } diff --git a/FirebaseVertexAI/Sources/GenerativeAIService.swift b/FirebaseVertexAI/Sources/GenerativeAIService.swift index 7fe0df330636..85944adeda9e 100644 --- a/FirebaseVertexAI/Sources/GenerativeAIService.swift +++ b/FirebaseVertexAI/Sources/GenerativeAIService.swift @@ -56,9 +56,9 @@ struct GenerativeAIService { // Verify the status code is 200 guard response.statusCode == 200 else { - Logging.default.error("[GoogleGenerativeAI] The server responded with an error: \(response)") + Logging.default.error("[FirebaseVertexAI] The server responded with an error: \(response)") if let responseString = String(data: data, encoding: .utf8) { - Logging.network.error("[GoogleGenerativeAI] Response payload: \(responseString)") + Logging.network.error("[FirebaseVertexAI] Response payload: \(responseString)") } throw parseError(responseData: data) @@ -105,13 +105,13 @@ struct GenerativeAIService { // Verify the status code is 200 guard response.statusCode == 200 else { Logging.default - .error("[GoogleGenerativeAI] The server responded with an error: \(response)") + .error("[FirebaseVertexAI] The server responded with an error: \(response)") var responseBody = "" for try await line in stream.lines { responseBody += line + "\n" } - Logging.network.error("[GoogleGenerativeAI] Response payload: \(responseBody)") + Logging.network.error("[FirebaseVertexAI] Response payload: \(responseBody)") continuation.finish(throwing: parseError(responseBody: responseBody)) return @@ -123,7 +123,7 @@ struct GenerativeAIService { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase for try await line in stream.lines { - Logging.network.debug("[GoogleGenerativeAI] Stream response: \(line)") + Logging.network.debug("[FirebaseVertexAI] Stream response: \(line)") if line.hasPrefix("data:") { // We can assume 5 characters since it's utf-8 encoded, removing `data:`. @@ -176,7 +176,7 @@ struct GenerativeAIService { urlRequest.setValue(tokenResult.token, forHTTPHeaderField: "X-Firebase-AppCheck") if let error = tokenResult.error { Logging.default - .debug("[GoogleGenerativeAI] Failed to fetch AppCheck token. Error: \(error)") + .debug("[FirebaseVertexAI] Failed to fetch AppCheck token. Error: \(error)") } } @@ -200,7 +200,7 @@ struct GenerativeAIService { guard let response = urlResponse as? HTTPURLResponse else { Logging.default .error( - "[GoogleGenerativeAI] Response wasn't an HTTP response, internal error \(urlResponse)" + "[FirebaseVertexAI] Response wasn't an HTTP response, internal error \(urlResponse)" ) throw NSError( domain: "com.google.generative-ai", @@ -248,9 +248,9 @@ struct GenerativeAIService { return try JSONDecoder().decode(type, from: data) } catch { if let json = String(data: data, encoding: .utf8) { - Logging.network.error("[GoogleGenerativeAI] JSON response: \(json)") + Logging.network.error("[FirebaseVertexAI] JSON response: \(json)") } - Logging.default.error("[GoogleGenerativeAI] Error decoding server JSON: \(error)") + Logging.default.error("[FirebaseVertexAI] Error decoding server JSON: \(error)") throw error } } @@ -278,7 +278,7 @@ struct GenerativeAIService { private func printCURLCommand(from request: URLRequest) { let command = cURLCommand(from: request) Logging.verbose.debug(""" - [GoogleGenerativeAI] Creating request with the equivalent cURL command: + [FirebaseVertexAI] Creating request with the equivalent cURL command: ----- cURL command ----- \(command, privacy: .private) ------------------------ diff --git a/FirebaseVertexAI/Sources/GenerativeModel.swift b/FirebaseVertexAI/Sources/GenerativeModel.swift index c7be518db085..1a5fc8c15338 100644 --- a/FirebaseVertexAI/Sources/GenerativeModel.swift +++ b/FirebaseVertexAI/Sources/GenerativeModel.swift @@ -86,14 +86,23 @@ public final class GenerativeModel { self.systemInstruction = systemInstruction self.requestOptions = requestOptions - Logging.default.info(""" - [GoogleGenerativeAI] Model \( - name, - privacy: .public - ) initialized. To enable additional logging, add \ - `\(Logging.enableArgumentKey, privacy: .public)` as a launch argument in Xcode. - """) - Logging.verbose.debug("[GoogleGenerativeAI] Verbose logging enabled.") + 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.") + } + } else { + Logging.default.info(""" + [FirebaseVertexAI] To enable additional logging, add \ + `\(Logging.enableArgumentKey, privacy: .public)` as a launch argument in Xcode. + """) + } + Logging.default.debug("[FirebaseVertexAI] Model \(name, privacy: .public) initialized.") } /// Generates content from String and/or image inputs, given to the model as a prompt, that are diff --git a/FirebaseVertexAI/Sources/Logging.swift b/FirebaseVertexAI/Sources/Logging.swift index 458c34ed18da..81587ba20c8a 100644 --- a/FirebaseVertexAI/Sources/Logging.swift +++ b/FirebaseVertexAI/Sources/Logging.swift @@ -18,13 +18,19 @@ import OSLog @available(iOS 15.0, macOS 11.0, macCatalyst 15.0, *) struct Logging { /// Subsystem that should be used for all Loggers. - static let subsystem = "com.google.generative-ai" + static let subsystem = "com.google.firebase.vertex-ai" /// Default category used for most loggers, unless specialized. static let defaultCategory = "" /// The argument required to enable additional logging. - static let enableArgumentKey = "-GoogleGenerativeAIDebugLogEnabled" + static let enableArgumentKey = "-FIRDebugEnabled" + + /// The argument required to enable additional logging in the Google AI SDK; used for migration. + /// + /// To facillitate migration between the SDKs, this launch argument is also accepted to enable + /// additional logging at this time, though it is expected to be removed in the future. + static let migrationEnableArgumentKey = "-GoogleGenerativeAIDebugLogEnabled" // No initializer available. @available(*, unavailable) @@ -36,7 +42,7 @@ struct Logging { /// A non default static var network: Logger = { - if ProcessInfo.processInfo.arguments.contains(enableArgumentKey) { + if additionalLoggingEnabled() { return Logger(subsystem: subsystem, category: "NetworkResponse") } else { // Return a valid logger that's using `OSLog.disabled` as the logger, hiding everything. @@ -46,11 +52,20 @@ struct Logging { /// static var verbose: Logger = { - if ProcessInfo.processInfo.arguments.contains(enableArgumentKey) { + if additionalLoggingEnabled() { return Logger(subsystem: subsystem, category: defaultCategory) } else { // Return a valid logger that's using `OSLog.disabled` as the logger, hiding everything. return Logger(.disabled) } }() + + /// Returns `true` if additional logging has been enabled via a launch argument. + static func additionalLoggingEnabled() -> Bool { + let arguments = ProcessInfo.processInfo.arguments + if arguments.contains(enableArgumentKey) || arguments.contains(migrationEnableArgumentKey) { + return true + } + return false + } } diff --git a/FirebaseVertexAI/Sources/Safety.swift b/FirebaseVertexAI/Sources/Safety.swift index 0541a609b93d..a81ac7d8e6e6 100644 --- a/FirebaseVertexAI/Sources/Safety.swift +++ b/FirebaseVertexAI/Sources/Safety.swift @@ -148,7 +148,7 @@ extension SafetyRating.HarmProbability: Codable { let value = try decoder.singleValueContainer().decode(String.self) guard let decodedProbability = SafetyRating.HarmProbability(rawValue: value) else { Logging.default - .error("[GoogleGenerativeAI] Unrecognized HarmProbability with value \"\(value)\".") + .error("[FirebaseVertexAI] Unrecognized HarmProbability with value \"\(value)\".") self = .unknown return } @@ -169,7 +169,7 @@ extension SafetySetting.HarmCategory: Codable { let value = try decoder.singleValueContainer().decode(String.self) guard let decodedCategory = SafetySetting.HarmCategory(rawValue: value) else { Logging.default - .error("[GoogleGenerativeAI] Unrecognized HarmCategory with value \"\(value)\".") + .error("[FirebaseVertexAI] Unrecognized HarmCategory with value \"\(value)\".") self = .unknown return } @@ -184,7 +184,7 @@ extension SafetySetting.BlockThreshold: Codable { let value = try decoder.singleValueContainer().decode(String.self) guard let decodedThreshold = SafetySetting.BlockThreshold(rawValue: value) else { Logging.default - .error("[GoogleGenerativeAI] Unrecognized BlockThreshold with value \"\(value)\".") + .error("[FirebaseVertexAI] Unrecognized BlockThreshold with value \"\(value)\".") self = .unknown return }