From dbcdda9b6249a45ad1b128b902ca72ac784be075 Mon Sep 17 00:00:00 2001 From: Antonio War <59933379+antonio-war@users.noreply.github.com> Date: Sat, 27 Apr 2024 15:30:03 +0200 Subject: [PATCH 1/3] fix: SwiftyGPTChatResponse decoding --- .../xcshareddata/xcschemes/SwiftyGPT.xcscheme | 12 +++++++++++ .../Models/SwiftyGPTChatResponseChoice.swift | 13 ++++++++++++ .../Managers/SwiftyGPTChatManagerTests.swift | 2 +- .../SwiftyGPTChatMockServiceTests.swift | 2 +- .../SwiftyGPTChatNetworkingServiceTests.swift | 20 ++++++++++++++++++- 5 files changed, 46 insertions(+), 3 deletions(-) diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/SwiftyGPT.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/SwiftyGPT.xcscheme index 00140a4..7b93329 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/SwiftyGPT.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/SwiftyGPT.xcscheme @@ -62,6 +62,18 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> + + + + + + Date: Sat, 27 Apr 2024 15:50:06 +0200 Subject: [PATCH 2/3] feat: create SwiftyGPTChatResponseError --- .../xcode/xcshareddata/xcschemes/SwiftyGPT.xcscheme | 2 +- .../Models/SwiftyGPTChatResponseBody.swift | 1 + .../Models/SwiftyGPTChatResponseError.swift | 13 +++++++++++++ .../SwiftyGPTChatNetworkingServiceTests.swift | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseError.swift diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/SwiftyGPT.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/SwiftyGPT.xcscheme index 7b93329..cea53fd 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/SwiftyGPT.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/SwiftyGPT.xcscheme @@ -69,7 +69,7 @@ isEnabled = "YES"> diff --git a/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseBody.swift b/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseBody.swift index c3e72ec..edc26ba 100644 --- a/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseBody.swift +++ b/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseBody.swift @@ -30,4 +30,5 @@ public struct SwiftyGPTChatResponseSuccessBody: SwiftyGPTChatResponseBody, Ident } public struct SwiftyGPTChatResponseFailureBody: SwiftyGPTChatResponseBody, Decodable { + let error: SwiftyGPTChatResponseError } diff --git a/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseError.swift b/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseError.swift new file mode 100644 index 0000000..611ce8e --- /dev/null +++ b/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseError.swift @@ -0,0 +1,13 @@ +// +// SwiftyGPTChatResponseError.swift +// +// +// Created by Antonio Guerra on 27/04/24. +// + +import Foundation + +struct SwiftyGPTChatResponseError: Decodable, Equatable { + let type: String + let message: String +} diff --git a/Tests/SwiftyGPTChatTests/Services/SwiftyGPTChatNetworkingServiceTests.swift b/Tests/SwiftyGPTChatTests/Services/SwiftyGPTChatNetworkingServiceTests.swift index cb8473c..c5a621e 100644 --- a/Tests/SwiftyGPTChatTests/Services/SwiftyGPTChatNetworkingServiceTests.swift +++ b/Tests/SwiftyGPTChatTests/Services/SwiftyGPTChatNetworkingServiceTests.swift @@ -37,5 +37,6 @@ final class SwiftyGPTChatNetworkingServiceTests: XCTestCase { let requestBody = SwiftyGPTChatRequestBody(messages: [SwiftyGPTChatUserMessage(content: "Hello world!")], model: .gpt3_5_turbo, n: 1) let responseBody = try await service.request(body: requestBody) let failureBody = try XCTUnwrap(responseBody as? SwiftyGPTChatResponseFailureBody) + XCTAssertEqual(failureBody.error.type, "invalid_request_error") } } From 059c8488d95e8af4eadc4dd1e11e2fa812ade7ce Mon Sep 17 00:00:00 2001 From: Antonio War <59933379+antonio-war@users.noreply.github.com> Date: Sat, 27 Apr 2024 17:00:52 +0200 Subject: [PATCH 3/3] feat: add SwiftyGPTChatResponseTests --- .../Models/SwiftyGPTChatResponseChoice.swift | 6 + .../SwiftyGPTChatResponseFinishReason.swift | 2 +- .../SwiftyGPTChatResponseChoiceTests.swift | 146 ++++++++++++++++++ .../Models/SwiftyGPTChatResponseTests.swift | 19 +++ 4 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 Tests/SwiftyGPTChatTests/Models/SwiftyGPTChatResponseChoiceTests.swift diff --git a/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseChoice.swift b/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseChoice.swift index 0bc5b65..1236b6e 100644 --- a/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseChoice.swift +++ b/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseChoice.swift @@ -25,6 +25,12 @@ public struct SwiftyGPTChatResponseChoice: Decodable, Equatable { } } + init(index: Int, codableMessage: SwiftyGPTChatCodableMessage, finishReason: SwiftyGPTChatResponseFinishReason) { + self.index = index + self.codableMessage = codableMessage + self.finishReason = finishReason + } + public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.index = try container.decode(Int.self, forKey: .index) diff --git a/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseFinishReason.swift b/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseFinishReason.swift index a78df4c..50974f7 100644 --- a/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseFinishReason.swift +++ b/Sources/SwiftyGPTChat/Models/SwiftyGPTChatResponseFinishReason.swift @@ -7,7 +7,7 @@ import Foundation -enum SwiftyGPTChatResponseFinishReason: String, Decodable { +public enum SwiftyGPTChatResponseFinishReason: String, Decodable { case stop case lenght case contentFilter = "content_filter" diff --git a/Tests/SwiftyGPTChatTests/Models/SwiftyGPTChatResponseChoiceTests.swift b/Tests/SwiftyGPTChatTests/Models/SwiftyGPTChatResponseChoiceTests.swift new file mode 100644 index 0000000..7f767dd --- /dev/null +++ b/Tests/SwiftyGPTChatTests/Models/SwiftyGPTChatResponseChoiceTests.swift @@ -0,0 +1,146 @@ +// +// SwiftyGPTChatResponseChoiceTests.swift +// +// +// Created by Antonio Guerra on 27/04/24. +// + +import XCTest +@testable import SwiftyGPTChat + +final class SwiftyGPTChatResponseChoiceTests: XCTestCase { + + var decoder: JSONDecoder! + + override func setUpWithError() throws { + self.decoder = JSONDecoder() + } + + override func tearDownWithError() throws { + self.decoder = nil + } + + func testInitWhenMessageIsSystem() throws { + let message = SwiftyGPTChatSystemMessage(content: "test") + let choice = SwiftyGPTChatResponseChoice(index: 0, codableMessage: .system(message), finishReason: .stop) + let systemMessage = try XCTUnwrap(choice.message as? SwiftyGPTChatSystemMessage) + XCTAssertEqual(choice.index, 0) + XCTAssertEqual(choice.codableMessage, .system(message)) + XCTAssertEqual(systemMessage, message) + XCTAssertEqual(choice.finishReason, .stop) + } + + func testDecodeWhenMessageIsSystem() throws { + let json = """ + { + "index": 0, + "message": { + "role": "system", + "content": "test" + }, + "finish_reason": "stop" + } + """ + let data = try XCTUnwrap(json.data(using: .utf8)) + let choice = try decoder.decode(SwiftyGPTChatResponseChoice.self, from: data) + let message = SwiftyGPTChatSystemMessage(content: "test") + XCTAssertEqual(choice.index, 0) + XCTAssertEqual(choice.codableMessage, .system(message)) + let systemMessage = try XCTUnwrap(choice.message as? SwiftyGPTChatSystemMessage) + XCTAssertEqual(systemMessage, message) + XCTAssertEqual(choice.finishReason, .stop) + } + + func testInitWhenMessageIsAssistant() throws { + let message = SwiftyGPTChatAssistantMessage(content: "test") + let choice = SwiftyGPTChatResponseChoice(index: 0, codableMessage: .assistant(message), finishReason: .stop) + let assistantMessage = try XCTUnwrap(choice.message as? SwiftyGPTChatAssistantMessage) + XCTAssertEqual(choice.index, 0) + XCTAssertEqual(choice.codableMessage, .assistant(message)) + XCTAssertEqual(assistantMessage, message) + XCTAssertEqual(choice.finishReason, .stop) + } + + func testDecodeWhenMessageIsAssistant() throws { + let json = """ + { + "index": 0, + "message": { + "role": "assistant", + "content": "test" + }, + "finish_reason": "stop" + } + """ + let data = try XCTUnwrap(json.data(using: .utf8)) + let choice = try decoder.decode(SwiftyGPTChatResponseChoice.self, from: data) + let message = SwiftyGPTChatAssistantMessage(content: "test") + XCTAssertEqual(choice.index, 0) + XCTAssertEqual(choice.codableMessage, .assistant(message)) + let assistantMessage = try XCTUnwrap(choice.message as? SwiftyGPTChatAssistantMessage) + XCTAssertEqual(assistantMessage, message) + XCTAssertEqual(choice.finishReason, .stop) + } + + func testInitWhenMessageIsUser() throws { + let message = SwiftyGPTChatUserMessage(content: "test") + let choice = SwiftyGPTChatResponseChoice(index: 0, codableMessage: .user(message), finishReason: .stop) + let userMessage = try XCTUnwrap(choice.message as? SwiftyGPTChatUserMessage) + XCTAssertEqual(choice.index, 0) + XCTAssertEqual(choice.codableMessage, .user(message)) + XCTAssertEqual(userMessage, message) + XCTAssertEqual(choice.finishReason, .stop) + } + + func testDecodeWhenMessageIsUser() throws { + let json = """ + { + "index": 0, + "message": { + "role": "user", + "content": "test" + }, + "finish_reason": "stop" + } + """ + let data = try XCTUnwrap(json.data(using: .utf8)) + let choice = try decoder.decode(SwiftyGPTChatResponseChoice.self, from: data) + let message = SwiftyGPTChatUserMessage(content: "test") + XCTAssertEqual(choice.index, 0) + XCTAssertEqual(choice.codableMessage, .user(message)) + let userMessage = try XCTUnwrap(choice.message as? SwiftyGPTChatUserMessage) + XCTAssertEqual(userMessage, message) + XCTAssertEqual(choice.finishReason, .stop) + } + + func testInitWhenMessageIsTool() throws { + let message = SwiftyGPTChatToolMessage(content: "test") + let choice = SwiftyGPTChatResponseChoice(index: 0, codableMessage: .tool(message), finishReason: .stop) + let toolMessage = try XCTUnwrap(choice.message as? SwiftyGPTChatToolMessage) + XCTAssertEqual(choice.index, 0) + XCTAssertEqual(choice.codableMessage, .tool(message)) + XCTAssertEqual(toolMessage, message) + XCTAssertEqual(choice.finishReason, .stop) + } + + func testDecodeWhenMessageIsTool() throws { + let json = """ + { + "index": 0, + "message": { + "role": "tool", + "content": "test" + }, + "finish_reason": "stop" + } + """ + let data = try XCTUnwrap(json.data(using: .utf8)) + let choice = try decoder.decode(SwiftyGPTChatResponseChoice.self, from: data) + let message = SwiftyGPTChatToolMessage(content: "test") + XCTAssertEqual(choice.index, 0) + XCTAssertEqual(choice.codableMessage, .tool(message)) + let toolMessage = try XCTUnwrap(choice.message as? SwiftyGPTChatToolMessage) + XCTAssertEqual(toolMessage, message) + XCTAssertEqual(choice.finishReason, .stop) + } +} diff --git a/Tests/SwiftyGPTChatTests/Models/SwiftyGPTChatResponseTests.swift b/Tests/SwiftyGPTChatTests/Models/SwiftyGPTChatResponseTests.swift index 8a96071..2f21c52 100644 --- a/Tests/SwiftyGPTChatTests/Models/SwiftyGPTChatResponseTests.swift +++ b/Tests/SwiftyGPTChatTests/Models/SwiftyGPTChatResponseTests.swift @@ -9,5 +9,24 @@ import XCTest final class SwiftyGPTChatResponseTests: XCTestCase { + + func testInitWhenBodyIsSuccess() throws { + let body = SwiftyGPTChatResponseSuccessBody(id: "Test", choices: [], created: 0.0, model: .gpt3_5_turbo, fingerprint: "Test", object: .completion, usage: SwiftyGPTChatResponseTokenUsage(completion: 0, prompt: 0, total: 0)) + let response = try SwiftyGPTChatResponse(body: body) + XCTAssertEqual(response, .success(body: body)) + } + + func testInitWhenBodyIsFailure() throws { + let body = SwiftyGPTChatResponseFailureBody(error: SwiftyGPTChatResponseError(type: "Test", message: "Test")) + let response = try SwiftyGPTChatResponse(body: body) + XCTAssertEqual(response, .failure(body: body)) + } + + func testInitWhenBodyIsUnknown() throws { + let body = SwiftyGPTChatResponseUnknownBody() + XCTAssertThrowsError(try SwiftyGPTChatResponse(body: body)) + } + + struct SwiftyGPTChatResponseUnknownBody: SwiftyGPTChatResponseBody {} }