@@ -14,11 +14,13 @@ final class GeminiProvider: AIProvider {
1414
1515 private let endpoint : String
1616 private let apiKey : String
17+ private let maxOutputTokens : Int
1718 private let session : URLSession
1819
19- init ( endpoint: String , apiKey: String ) {
20+ init ( endpoint: String , apiKey: String , maxOutputTokens : Int = 8_192 ) {
2021 self . endpoint = endpoint. hasSuffix ( " / " ) ? String ( endpoint. dropLast ( ) ) : endpoint
2122 self . apiKey = apiKey. trimmingCharacters ( in: . whitespacesAndNewlines)
23+ self . maxOutputTokens = maxOutputTokens
2224 self . session = URLSession ( configuration: . ephemeral)
2325 }
2426
@@ -106,6 +108,14 @@ final class GeminiProvider: AIProvider {
106108 }
107109 }
108110
111+ private static let knownModels = [
112+ " gemini-2.5-flash " ,
113+ " gemini-2.5-pro " ,
114+ " gemini-2.0-flash " ,
115+ " gemini-1.5-flash " ,
116+ " gemini-1.5-pro "
117+ ]
118+
109119 func fetchAvailableModels( ) async throws -> [ String ] {
110120 guard let url = URL ( string: " \( endpoint) /v1beta/models " ) else {
111121 throw AIProviderError . invalidEndpoint ( endpoint)
@@ -115,36 +125,41 @@ final class GeminiProvider: AIProvider {
115125 request. httpMethod = " GET "
116126 request. setValue ( apiKey, forHTTPHeaderField: " x-goog-api-key " )
117127
118- let ( data, response) = try await session. data ( for: request)
128+ let data : Data
129+ let response : URLResponse
130+ do {
131+ ( data, response) = try await session. data ( for: request)
132+ } catch {
133+ return Self . knownModels
134+ }
119135
120136 guard let httpResponse = response as? HTTPURLResponse else {
121- throw AIProviderError . networkError ( " Invalid response " )
137+ return Self . knownModels
122138 }
123139
124140 guard httpResponse. statusCode == 200 else {
125- throw mapHTTPError (
126- statusCode: httpResponse. statusCode,
127- body: String ( data: data, encoding: . utf8) ?? " "
128- )
141+ return Self . knownModels
129142 }
130143
131144 guard let json = try ? JSONSerialization . jsonObject ( with: data) as? [ String : Any ] ,
132145 let models = json [ " models " ] as? [ [ String : Any ] ]
133146 else {
134- return [ ]
147+ return Self . knownModels
135148 }
136149
137- return models. compactMap { model -> String ? in
150+ let fetched = models. compactMap { model -> String ? in
138151 guard let name = model [ " name " ] as? String ,
139152 let methods = model [ " supportedGenerationMethods " ] as? [ String ] ,
140153 methods. contains ( " generateContent " )
141154 else { return nil }
142- // Strip "models/" prefix: "models/gemini-2.0-flash" → "gemini-2.0-flash"
155+ // Strip "models/" prefix: "models/gemini-2.0-flash" -> "gemini-2.0-flash"
143156 if name. hasPrefix ( " models/ " ) {
144157 return String ( name. dropFirst ( 7 ) )
145158 }
146159 return name
147160 }
161+
162+ return fetched. isEmpty ? Self . knownModels : fetched
148163 }
149164
150165 func testConnection( ) async throws -> Bool {
@@ -196,7 +211,7 @@ final class GeminiProvider: AIProvider {
196211 request. setValue ( apiKey, forHTTPHeaderField: " x-goog-api-key " )
197212
198213 var body : [ String : Any ] = [
199- " generationConfig " : [ " maxOutputTokens " : 8_192 ]
214+ " generationConfig " : [ " maxOutputTokens " : maxOutputTokens ]
200215 ]
201216
202217 if let systemPrompt, !systemPrompt. isEmpty {
0 commit comments