@@ -233,23 +233,34 @@ enum OpenAIError: LocalizedError {
233
233
var errorDescription : String ? {
234
234
switch self {
235
235
case . invalidURL:
236
- return " Invalid URL "
236
+ return " Could not connect to OpenAI service. Please check your internet connection. "
237
237
case . noServerResponse:
238
- return " No response from server "
239
- case . invalidResponseStatus( let code, let body) :
240
- return " Invalid response from server. Status code: \( code) , Response body: \( body) "
241
- case . parseError( let message) :
242
- return " Parse error: \( message) "
243
- case . invalidResponseStructure( let received) :
244
- return " Failed to parse response structure. Received: \( received) "
238
+ return " OpenAI service is not responding. Please try again later. "
239
+ case . invalidResponseStatus( let code, _) :
240
+ switch code {
241
+ case 401 :
242
+ return " OpenAI API key is invalid. Please check your API key in preferences. "
243
+ case 403 :
244
+ return " Access denied by OpenAI. Please check your API key permissions. "
245
+ case 429 :
246
+ return " OpenAI rate limit exceeded. Please wait a moment and try again. "
247
+ case 500 ... 599 :
248
+ return " OpenAI service is temporarily unavailable. Please try again later. "
249
+ default :
250
+ return " OpenAI request failed. Please try again later. "
251
+ }
252
+ case . parseError:
253
+ return " Could not understand OpenAI response. Please try again. "
254
+ case . invalidResponseStructure:
255
+ return " Received unexpected response from OpenAI. Please try again. "
245
256
}
246
257
}
247
258
}
248
259
249
260
// OpenAI APIクライアント
250
261
enum OpenAIClient {
251
262
// APIリクエストを送信する静的メソッド
252
- static func sendRequest( _ request: OpenAIRequest , apiKey: String , segmentsManager : SegmentsManager ) async throws -> [ String ] {
263
+ static func sendRequest( _ request: OpenAIRequest , apiKey: String , logger : ( ( String ) -> Void ) ? = nil ) async throws -> [ String ] {
253
264
guard let url = URL ( string: " https://api.openai.com/v1/chat/completions " ) else {
254
265
throw OpenAIError . invalidURL
255
266
}
@@ -276,18 +287,18 @@ enum OpenAIClient {
276
287
}
277
288
278
289
// レスポンスデータの解析
279
- return try parseResponseData ( data, segmentsManager : segmentsManager )
290
+ return try parseResponseData ( data, logger : logger )
280
291
}
281
292
282
293
// レスポンスデータのパースを行う静的メソッド
283
- private static func parseResponseData( _ data: Data , segmentsManager : SegmentsManager ) throws -> [ String ] {
284
- segmentsManager . appendDebugMessage ( " Received JSON response " )
294
+ private static func parseResponseData( _ data: Data , logger : ( ( String ) -> Void ) ? = nil ) throws -> [ String ] {
295
+ logger ? ( " Received JSON response " )
285
296
286
297
let jsonObject : Any
287
298
do {
288
299
jsonObject = try JSONSerialization . jsonObject ( with: data)
289
300
} catch {
290
- segmentsManager . appendDebugMessage ( " Failed to parse JSON response " )
301
+ logger ? ( " Failed to parse JSON response " )
291
302
throw OpenAIError . parseError ( " Failed to parse response " )
292
303
}
293
304
@@ -303,29 +314,78 @@ enum OpenAIClient {
303
314
continue
304
315
}
305
316
306
- segmentsManager . appendDebugMessage ( " Raw content string: \( contentString) " )
317
+ logger ? ( " Raw content string: \( contentString) " )
307
318
308
319
guard let contentData = contentString. data ( using: . utf8) else {
309
- segmentsManager . appendDebugMessage ( " Failed to convert `content` string to data " )
320
+ logger ? ( " Failed to convert `content` string to data " )
310
321
continue
311
322
}
312
323
313
324
do {
314
325
guard let parsedContent = try JSONSerialization . jsonObject ( with: contentData) as? [ String : [ String ] ] ,
315
326
let predictions = parsedContent [ " predictions " ] else {
316
- segmentsManager . appendDebugMessage ( " Failed to parse `content` as expected JSON dictionary: \( contentString) " )
327
+ logger ? ( " Failed to parse `content` as expected JSON dictionary: \( contentString) " )
317
328
continue
318
329
}
319
330
320
- segmentsManager . appendDebugMessage ( " Parsed predictions: \( predictions) " )
331
+ logger ? ( " Parsed predictions: \( predictions) " )
321
332
allPredictions. append ( contentsOf: predictions)
322
333
} catch {
323
- segmentsManager . appendDebugMessage ( " Error parsing JSON from `content`: \( error. localizedDescription) " )
334
+ logger ? ( " Error parsing JSON from `content`: \( error. localizedDescription) " )
324
335
}
325
336
}
326
337
327
338
return allPredictions
328
339
}
340
+
341
+ // Simple text transformation method for AI Transform feature
342
+ static func sendTextTransformRequest( prompt: String , modelName: String , apiKey: String ) async throws -> String {
343
+ guard let url = URL ( string: " https://api.openai.com/v1/chat/completions " ) else {
344
+ throw OpenAIError . invalidURL
345
+ }
346
+
347
+ var request = URLRequest ( url: url)
348
+ request. httpMethod = " POST "
349
+ request. setValue ( " Bearer \( apiKey) " , forHTTPHeaderField: " Authorization " )
350
+ request. setValue ( " application/json " , forHTTPHeaderField: " Content-Type " )
351
+
352
+ let body : [ String : Any ] = [
353
+ " model " : modelName,
354
+ " messages " : [
355
+ [ " role " : " system " , " content " : " You are a helpful assistant that transforms text according to user instructions. " ] ,
356
+ [ " role " : " user " , " content " : prompt]
357
+ ] ,
358
+ " max_tokens " : 150 ,
359
+ " temperature " : 0.7
360
+ ]
361
+
362
+ request. httpBody = try JSONSerialization . data ( withJSONObject: body)
363
+
364
+ // Send async request
365
+ let ( data, response) = try await URLSession . shared. data ( for: request)
366
+
367
+ // Validate response
368
+ guard let httpResponse = response as? HTTPURLResponse else {
369
+ throw OpenAIError . noServerResponse
370
+ }
371
+
372
+ guard httpResponse. statusCode == 200 else {
373
+ let responseBody = String ( bytes: data, encoding: . utf8) ?? " Body is not encoded in UTF-8 "
374
+ throw OpenAIError . invalidResponseStatus ( code: httpResponse. statusCode, body: responseBody)
375
+ }
376
+
377
+ // Parse response data
378
+ let jsonObject = try JSONSerialization . jsonObject ( with: data)
379
+ guard let jsonDict = jsonObject as? [ String : Any ] ,
380
+ let choices = jsonDict [ " choices " ] as? [ [ String : Any ] ] ,
381
+ let firstChoice = choices. first,
382
+ let message = firstChoice [ " message " ] as? [ String : Any ] ,
383
+ let content = message [ " content " ] as? String else {
384
+ throw OpenAIError . invalidResponseStructure ( jsonObject)
385
+ }
386
+
387
+ return content. trimmingCharacters ( in: . whitespacesAndNewlines)
388
+ }
329
389
}
330
390
331
391
private enum ErrorUnion : Error {
0 commit comments