Skip to content
Snippets Groups Projects
Commit 95bb9742 authored by nezhyborets's avatar nezhyborets
Browse files

Use new options in more places

To fix parsing Gemini responses
parent 035fdc41
No related branches found
No related tags found
No related merge requests found
......@@ -175,25 +175,25 @@ public final class ChatStore: ObservableObject {
let chatQuery = ChatQuery(
messages: conversation.messages.map { message in
ChatQuery.ChatCompletionMessageParam(role: message.role, content: message.content)!
}, model: model,
},
model: model,
tools: functions
)
if stream {
try await completeConversationStreaming(
conversationIndex: conversationIndex,
model: model,
query: chatQuery
)
} else {
try await completeConversation(conversationIndex: conversationIndex, model: model, query: chatQuery)
try await completeConversation(conversationIndex: conversationIndex, query: chatQuery)
}
} catch {
conversationErrors[conversationId] = error
}
}
private func completeConversation(conversationIndex: Int, model: Model, query: ChatQuery) async throws {
private func completeConversation(conversationIndex: Int, query: ChatQuery) async throws {
let chatResult: ChatResult = try await openAIClient.chats(query: query)
chatResult.choices
.map {
......@@ -212,7 +212,7 @@ public final class ChatStore: ObservableObject {
}
}
private func completeConversationStreaming(conversationIndex: Int, model: Model, query: ChatQuery) async throws {
private func completeConversationStreaming(conversationIndex: Int, query: ChatQuery) async throws {
let chatsStream: AsyncThrowingStream<ChatStreamResult, Error> = openAIClient.chatsStream(query: query)
var functionCalls = [Int: (name: String?, arguments: String)]()
......
......@@ -221,7 +221,11 @@ extension OpenAI: OpenAIAsync {
do {
return try decoder.decode(ResultType.self, from: interceptedData ?? data)
} catch {
throw (try? decoder.decode(APIErrorResponse.self, from: interceptedData ?? data)) ?? error
if let decoded = JSONResponseErrorDecoder(decoder: decoder).decodeErrorResponse(data: interceptedData ?? data) {
throw decoded
} else {
throw error
}
}
} else {
let dataTaskStore = URLSessionDataTaskStore()
......
......@@ -406,7 +406,11 @@ extension OpenAI {
do {
completion(.success(try decoder.decode(ResultType.self, from: data)))
} catch {
completion(.failure((try? decoder.decode(APIErrorResponse.self, from: data)) ?? error))
if let decoded = JSONResponseErrorDecoder(decoder: decoder).decodeErrorResponse(data: data) {
completion(.failure(decoded))
} else {
completion(.failure(error))
}
}
}
}
......
//
// JSONResponseErrorDecoder.swift
// OpenAI
//
// Created by Oleksii Nezhyborets on 31.03.2025.
//
import Foundation
struct JSONResponseErrorDecoder {
let decoder: JSONDecoder
func decodeErrorResponse(data: Data) -> (any ErrorResponse)? {
if let decoded = try? decoder.decode(APIErrorResponse.self, from: data) {
return decoded
} else if let decoded = try? decoder.decode([GeminiAPIErrorResponse].self, from: data) {
return decoded[0]
} else {
return nil
}
}
}
......@@ -28,7 +28,7 @@ final class AudioSpeechStreamInterpreter: @unchecked Sendable, StreamInterpreter
func processData(_ data: Data) {
executionSerializer.dispatch {
let decoder = JSONDecoder()
if let decoded = try? decoder.decode(APIErrorResponse.self, from: data) {
if let decoded = JSONResponseErrorDecoder(decoder: decoder).decodeErrorResponse(data: data) {
self.onError?(decoded)
}
......
......@@ -37,7 +37,7 @@ final class ServerSentEventsStreamInterpreter <ResultType: Codable & Sendable>:
func processData(_ data: Data) {
let decoder = JSONDecoder()
if let decoded = try? decoder.decode(APIErrorResponse.self, from: data) {
if let decoded = JSONResponseErrorDecoder(decoder: decoder).decodeErrorResponse(data: data) {
onError?(decoded)
return
}
......@@ -97,7 +97,7 @@ final class ServerSentEventsStreamInterpreter <ResultType: Codable & Sendable>:
let object = try decoder.decode(ResultType.self, from: jsonData)
onEventDispatched?(object)
} catch {
if let decoded = try? decoder.decode(APIErrorResponse.self, from: jsonData) {
if let decoded = JSONResponseErrorDecoder(decoder: decoder).decodeErrorResponse(data: jsonData) {
onError?(decoded)
return
} else if index == jsonObjects.count - 1 {
......
......@@ -63,13 +63,17 @@ extension APIError: LocalizedError {
}
}
public struct APIErrorResponse: Error, Decodable, Equatable {
public struct APIErrorResponse: ErrorResponse {
public let error: APIError
}
extension APIErrorResponse: LocalizedError {
public var errorDescription: String? {
return error.errorDescription
error.errorDescription
}
}
public protocol ErrorResponse: Error, Decodable, Equatable, LocalizedError {
associatedtype Err: Error, Decodable, Equatable, LocalizedError
var error: Err { get }
var errorDescription: String? { get }
}
//
// GeminiAPIError.swift
// OpenAI
//
// Created by Oleksii Nezhyborets on 31.03.2025.
//
import Foundation
public struct GeminiAPIErrorResponse: ErrorResponse {
public let error: GeminiAPIError
public var errorDescription: String? {
error.errorDescription
}
}
public struct GeminiAPIError: Error, Decodable, Equatable, LocalizedError {
public let code: Int
public let message: String
public let status: String
public var errorDescription: String? {
message
}
}
......@@ -213,6 +213,20 @@ public struct ChatResult: Codable, Equatable, Sendable {
case usage
case citations
}
public init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let parsingOptions = decoder.userInfo[.parsingOptions] as? ParsingOptions ?? []
self.id = try container.decodeString(forKey: .id, parsingOptions: parsingOptions)
self.object = try container.decodeString(forKey: .object, parsingOptions: parsingOptions)
self.created = try container.decode(Int.self, forKey: .created)
self.model = try container.decodeString(forKey: .model, parsingOptions: parsingOptions)
self.choices = try container.decode([ChatResult.Choice].self, forKey: .choices)
self.serviceTier = try container.decodeIfPresent(String.self, forKey: .serviceTier)
self.systemFingerprint = try container.decodeString(forKey: .systemFingerprint, parsingOptions: parsingOptions)
self.usage = try container.decodeIfPresent(ChatResult.CompletionUsage.self, forKey: .usage)
self.citations = try container.decodeIfPresent([String].self, forKey: .citations)
}
}
extension ChatQuery.ChatCompletionMessageParam {
......
......@@ -154,9 +154,9 @@ public struct ChatStreamResult: Codable, Equatable, Sendable {
let parsingOptions = decoder.userInfo[.parsingOptions] as? ParsingOptions ?? []
self.id = try container.decodeString(forKey: .id, parsingOptions: parsingOptions)
self.object = try container.decode(String.self, forKey: .object)
self.object = try container.decodeString(forKey: .object, parsingOptions: parsingOptions)
self.created = try container.decode(TimeInterval.self, forKey: .created)
self.model = try container.decode(String.self, forKey: .model)
self.model = try container.decodeString(forKey: .model, parsingOptions: parsingOptions)
self.citations = try container.decodeIfPresent([String].self, forKey: .citations)
self.choices = try container.decode([ChatStreamResult.Choice].self, forKey: .choices)
self.systemFingerprint = try container.decodeString(forKey: .systemFingerprint, parsingOptions: parsingOptions)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment