import Foundation public struct ProviderDiagnosticBatchExport: Codable, Sendable { public let schemaVersion: String public let timestamp: Date public let diagnostics: [ProviderDiagnosticExport] public init( schemaVersion: String = "1.0", timestamp: Date, diagnostics: [ProviderDiagnosticExport]) { self.schemaVersion = schemaVersion self.timestamp = timestamp self.diagnostics = diagnostics }} public struct ProviderDiagnosticExport: Codable, Sendable { public let schemaVersion: String public let timestamp: Date public let provider: String public let displayName: String public let source: String public let sourceMode: String public let auth: ProviderDiagnosticAuthSummary public let usage: ProviderDiagnosticUsageSummary? public let fetchAttempts: [ProviderDiagnosticFetchAttempt] public let error: ProviderDiagnosticError? public let settings: ProviderDiagnosticSettingsSummary public let details: ProviderDiagnosticDetails? public init( schemaVersion: String = "1.0", timestamp: Date, provider: String, displayName: String, source: String, sourceMode: String, auth: ProviderDiagnosticAuthSummary, usage: ProviderDiagnosticUsageSummary?, fetchAttempts: [ProviderDiagnosticFetchAttempt], error: ProviderDiagnosticError?, settings: ProviderDiagnosticSettingsSummary, details: ProviderDiagnosticDetails?) { self.schemaVersion = schemaVersion self.timestamp = timestamp self.provider = provider self.displayName = displayName self.source = source self.sourceMode = sourceMode self.auth = auth self.usage = usage self.fetchAttempts = fetchAttempts self.error = error self.settings = settings self.details = details }} public struct ProviderDiagnosticAuthSummary: Codable, Sendable { public let configured: Bool public let modes: [String] public init(configured: Bool, modes: [String]) { self.configured = configured self.modes = modes } public func resolved(with outcome: ProviderFetchOutcome) -> ProviderDiagnosticAuthSummary { var resolvedModes = self.modes if outcome.isSuccess { for attempt in outcome.attempts where attempt.wasAvailable { let mode = ProviderDiagnosticFetchAttempt.kindLabel(attempt.kind) if !resolvedModes.contains(mode) { resolvedModes.append(mode) } } } let configured = self.configured || outcome.isSuccess return ProviderDiagnosticAuthSummary(configured: configured, modes: resolvedModes) }} public struct ProviderDiagnosticUsageSummary: Codable, Sendable { public let updatedAt: Date public let dataConfidence: String public let windows: [ProviderDiagnosticRateWindow] public let extraWindowCount: Int public let providerCostPresent: Bool public let providerSpecificData: [String] private enum CodingKeys: String, CodingKey { case updatedAt case dataConfidence case windows case extraWindowCount case providerCostPresent case providerSpecificData } public init(from snapshot: UsageSnapshot) { var windows: [ProviderDiagnosticRateWindow] = [] if let primary = snapshot.primary { windows.append(ProviderDiagnosticRateWindow(label: "primary", window: primary)) } if let secondary = snapshot.secondary { windows.append(ProviderDiagnosticRateWindow(label: "secondary", window: secondary)) } if let tertiary = snapshot.tertiary { windows.append(ProviderDiagnosticRateWindow(label: "tertiary", window: tertiary)) } for extra in snapshot.extraRateWindows ?? [] { windows.append(ProviderDiagnosticRateWindow( label: extra.title, window: extra.window, usageKnown: extra.usageKnown)) } var providerSpecificData: [String] = [] if snapshot.kiroUsage != nil { providerSpecificData.append("kiroUsage") } if snapshot.ampUsage != nil { providerSpecificData.append("ampUsage") } if snapshot.zaiUsage != nil { providerSpecificData.append("zaiUsage") } if snapshot.minimaxUsage != nil { providerSpecificData.append("minimaxUsage") } if snapshot.deepseekUsage != nil { providerSpecificData.append("deepseekUsage") } if snapshot.openRouterUsage != nil { providerSpecificData.append("openRouterUsage") } if snapshot.openAIAPIUsage != nil { providerSpecificData.append("openAIAPIUsage") } if snapshot.claudeAdminAPIUsage != nil { providerSpecificData.append("claudeAdminAPIUsage") } if snapshot.mistralUsage != nil { providerSpecificData.append("mistralUsage") } if snapshot.deepgramUsage != nil { providerSpecificData.append("deepgramUsage") } if snapshot.cursorRequests != nil { providerSpecificData.append("cursorRequests") } self.updatedAt = snapshot.updatedAt self.dataConfidence = snapshot.dataConfidence.rawValue self.windows = windows self.extraWindowCount = snapshot.extraRateWindows?.count ?? 0 self.providerCostPresent = snapshot.providerCost != nil self.providerSpecificData = providerSpecificData.sorted() } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.updatedAt = try container.decode(Date.self, forKey: .updatedAt) self.dataConfidence = try container.decodeIfPresent(String.self, forKey: .dataConfidence) ?? UsageDataConfidence.unknown.rawValue self.windows = try container.decode([ProviderDiagnosticRateWindow].self, forKey: .windows) self.extraWindowCount = try container.decode(Int.self, forKey: .extraWindowCount) self.providerCostPresent = try container.decode(Bool.self, forKey: .providerCostPresent) self.providerSpecificData = try container.decode([String].self, forKey: .providerSpecificData) }} public struct ProviderDiagnosticRateWindow: Codable, Sendable { public let label: String public let usedPercent: Double public let windowMinutes: Int? public let resetsAt: Date? public let hasResetDescription: Bool public let nextRegenPercent: Double? public let usageKnown: Bool private enum CodingKeys: String, CodingKey { case label case usedPercent case windowMinutes case resetsAt case hasResetDescription case nextRegenPercent case usageKnown } public init(label: String, window: RateWindow, usageKnown: Bool = true) { self.label = label self.usedPercent = window.usedPercent self.windowMinutes = window.windowMinutes self.resetsAt = window.resetsAt self.hasResetDescription = window.resetDescription?.isEmpty == false self.nextRegenPercent = window.nextRegenPercent self.usageKnown = usageKnown } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.label = try container.decode(String.self, forKey: .label) self.usedPercent = try container.decode(Double.self, forKey: .usedPercent) self.windowMinutes = try container.decodeIfPresent(Int.self, forKey: .windowMinutes) self.resetsAt = try container.decodeIfPresent(Date.self, forKey: .resetsAt) self.hasResetDescription = try container.decode(Bool.self, forKey: .hasResetDescription) self.nextRegenPercent = try container.decodeIfPresent(Double.self, forKey: .nextRegenPercent) self.usageKnown = try container.decodeIfPresent(Bool.self, forKey: .usageKnown) ?? true } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.label, forKey: .label) try container.encode(self.usedPercent, forKey: .usedPercent) try container.encodeIfPresent(self.windowMinutes, forKey: .windowMinutes) try container.encodeIfPresent(self.resetsAt, forKey: .resetsAt) try container.encode(self.hasResetDescription, forKey: .hasResetDescription) try container.encodeIfPresent(self.nextRegenPercent, forKey: .nextRegenPercent) if !self.usageKnown { try container.encode(false, forKey: .usageKnown) } }} public struct ProviderDiagnosticFetchAttempt: Codable, Sendable { public let kind: String public let wasAvailable: Bool public let errorCategory: String? public init( kind: String, wasAvailable: Bool, errorCategory: String?) { self.kind = kind self.wasAvailable = wasAvailable self.errorCategory = errorCategory } public init(from attempt: ProviderFetchAttempt) { self.kind = Self.kindLabel(attempt.kind) self.wasAvailable = attempt.wasAvailable self.errorCategory = attempt.errorDescription.map(Self.errorCategoryLabel) } public static func kindLabel(_ kind: ProviderFetchKind) -> String { switch kind { case .cli: "cli" case .web: "web" case .oauth: "oauth" case .apiToken: "api" case .localProbe: "local" case .webDashboard: "web" } } public static func errorCategoryLabel(_ description: String?) -> String { guard let desc = description?.lowercased() else { return "unknown" } if desc.contains("endpoint override") { return "configuration" } if desc.contains("network") || desc.contains("timeout") || desc.contains("connection") { return "network" } if desc.contains("auth") || desc.contains("credential") || desc.contains("token") || desc.contains("cookie") || desc.contains("api key") || desc.contains("key not configured") || desc.contains("missing key") { return "auth" } if desc.contains("source") || desc.contains("not supported") || desc.contains("unavailable") { return "configuration" } if desc.contains("api") || desc.contains("http") || desc.contains("404") || desc.contains("403") { return "api" } if desc.contains("parse") || desc.contains("format") || desc.contains("decode") { return "parse" } return "unknown" }} public struct ProviderDiagnosticError: Codable, Sendable { public let category: String public let safeDescription: String public init(category: String, safeDescription: String) { self.category = category self.safeDescription = safeDescription } public init(from error: Error, authConfigured: Bool) { self.category = Self.errorCategory(error, authConfigured: authConfigured) self.safeDescription = Self.safeDescription(category: self.category) } private static func errorCategory(_ error: Error, authConfigured: Bool) -> String { if case ProviderFetchError.noAvailableStrategy = error { return authConfigured ? "configuration" : "auth" } if error is ProviderEndpointOverrideError { return "configuration" } if let minimaxError = error as? MiniMaxUsageError { switch minimaxError { case .networkError: return "network" case .invalidCredentials: return "auth" case .apiError: return "api" case .parseFailed: return "parse" } } if let alibabaError = error as? AlibabaCodingPlanUsageError { switch alibabaError { case .networkError: return "network" case .loginRequired, .invalidCredentials: return "auth" case .apiError, .apiKeyUnavailableInRegion: return "api" case .parseFailed: return "parse" } } if error is MiniMaxSettingsError || error is MiniMaxAPISettingsError { return "auth" } return ProviderDiagnosticFetchAttempt.errorCategoryLabel(error.localizedDescription) } private static func safeDescription(category: String) -> String { switch category { case "network": "Network error - check your connection" case "auth": "Authentication or setup issue - check provider credentials" case "api": "API error - service returned an unexpected response" case "parse": "Parse error - unexpected response format" case "configuration": "Configuration issue - check provider source and settings" default: "An unexpected error occurred" } }} public struct ProviderDiagnosticSettingsSummary: Codable, Sendable { public let sourceMode: String public let apiRegion: String? public init(sourceMode: ProviderSourceMode, apiRegion: String? = nil) { self.sourceMode = sourceMode.rawValue self.apiRegion = apiRegion }} public enum ProviderDiagnosticDetails: Codable, Sendable { case minimax(MiniMaxDiagnosticDetails) private enum CodingKeys: String, CodingKey { case type case minimax } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let type = try container.decode(String.self, forKey: .type) switch type { case "minimax": self = try .minimax(container.decode(MiniMaxDiagnosticDetails.self, forKey: .minimax)) default: throw DecodingError.dataCorruptedError( forKey: .type, in: container, debugDescription: "Unknown provider diagnostic detail type") } } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) switch self { case let .minimax(details): try container.encode("minimax", forKey: .type) try container.encode(details, forKey: .minimax) } }} public struct MiniMaxDiagnosticDetails: Codable, Sendable { public let planName: String? public let availablePrompts: Int? public let currentPrompts: Int? public let remainingPrompts: Int? public let windowMinutes: Int? public let usedPercent: Double? public let resetsAt: Date? public let services: [MiniMaxDiagnosticServiceUsage]? public let billingSummaryPresent: Bool public init(from snapshot: MiniMaxUsageSnapshot) { self.planName = snapshot.planName self.availablePrompts = snapshot.availablePrompts self.currentPrompts = snapshot.currentPrompts self.remainingPrompts = snapshot.remainingPrompts self.windowMinutes = snapshot.windowMinutes self.usedPercent = snapshot.usedPercent self.resetsAt = snapshot.resetsAt self.services = snapshot.services?.map { MiniMaxDiagnosticServiceUsage(from: $0) } self.billingSummaryPresent = snapshot.billingSummary != nil }} public struct MiniMaxDiagnosticServiceUsage: Codable, Sendable { public let displayName: String public let percent: Double public let windowType: String public let resetsAt: Date? public let hasResetDescription: Bool public init(from service: MiniMaxServiceUsage) { self.displayName = service.displayName self.percent = service.percent self.windowType = service.windowType self.resetsAt = service.resetsAt self.hasResetDescription = !service.resetDescription.isEmpty }} public enum ProviderDiagnosticExportBuilder { public struct Input: Sendable { public let provider: UsageProvider public let descriptor: ProviderDescriptor public let outcome: ProviderFetchOutcome public let sourceMode: ProviderSourceMode public let settings: ProviderSettingsSnapshot? public let auth: ProviderDiagnosticAuthSummary public init( provider: UsageProvider, descriptor: ProviderDescriptor, outcome: ProviderFetchOutcome, sourceMode: ProviderSourceMode, settings: ProviderSettingsSnapshot?, auth: ProviderDiagnosticAuthSummary) { self.provider = provider self.descriptor = descriptor self.outcome = outcome self.sourceMode = sourceMode self.settings = settings self.auth = auth } } public static func build(_ input: Input) -> ProviderDiagnosticExport { let resolvedAuth = input.auth.resolved(with: input.outcome) let usage = input.outcome.usageSnapshot.map { ProviderDiagnosticUsageSummary(from: $0) } let error = input.outcome.failureError .map { ProviderDiagnosticError(from: $0, authConfigured: resolvedAuth.configured) } let settingsSummary = ProviderDiagnosticSettingsSummary( sourceMode: input.sourceMode, apiRegion: Self.safeAPIRegion(provider: input.provider, settings: input.settings)) return ProviderDiagnosticExport( timestamp: Date(), provider: input.provider.rawValue, displayName: input.descriptor.metadata.displayName, source: input.outcome.sourceLabel, sourceMode: input.sourceMode.rawValue, auth: resolvedAuth, usage: usage, fetchAttempts: input.outcome.attempts.map { ProviderDiagnosticFetchAttempt(from: $0) }, error: error, settings: settingsSummary, details: Self.details(provider: input.provider, outcome: input.outcome)) } private static func safeAPIRegion(provider: UsageProvider, settings: ProviderSettingsSnapshot?) -> String? { guard provider == .minimax else { return nil } return settings?.minimax?.apiRegion.rawValue ?? "global" } private static func details(provider: UsageProvider, outcome: ProviderFetchOutcome) -> ProviderDiagnosticDetails? { guard provider == .minimax, let usage = outcome.usageSnapshot?.minimaxUsage else { return nil } return .minimax(MiniMaxDiagnosticDetails(from: usage)) }} extension ProviderFetchOutcome { fileprivate var isSuccess: Bool { guard case .success = self.result else { return false } return true } fileprivate var sourceLabel: String { guard case let .success(result) = result else { return "failed" } return result.sourceLabel } fileprivate var usageSnapshot: UsageSnapshot? { guard case let .success(result) = result else { return nil } return result.usage } fileprivate var failureError: Error? { guard case let .failure(error) = result else { return nil } return error }}
import Foundation
public struct ProviderDiagnosticBatchExport: Codable, Sendable {
public let schemaVersion: String
public let timestamp: Date
public let diagnostics: [ProviderDiagnosticExport]
public init(
schemaVersion: String = "1.0",
timestamp: Date,
diagnostics: [ProviderDiagnosticExport])
{
self.schemaVersion = schemaVersion
self.timestamp = timestamp
self.diagnostics = diagnostics
}
}
public struct ProviderDiagnosticExport: Codable, Sendable {
public let schemaVersion: String
public let timestamp: Date
public let provider: String
public let displayName: String
public let source: String
public let sourceMode: String
public let auth: ProviderDiagnosticAuthSummary
public let usage: ProviderDiagnosticUsageSummary?
public let fetchAttempts: [ProviderDiagnosticFetchAttempt]
public let error: ProviderDiagnosticError?
public let settings: ProviderDiagnosticSettingsSummary
public let details: ProviderDiagnosticDetails?
public init(
schemaVersion: String = "1.0",
timestamp: Date,
provider: String,
displayName: String,
source: String,
sourceMode: String,
auth: ProviderDiagnosticAuthSummary,
usage: ProviderDiagnosticUsageSummary?,
fetchAttempts: [ProviderDiagnosticFetchAttempt],
error: ProviderDiagnosticError?,
settings: ProviderDiagnosticSettingsSummary,
details: ProviderDiagnosticDetails?)
{
self.schemaVersion = schemaVersion
self.timestamp = timestamp
self.provider = provider
self.displayName = displayName
self.source = source
self.sourceMode = sourceMode
self.auth = auth
self.usage = usage
self.fetchAttempts = fetchAttempts
self.error = error
self.settings = settings
self.details = details
}
}
public struct ProviderDiagnosticAuthSummary: Codable, Sendable {
public let configured: Bool
public let modes: [String]
public init(configured: Bool, modes: [String]) {
self.configured = configured
self.modes = modes
}
public func resolved(with outcome: ProviderFetchOutcome) -> ProviderDiagnosticAuthSummary {
var resolvedModes = self.modes
if outcome.isSuccess {
for attempt in outcome.attempts where attempt.wasAvailable {
let mode = ProviderDiagnosticFetchAttempt.kindLabel(attempt.kind)
if !resolvedModes.contains(mode) {
resolvedModes.append(mode)
}
}
}
let configured = self.configured || outcome.isSuccess
return ProviderDiagnosticAuthSummary(configured: configured, modes: resolvedModes)
}
}
public struct ProviderDiagnosticUsageSummary: Codable, Sendable {
public let updatedAt: Date
public let dataConfidence: String
public let windows: [ProviderDiagnosticRateWindow]
public let extraWindowCount: Int
public let providerCostPresent: Bool
public let providerSpecificData: [String]
private enum CodingKeys: String, CodingKey {
case updatedAt
case dataConfidence
case windows
case extraWindowCount
case providerCostPresent
case providerSpecificData
}
public init(from snapshot: UsageSnapshot) {
var windows: [ProviderDiagnosticRateWindow] = []
if let primary = snapshot.primary {
windows.append(ProviderDiagnosticRateWindow(label: "primary", window: primary))
}
if let secondary = snapshot.secondary {
windows.append(ProviderDiagnosticRateWindow(label: "secondary", window: secondary))
}
if let tertiary = snapshot.tertiary {
windows.append(ProviderDiagnosticRateWindow(label: "tertiary", window: tertiary))
}
for extra in snapshot.extraRateWindows ?? [] {
windows.append(ProviderDiagnosticRateWindow(
label: extra.title,
window: extra.window,
usageKnown: extra.usageKnown))
}
var providerSpecificData: [String] = []
if snapshot.kiroUsage != nil { providerSpecificData.append("kiroUsage") }
if snapshot.ampUsage != nil { providerSpecificData.append("ampUsage") }
if snapshot.zaiUsage != nil { providerSpecificData.append("zaiUsage") }
if snapshot.minimaxUsage != nil { providerSpecificData.append("minimaxUsage") }
if snapshot.deepseekUsage != nil { providerSpecificData.append("deepseekUsage") }
if snapshot.openRouterUsage != nil { providerSpecificData.append("openRouterUsage") }
if snapshot.openAIAPIUsage != nil { providerSpecificData.append("openAIAPIUsage") }
if snapshot.claudeAdminAPIUsage != nil { providerSpecificData.append("claudeAdminAPIUsage") }
if snapshot.mistralUsage != nil { providerSpecificData.append("mistralUsage") }
if snapshot.deepgramUsage != nil { providerSpecificData.append("deepgramUsage") }
if snapshot.cursorRequests != nil { providerSpecificData.append("cursorRequests") }
self.updatedAt = snapshot.updatedAt
self.dataConfidence = snapshot.dataConfidence.rawValue
self.windows = windows
self.extraWindowCount = snapshot.extraRateWindows?.count ?? 0
self.providerCostPresent = snapshot.providerCost != nil
self.providerSpecificData = providerSpecificData.sorted()
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.updatedAt = try container.decode(Date.self, forKey: .updatedAt)
self.dataConfidence = try container.decodeIfPresent(String.self, forKey: .dataConfidence)
?? UsageDataConfidence.unknown.rawValue
self.windows = try container.decode([ProviderDiagnosticRateWindow].self, forKey: .windows)
self.extraWindowCount = try container.decode(Int.self, forKey: .extraWindowCount)
self.providerCostPresent = try container.decode(Bool.self, forKey: .providerCostPresent)
self.providerSpecificData = try container.decode([String].self, forKey: .providerSpecificData)
}
}
public struct ProviderDiagnosticRateWindow: Codable, Sendable {
public let label: String
public let usedPercent: Double
public let windowMinutes: Int?
public let resetsAt: Date?
public let hasResetDescription: Bool
public let nextRegenPercent: Double?
public let usageKnown: Bool
private enum CodingKeys: String, CodingKey {
case label
case usedPercent
case windowMinutes
case resetsAt
case hasResetDescription
case nextRegenPercent
case usageKnown
}
public init(label: String, window: RateWindow, usageKnown: Bool = true) {
self.label = label
self.usedPercent = window.usedPercent
self.windowMinutes = window.windowMinutes
self.resetsAt = window.resetsAt
self.hasResetDescription = window.resetDescription?.isEmpty == false
self.nextRegenPercent = window.nextRegenPercent
self.usageKnown = usageKnown
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.label = try container.decode(String.self, forKey: .label)
self.usedPercent = try container.decode(Double.self, forKey: .usedPercent)
self.windowMinutes = try container.decodeIfPresent(Int.self, forKey: .windowMinutes)
self.resetsAt = try container.decodeIfPresent(Date.self, forKey: .resetsAt)
self.hasResetDescription = try container.decode(Bool.self, forKey: .hasResetDescription)
self.nextRegenPercent = try container.decodeIfPresent(Double.self, forKey: .nextRegenPercent)
self.usageKnown = try container.decodeIfPresent(Bool.self, forKey: .usageKnown) ?? true
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.label, forKey: .label)
try container.encode(self.usedPercent, forKey: .usedPercent)
try container.encodeIfPresent(self.windowMinutes, forKey: .windowMinutes)
try container.encodeIfPresent(self.resetsAt, forKey: .resetsAt)
try container.encode(self.hasResetDescription, forKey: .hasResetDescription)
try container.encodeIfPresent(self.nextRegenPercent, forKey: .nextRegenPercent)
if !self.usageKnown {
try container.encode(false, forKey: .usageKnown)
}
}
}
public struct ProviderDiagnosticFetchAttempt: Codable, Sendable {
public let kind: String
public let wasAvailable: Bool
public let errorCategory: String?
public init(
kind: String,
wasAvailable: Bool,
errorCategory: String?)
{
self.kind = kind
self.wasAvailable = wasAvailable
self.errorCategory = errorCategory
}
public init(from attempt: ProviderFetchAttempt) {
self.kind = Self.kindLabel(attempt.kind)
self.wasAvailable = attempt.wasAvailable
self.errorCategory = attempt.errorDescription.map(Self.errorCategoryLabel)
}
public static func kindLabel(_ kind: ProviderFetchKind) -> String {
switch kind {
case .cli: "cli"
case .web: "web"
case .oauth: "oauth"
case .apiToken: "api"
case .localProbe: "local"
case .webDashboard: "web"
}
}
public static func errorCategoryLabel(_ description: String?) -> String {
guard let desc = description?.lowercased() else { return "unknown" }
if desc.contains("endpoint override") {
return "configuration"
}
if desc.contains("network") || desc.contains("timeout") || desc.contains("connection") {
return "network"
}
if desc.contains("auth") || desc.contains("credential") || desc.contains("token") || desc.contains("cookie") ||
desc.contains("api key") || desc.contains("key not configured") || desc.contains("missing key")
{
return "auth"
}
if desc.contains("source") || desc.contains("not supported") || desc.contains("unavailable") {
return "configuration"
}
if desc.contains("api") || desc.contains("http") || desc.contains("404") || desc.contains("403") {
return "api"
}
if desc.contains("parse") || desc.contains("format") || desc.contains("decode") {
return "parse"
}
return "unknown"
}
}
public struct ProviderDiagnosticError: Codable, Sendable {
public let category: String
public let safeDescription: String
public init(category: String, safeDescription: String) {
self.category = category
self.safeDescription = safeDescription
}
public init(from error: Error, authConfigured: Bool) {
self.category = Self.errorCategory(error, authConfigured: authConfigured)
self.safeDescription = Self.safeDescription(category: self.category)
}
private static func errorCategory(_ error: Error, authConfigured: Bool) -> String {
if case ProviderFetchError.noAvailableStrategy = error {
return authConfigured ? "configuration" : "auth"
}
if error is ProviderEndpointOverrideError {
return "configuration"
}
if let minimaxError = error as? MiniMaxUsageError {
switch minimaxError {
case .networkError: return "network"
case .invalidCredentials: return "auth"
case .apiError: return "api"
case .parseFailed: return "parse"
}
}
if let alibabaError = error as? AlibabaCodingPlanUsageError {
switch alibabaError {
case .networkError: return "network"
case .loginRequired, .invalidCredentials: return "auth"
case .apiError, .apiKeyUnavailableInRegion: return "api"
case .parseFailed: return "parse"
}
}
if error is MiniMaxSettingsError || error is MiniMaxAPISettingsError { return "auth" }
return ProviderDiagnosticFetchAttempt.errorCategoryLabel(error.localizedDescription)
}
private static func safeDescription(category: String) -> String {
switch category {
case "network":
"Network error - check your connection"
case "auth":
"Authentication or setup issue - check provider credentials"
case "api":
"API error - service returned an unexpected response"
case "parse":
"Parse error - unexpected response format"
case "configuration":
"Configuration issue - check provider source and settings"
default:
"An unexpected error occurred"
}
}
}
public struct ProviderDiagnosticSettingsSummary: Codable, Sendable {
public let sourceMode: String
public let apiRegion: String?
public init(sourceMode: ProviderSourceMode, apiRegion: String? = nil) {
self.sourceMode = sourceMode.rawValue
self.apiRegion = apiRegion
}
}
public enum ProviderDiagnosticDetails: Codable, Sendable {
case minimax(MiniMaxDiagnosticDetails)
private enum CodingKeys: String, CodingKey {
case type
case minimax
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(String.self, forKey: .type)
switch type {
case "minimax":
self = try .minimax(container.decode(MiniMaxDiagnosticDetails.self, forKey: .minimax))
default:
throw DecodingError.dataCorruptedError(
forKey: .type,
in: container,
debugDescription: "Unknown provider diagnostic detail type")
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .minimax(details):
try container.encode("minimax", forKey: .type)
try container.encode(details, forKey: .minimax)
}
}
}
public struct MiniMaxDiagnosticDetails: Codable, Sendable {
public let planName: String?
public let availablePrompts: Int?
public let currentPrompts: Int?
public let remainingPrompts: Int?
public let windowMinutes: Int?
public let usedPercent: Double?
public let resetsAt: Date?
public let services: [MiniMaxDiagnosticServiceUsage]?
public let billingSummaryPresent: Bool
public init(from snapshot: MiniMaxUsageSnapshot) {
self.planName = snapshot.planName
self.availablePrompts = snapshot.availablePrompts
self.currentPrompts = snapshot.currentPrompts
self.remainingPrompts = snapshot.remainingPrompts
self.windowMinutes = snapshot.windowMinutes
self.usedPercent = snapshot.usedPercent
self.resetsAt = snapshot.resetsAt
self.services = snapshot.services?.map { MiniMaxDiagnosticServiceUsage(from: $0) }
self.billingSummaryPresent = snapshot.billingSummary != nil
}
}
public struct MiniMaxDiagnosticServiceUsage: Codable, Sendable {
public let displayName: String
public let percent: Double
public let windowType: String
public let resetsAt: Date?
public let hasResetDescription: Bool
public init(from service: MiniMaxServiceUsage) {
self.displayName = service.displayName
self.percent = service.percent
self.windowType = service.windowType
self.resetsAt = service.resetsAt
self.hasResetDescription = !service.resetDescription.isEmpty
}
}
public enum ProviderDiagnosticExportBuilder {
public struct Input: Sendable {
public let provider: UsageProvider
public let descriptor: ProviderDescriptor
public let outcome: ProviderFetchOutcome
public let sourceMode: ProviderSourceMode
public let settings: ProviderSettingsSnapshot?
public let auth: ProviderDiagnosticAuthSummary
public init(
provider: UsageProvider,
descriptor: ProviderDescriptor,
outcome: ProviderFetchOutcome,
sourceMode: ProviderSourceMode,
settings: ProviderSettingsSnapshot?,
auth: ProviderDiagnosticAuthSummary)
{
self.provider = provider
self.descriptor = descriptor
self.outcome = outcome
self.sourceMode = sourceMode
self.settings = settings
self.auth = auth
}
}
public static func build(_ input: Input) -> ProviderDiagnosticExport {
let resolvedAuth = input.auth.resolved(with: input.outcome)
let usage = input.outcome.usageSnapshot.map { ProviderDiagnosticUsageSummary(from: $0) }
let error = input.outcome.failureError
.map { ProviderDiagnosticError(from: $0, authConfigured: resolvedAuth.configured) }
let settingsSummary = ProviderDiagnosticSettingsSummary(
sourceMode: input.sourceMode,
apiRegion: Self.safeAPIRegion(provider: input.provider, settings: input.settings))
return ProviderDiagnosticExport(
timestamp: Date(),
provider: input.provider.rawValue,
displayName: input.descriptor.metadata.displayName,
source: input.outcome.sourceLabel,
sourceMode: input.sourceMode.rawValue,
auth: resolvedAuth,
usage: usage,
fetchAttempts: input.outcome.attempts.map { ProviderDiagnosticFetchAttempt(from: $0) },
error: error,
settings: settingsSummary,
details: Self.details(provider: input.provider, outcome: input.outcome))
}
private static func safeAPIRegion(provider: UsageProvider, settings: ProviderSettingsSnapshot?) -> String? {
guard provider == .minimax else { return nil }
return settings?.minimax?.apiRegion.rawValue ?? "global"
}
private static func details(provider: UsageProvider, outcome: ProviderFetchOutcome) -> ProviderDiagnosticDetails? {
guard provider == .minimax,
let usage = outcome.usageSnapshot?.minimaxUsage
else {
return nil
}
return .minimax(MiniMaxDiagnosticDetails(from: usage))
}
}
extension ProviderFetchOutcome {
fileprivate var isSuccess: Bool {
guard case .success = self.result else { return false }
return true
}
fileprivate var sourceLabel: String {
guard case let .success(result) = result else { return "failed" }
return result.sourceLabel
}
fileprivate var usageSnapshot: UsageSnapshot? {
guard case let .success(result) = result else { return nil }
return result.usage
}
fileprivate var failureError: Error? {
guard case let .failure(error) = result else { return nil }
return error
}
}