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] Update logging details from Google AI SDK #12933

Merged
merged 3 commits into from
May 10, 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
10 changes: 6 additions & 4 deletions FirebaseVertexAI/Sources/GenerateContentResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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: " ")
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down
20 changes: 10 additions & 10 deletions FirebaseVertexAI/Sources/GenerativeAIService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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:`.
Expand Down Expand Up @@ -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)")
}
}

Expand All @@ -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",
Expand Down Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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)
------------------------
Expand Down
25 changes: 17 additions & 8 deletions FirebaseVertexAI/Sources/GenerativeModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 19 additions & 4 deletions FirebaseVertexAI/Sources/Logging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that subsystem is redundant with [FirebaseVertexAI]? I'm fine to leave that as a future question.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for bringing it up, @paulb777. It is largely redundant given that a Firebase dev would need to filter by tag (like [FirebaseVertexAI]) since the rest of our SDKs print like this. However, if we were to migrate everything to OSLog in the future it would actually be [FirebaseVertexAI] that would be redundant (along with -FIRDebugEnabled). Rather than setting a log level as a launch argument in Xcode, with OSLog it's easy to filter by levels and subsystems in the Xcode UI, e.g.:
Xcode OSLog Screenshot
In the above, I've enabled the option to display the subsystem and to filter by it, as well as log level. Definitely more flexible to tweak these as needed without having to restart the app. These options are also available in the Console app.

For now I'll leave it in (alternative is an empty string, like we did for category, since the parameter is non-optional) but we can adjust later or migrate to FirebaseLogger.


/// 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)
Expand All @@ -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.
Expand All @@ -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
}
}
6 changes: 3 additions & 3 deletions FirebaseVertexAI/Sources/Safety.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand All @@ -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
}
Expand Down
Loading