import Foundation public enum VertexAIProviderDescriptor { public static let descriptor: ProviderDescriptor = Self.makeDescriptor() static func makeDescriptor() -> ProviderDescriptor { ProviderDescriptor( id: .vertexai, metadata: ProviderMetadata( id: .vertexai, displayName: "Vertex AI", sessionLabel: "Requests", weeklyLabel: "Tokens", opusLabel: nil, supportsOpus: false, supportsCredits: false, creditsHint: "", toggleTitle: "Show Vertex AI usage", cliName: "vertexai", defaultEnabled: false, isPrimaryProvider: false, usesAccountFallback: false, dashboardURL: "https://console.cloud.google.com/vertex-ai", statusPageURL: nil, statusLinkURL: "https://status.cloud.google.com"), branding: ProviderBranding( iconStyle: .vertexai, iconResourceName: "ProviderIcon-vertexai", color: ProviderColor(red: 66 / 255, green: 133 / 255, blue: 244 / 255)), tokenCost: ProviderTokenCostConfig( supportsTokenCost: true, noDataMessage: { "No Vertex AI cost data found in Claude logs. Ensure entries include Vertex metadata." }), fetchPlan: ProviderFetchPlan( sourceModes: [.auto, .oauth], pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [VertexAIOAuthFetchStrategy()] })), cli: ProviderCLIConfig( name: "vertexai", versionDetector: nil)) } } struct VertexAIOAuthFetchStrategy: ProviderFetchStrategy { let id: String = "vertexai.oauth" let kind: ProviderFetchKind = .oauth func isAvailable(_ context: ProviderFetchContext) async -> Bool { VertexAIOAuthCredentialsStore.hasCredentials(environment: context.env) } func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { var credentials = try await VertexAIOAuthCredentialsStore.loadForFetch(environment: context.env) // Refresh token if expired if credentials.needsRefresh { credentials = try await VertexAITokenRefresher.refresh(credentials) try VertexAIOAuthCredentialsStore.save(credentials) } // Fetch quota usage from Cloud Monitoring. If no data is found (e.g., no recent // Vertex AI requests), return an empty snapshot so token costs can still display. let usage: VertexAIUsageResponse? do { usage = try await VertexAIUsageFetcher.fetchUsage( accessToken: credentials.accessToken, projectId: credentials.projectId) } catch VertexAIFetchError.noData { // No quota data is fine - token costs from local logs can still be shown. usage = nil } return self.makeResult( usage: Self.mapUsage(usage, credentials: credentials), sourceLabel: "oauth") } func shouldFallback(on error: Error, context _: ProviderFetchContext) -> Bool { if error is VertexAIOAuthCredentialsError { return true } if let fetchError = error as? VertexAIFetchError { switch fetchError { case .unauthorized, .forbidden: return true default: return false } } return false } private static func mapUsage( _ response: VertexAIUsageResponse?, credentials: VertexAIOAuthCredentials) -> UsageSnapshot { // Token cost is fetched separately via CostUsageScanner from local Claude logs. // Quota usage from Cloud Monitoring is optional - we still show token costs if unavailable. let identity = ProviderIdentitySnapshot( providerID: .vertexai, accountEmail: credentials.email, accountOrganization: credentials.projectId, loginMethod: "gcloud") return UsageSnapshot( primary: nil, secondary: nil, providerCost: nil, updatedAt: Date(), identity: identity) } }