Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 5.8
// swift-tools-version: 5.10
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,9 @@ do {
print(completion.choices.first?.message.content ?? "No response")
} catch let error as LLMChatOpenAIError {
switch error {
case .serverError(let message):
case .serverError(let statusCode, let message):
// Handle server-side errors (e.g., invalid API key, rate limits)
print("Server Error: \(message)")
print("Server Error [\(statusCode)]: \(message)")
case .networkError(let error):
// Handle network-related errors (e.g., no internet connection)
print("Network Error: \(error.localizedDescription)")
Expand Down
4 changes: 2 additions & 2 deletions Sources/LLMChatOpenAI/Documentation.docc/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,9 @@ do {
print(completion.choices.first?.message.content ?? "No response")
} catch let error as LLMChatOpenAIError {
switch error {
case .serverError(let message):
case .serverError(let statusCode, let message):
// Handle server-side errors (e.g., invalid API key, rate limits)
print("Server Error: \(message)")
print("Server Error [\(statusCode)]: \(message)")
case .networkError(let error):
// Handle network-related errors (e.g., no internet connection)
print("Network Error: \(error.localizedDescription)")
Expand Down
14 changes: 9 additions & 5 deletions Sources/LLMChatOpenAI/LLMChatOpenAI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,16 @@ private extension LLMChatOpenAI {
let (data, response) = try await URLSession.shared.data(for: request)

guard let httpResponse = response as? HTTPURLResponse else {
throw LLMChatOpenAIError.serverError(response.description)
throw LLMChatOpenAIError.serverError(statusCode: 0, message: response.description)
}

// Check for API errors first, as they might come with 200 status
if let errorResponse = try? JSONDecoder().decode(ChatCompletionError.self, from: data) {
throw LLMChatOpenAIError.serverError(errorResponse.error.message)
throw LLMChatOpenAIError.serverError(statusCode: httpResponse.statusCode, message: errorResponse.error.message)
}

guard 200...299 ~= httpResponse.statusCode else {
throw LLMChatOpenAIError.serverError(response.description)
throw LLMChatOpenAIError.serverError(statusCode: httpResponse.statusCode, message: response.description)
}

return try JSONDecoder().decode(ChatCompletion.self, from: data)
Expand All @@ -174,8 +174,12 @@ private extension LLMChatOpenAI {
let request = try createRequest(for: endpoint, with: body)
let (bytes, response) = try await URLSession.shared.bytes(for: request)

guard let httpResponse = response as? HTTPURLResponse, 200...299 ~= httpResponse.statusCode else {
throw LLMChatOpenAIError.serverError(response.description)
guard let httpResponse = response as? HTTPURLResponse else {
throw LLMChatOpenAIError.serverError(statusCode: 0, message: response.description)
}

guard 200...299 ~= httpResponse.statusCode else {
throw LLMChatOpenAIError.serverError(statusCode: httpResponse.statusCode, message: response.description)
}

for try await line in bytes.lines {
Expand Down
6 changes: 4 additions & 2 deletions Sources/LLMChatOpenAI/LLMChatOpenAIError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ public enum LLMChatOpenAIError: Error, Sendable {

/// An error returned by the server.
///
/// - Parameter message: The error message received from the server.
case serverError(String)
/// - Parameters:
/// - statusCode: The HTTP status code returned by the server.
/// - message: The error message received from the server.
case serverError(statusCode: Int, message: String)

/// An error that occurs during stream processing.
case streamError
Expand Down
14 changes: 9 additions & 5 deletions Tests/LLMChatOpenAITests/ChatCompletionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,16 @@ extension ChatCompletionTests {
"""

URLProtocolMock.mockData = mockErrorResponse.data(using: .utf8)
URLProtocolMock.mockStatusCode = 401

do {
_ = try await chat.send(model: "gpt-4o", messages: messages)

XCTFail("Expected serverError to be thrown")
} catch let error as LLMChatOpenAIError {
switch error {
case .serverError(let message):
case .serverError(let statusCode, let message):
XCTAssertEqual(statusCode, 401)
XCTAssertEqual(message, "Invalid API key provided")
default:
XCTFail("Expected serverError but got \(error)")
Expand Down Expand Up @@ -211,16 +213,17 @@ extension ChatCompletionTests {
}

func testHTTPError() async throws {
URLProtocolMock.mockStatusCode = 429
URLProtocolMock.mockData = "Rate limit exceeded".data(using: .utf8)
URLProtocolMock.mockStatusCode = 429

do {
_ = try await chat.send(model: "gpt-4o", messages: messages)

XCTFail("Expected serverError to be thrown")
} catch let error as LLMChatOpenAIError {
switch error {
case .serverError(let message):
case .serverError(let statusCode, let message):
XCTAssertEqual(statusCode, 429)
XCTAssertTrue(message.contains("429"))
default:
XCTFail("Expected serverError but got \(error)")
Expand Down Expand Up @@ -311,16 +314,17 @@ extension ChatCompletionTests {
}

func testStreamHTTPError() async throws {
URLProtocolMock.mockStatusCode = 503
URLProtocolMock.mockStreamData = [""]
URLProtocolMock.mockStatusCode = 503

do {
for try await _ in chat.stream(model: "gpt-4o", messages: messages) {
XCTFail("Expected serverError to be thrown")
}
} catch let error as LLMChatOpenAIError {
switch error {
case .serverError(let message):
case .serverError(let statusCode, let message):
XCTAssertEqual(statusCode, 503)
XCTAssertTrue(message.contains("503"))
default:
XCTFail("Expected serverError but got \(error)")
Expand Down