From 13e4d1b8dbdac8e064efb6dcf0329bb1563233cf Mon Sep 17 00:00:00 2001 From: Aleksei Zelentsov Date: Mon, 20 Nov 2023 15:12:19 +0300 Subject: [PATCH] add new runs methods add runs tests --- .../AISwiftAssist/APIs/AssistantsAPI.swift | 6 +- Sources/AISwiftAssist/APIs/ModelsAPI.swift | 6 +- Sources/AISwiftAssist/APIs/RunsAPI.swift | 37 ++++ Sources/AISwiftAssist/Base/HTTPClient.swift | 16 +- .../AISwiftAssist/Base/HTTPRequestError.swift | 6 +- .../Extensions/Data+decode.swift | 4 +- .../AISwiftAssist/Models/Main/ASARun.swift | 13 +- ....swift => ASAAssistantsListResponse.swift} | 2 +- ...onse.swift => ASAModelsListResponse.swift} | 2 +- ...sponse.swift => ASARunsListResponse.swift} | 2 +- .../APIs/AssistantsAPITests.swift | 2 +- .../APIs/MessagesAPITests.swift | 4 +- .../APIs/ModelsAPITests.swift | 2 +- .../APIs/RunsAPITests.swift | 179 ++++++++++++++++++ .../AISwiftAssistTests/Mosks/RunsMocks.swift | 166 ++++++++++++++++ 15 files changed, 419 insertions(+), 28 deletions(-) rename Sources/AISwiftAssist/Models/Response/{ASAListAssistantsResponse.swift => ASAAssistantsListResponse.swift} (93%) rename Sources/AISwiftAssist/Models/Response/{ASAListModelsResponse.swift => ASAModelsListResponse.swift} (87%) rename Sources/AISwiftAssist/Models/Response/{ASAListRunsResponse.swift => ASARunsListResponse.swift} (93%) create mode 100644 Tests/AISwiftAssistTests/APIs/RunsAPITests.swift create mode 100644 Tests/AISwiftAssistTests/Mosks/RunsMocks.swift diff --git a/Sources/AISwiftAssist/APIs/AssistantsAPI.swift b/Sources/AISwiftAssist/APIs/AssistantsAPI.swift index 1e52fd2..be8bda1 100644 --- a/Sources/AISwiftAssist/APIs/AssistantsAPI.swift +++ b/Sources/AISwiftAssist/APIs/AssistantsAPI.swift @@ -13,7 +13,7 @@ public protocol IAssistantsAPI: AnyObject { /// Returns a list of assistants. /// - Parameter parameters: Parameters for the list of assistants. /// - Returns: A list of assistant objects. - func get(with parameters: ASAListAssistantsParameters?) async throws -> ASAListAssistantsResponse + func get(with parameters: ASAListAssistantsParameters?) async throws -> ASAAssistantsListResponse /// Create an assistant with a model and instructions. /// - Parameter createAssistant: The create assistant model. @@ -58,9 +58,9 @@ public final class AssistantsAPI: HTTPClient, IAssistantsAPI { self.urlSession = urlSession } - public func get(with parameters: ASAListAssistantsParameters? = nil) async throws -> ASAListAssistantsResponse { + public func get(with parameters: ASAListAssistantsParameters? = nil) async throws -> ASAAssistantsListResponse { let endpoint = AssistantEndpoint.getAssistants(parameters) - return try await sendRequest(session: urlSession, endpoint: endpoint, responseModel: ASAListAssistantsResponse.self) + return try await sendRequest(session: urlSession, endpoint: endpoint, responseModel: ASAAssistantsListResponse.self) } public func create(by createAssistant: ASACreateAssistantRequest) async throws -> ASAAssistant { diff --git a/Sources/AISwiftAssist/APIs/ModelsAPI.swift b/Sources/AISwiftAssist/APIs/ModelsAPI.swift index d57816d..4cb0bbe 100644 --- a/Sources/AISwiftAssist/APIs/ModelsAPI.swift +++ b/Sources/AISwiftAssist/APIs/ModelsAPI.swift @@ -12,7 +12,7 @@ public protocol IModelsAPI: AnyObject { /// Lists the currently available models, and provides basic information about each one such as the owner and availability. /// - Returns: A list of model objects. - func get() async throws -> ASAListModelsResponse + func get() async throws -> ASAModelsListResponse /// Retrieves a model instance, providing basic information about the model such as the owner and permissioning. /// - Parameter modelId: The ID of the model to use for this request @@ -45,9 +45,9 @@ public final class ModelsAPI: HTTPClient, IModelsAPI { self.urlSession = urlSession } - public func get() async throws -> ASAListModelsResponse { + public func get() async throws -> ASAModelsListResponse { let endpoint = ModelsEndpoint.getModels - return try await sendRequest(session: urlSession, endpoint: endpoint, responseModel: ASAListModelsResponse.self) + return try await sendRequest(session: urlSession, endpoint: endpoint, responseModel: ASAModelsListResponse.self) } public func retrieve(by modelId: String) async throws -> ASAModel { diff --git a/Sources/AISwiftAssist/APIs/RunsAPI.swift b/Sources/AISwiftAssist/APIs/RunsAPI.swift index e2784ef..4d9bcbb 100644 --- a/Sources/AISwiftAssist/APIs/RunsAPI.swift +++ b/Sources/AISwiftAssist/APIs/RunsAPI.swift @@ -16,6 +16,28 @@ public protocol IRunsAPI: AnyObject { /// - createRun: Object with parameters for creating a run. /// - Returns: A run object. func create(by threadId: String, createRun: ASACreateRunRequest) async throws -> ASARun + + /// Returns a list of runs belonging to a thread. + /// - Parameters: + /// - threadId: The ID of the thread the run belongs to. + /// - parameters: Parameters for the list of runs. + /// - Returns: A list of run objects. + func listRuns(by threadId: String, parameters: ASAListRunsParameters?) async throws -> ASARunsListResponse + + /// Modifies a run. + /// - Parameters: + /// - threadId: The ID of the thread that was run. + /// - runId: The ID of the run to modify. + /// - modifyRun: A request structure for modifying a run. + /// - Returns: The modified run object matching the specified ID. + func modify(by threadId: String, runId: String, modifyRun: ASAModifyRunRequest) async throws -> ASARun + + /// Retrieves a run. + /// - Parameters: + /// - threadId: The ID of the thread that was run. + /// - runId: The ID of the run to retrieve. + /// - Returns: The run object matching the specified ID. + func retrieve(by threadId: String, runId: String) async throws -> ASARun } public final class RunsAPI: HTTPClient, IRunsAPI { @@ -43,4 +65,19 @@ public final class RunsAPI: HTTPClient, IRunsAPI { return try await sendRequest(session: urlSession, endpoint: endpoint, responseModel: ASARun.self) } + public func listRuns(by threadId: String, parameters: ASAListRunsParameters?) async throws -> ASARunsListResponse { + let endpoint = RunsEndpoint.listRuns(threadId, parameters) + return try await sendRequest(session: urlSession, endpoint: endpoint, responseModel: ASARunsListResponse.self) + } + + public func modify(by threadId: String, runId: String, modifyRun: ASAModifyRunRequest) async throws -> ASARun { + let endpoint = RunsEndpoint.modifyRun(threadId, runId, modifyRun) + return try await sendRequest(session: urlSession, endpoint: endpoint, responseModel: ASARun.self) + } + + public func retrieve(by threadId: String, runId: String) async throws -> ASARun { + let endpoint = RunsEndpoint.retrieveRun(threadId, runId) + return try await sendRequest(session: urlSession, endpoint: endpoint, responseModel: ASARun.self) + } + } diff --git a/Sources/AISwiftAssist/Base/HTTPClient.swift b/Sources/AISwiftAssist/Base/HTTPClient.swift index 0280e21..6687cf5 100644 --- a/Sources/AISwiftAssist/Base/HTTPClient.swift +++ b/Sources/AISwiftAssist/Base/HTTPClient.swift @@ -58,18 +58,20 @@ extension HTTPClient { if responseModel is Data.Type { return responseModel as! T } - if let decodeData = data.decode(model: responseModel) { + do { + let decodeData = try data.decode(model: responseModel) return decodeData - } else { - throw HTTPRequestError.decode + } catch { + throw HTTPRequestError.decode(error.localizedDescription) } case 400: - if let decodeData = data.decode(model: ValidatorErrorResponse.self) { + do { + let decodeData = try data.decode(model: ValidatorErrorResponse.self) throw HTTPRequestError.validator(error: decodeData) + } catch { + throw HTTPRequestError.unexpectedStatusCode(code: responseCode, + localized: responseCode.localStatusCode) } - throw HTTPRequestError.unexpectedStatusCode(code: responseCode, - localized: responseCode.localStatusCode) - case 401, 403: throw HTTPRequestError.unauthorizate default: throw HTTPRequestError.unexpectedStatusCode(code: responseCode, localized: responseCode.localStatusCode) diff --git a/Sources/AISwiftAssist/Base/HTTPRequestError.swift b/Sources/AISwiftAssist/Base/HTTPRequestError.swift index a911a8c..8a3b7b8 100644 --- a/Sources/AISwiftAssist/Base/HTTPRequestError.swift +++ b/Sources/AISwiftAssist/Base/HTTPRequestError.swift @@ -15,7 +15,7 @@ public struct ValidatorErrorResponse: Codable { /// Types of HTTP Request Errors public enum HTTPRequestError: Error { /// Model decoding error - case decode + case decode(String) /// URL validation error case invalidURL /// Error receiving response from server @@ -47,8 +47,8 @@ public enum HTTPRequestError: Error { extension HTTPRequestError: LocalizedError { public var errorDescription: String? { switch self { - case .decode: - return "Decoding error" + case .decode(let message): + return "Decoding error\n\(message)" case .invalidURL: return "Invalid URL" case .noResponse: diff --git a/Sources/AISwiftAssist/Extensions/Data+decode.swift b/Sources/AISwiftAssist/Extensions/Data+decode.swift index 8296cac..556cc56 100644 --- a/Sources/AISwiftAssist/Extensions/Data+decode.swift +++ b/Sources/AISwiftAssist/Extensions/Data+decode.swift @@ -8,7 +8,7 @@ import Foundation extension Data { - func decode(model: T.Type) -> T? { - return try? JSONDecoder().decode(model, from: self) + func decode(model: T.Type) throws -> T { + return try JSONDecoder().decode(model, from: self) } } diff --git a/Sources/AISwiftAssist/Models/Main/ASARun.swift b/Sources/AISwiftAssist/Models/Main/ASARun.swift index 94a03dc..ead7e5a 100644 --- a/Sources/AISwiftAssist/Models/Main/ASARun.swift +++ b/Sources/AISwiftAssist/Models/Main/ASARun.swift @@ -34,7 +34,7 @@ public struct ASARun: Codable { public let lastError: LastError? /// The Unix timestamp (in seconds) for when the run will expire. - public let expiresAt: Int + public let expiresAt: Int? /// The Unix timestamp (in seconds) for when the run was started. Null if not started. public let startedAt: Int? @@ -52,10 +52,11 @@ public struct ASARun: Codable { public let model: String /// The instructions that the assistant used for this run. - public let instructions: String + public let instructions: String? /// The list of tools that the assistant used for this run. - public let tools: [String] + /// Tools can be of types code_interpreter, retrieval, or function. + public let tools: [Tool] /// The list of File IDs the assistant used for this run. public let fileIds: [String] @@ -112,6 +113,12 @@ public struct ASARun: Codable { public let message: String } + /// Represents a tool enabled on the assistant. + public struct Tool: Codable { + /// The type of the tool (e.g., code_interpreter, retrieval, function). + public let type: String + } + enum CodingKeys: String, CodingKey { case id, object case createdAt = "created_at" diff --git a/Sources/AISwiftAssist/Models/Response/ASAListAssistantsResponse.swift b/Sources/AISwiftAssist/Models/Response/ASAAssistantsListResponse.swift similarity index 93% rename from Sources/AISwiftAssist/Models/Response/ASAListAssistantsResponse.swift rename to Sources/AISwiftAssist/Models/Response/ASAAssistantsListResponse.swift index 9c65dfd..b81619e 100644 --- a/Sources/AISwiftAssist/Models/Response/ASAListAssistantsResponse.swift +++ b/Sources/AISwiftAssist/Models/Response/ASAAssistantsListResponse.swift @@ -8,7 +8,7 @@ import Foundation /// A response structure for listing assistants. -public struct ASAListAssistantsResponse: Codable { +public struct ASAAssistantsListResponse: Codable { /// The object type, which is always 'list'. public let object: String diff --git a/Sources/AISwiftAssist/Models/Response/ASAListModelsResponse.swift b/Sources/AISwiftAssist/Models/Response/ASAModelsListResponse.swift similarity index 87% rename from Sources/AISwiftAssist/Models/Response/ASAListModelsResponse.swift rename to Sources/AISwiftAssist/Models/Response/ASAModelsListResponse.swift index bbd9616..54cac26 100644 --- a/Sources/AISwiftAssist/Models/Response/ASAListModelsResponse.swift +++ b/Sources/AISwiftAssist/Models/Response/ASAModelsListResponse.swift @@ -7,7 +7,7 @@ import Foundation -public struct ASAListModelsResponse: Codable { +public struct ASAModelsListResponse: Codable { /// The object type, which is always "list". public let object: String diff --git a/Sources/AISwiftAssist/Models/Response/ASAListRunsResponse.swift b/Sources/AISwiftAssist/Models/Response/ASARunsListResponse.swift similarity index 93% rename from Sources/AISwiftAssist/Models/Response/ASAListRunsResponse.swift rename to Sources/AISwiftAssist/Models/Response/ASARunsListResponse.swift index 8a5e1ad..9e6a066 100644 --- a/Sources/AISwiftAssist/Models/Response/ASAListRunsResponse.swift +++ b/Sources/AISwiftAssist/Models/Response/ASARunsListResponse.swift @@ -8,7 +8,7 @@ import Foundation /// Represents a response containing a list of runs. -public struct ASAListRunsResponse: Codable { +public struct ASARunsListResponse: Codable { /// The object type, which is always 'list'. public let object: String diff --git a/Tests/AISwiftAssistTests/APIs/AssistantsAPITests.swift b/Tests/AISwiftAssistTests/APIs/AssistantsAPITests.swift index 2b7ff8b..abbdbdb 100644 --- a/Tests/AISwiftAssistTests/APIs/AssistantsAPITests.swift +++ b/Tests/AISwiftAssistTests/APIs/AssistantsAPITests.swift @@ -35,7 +35,7 @@ final class AssistantsAPITests: XCTestCase { return (response, mockData) } - let response: ASAListAssistantsResponse = try await assistantsAPI.get(with: nil) + let response: ASAAssistantsListResponse = try await assistantsAPI.get(with: nil) XCTAssertNotNil(response) XCTAssertEqual(response.object, "list") diff --git a/Tests/AISwiftAssistTests/APIs/MessagesAPITests.swift b/Tests/AISwiftAssistTests/APIs/MessagesAPITests.swift index 238c8a6..8844f5a 100644 --- a/Tests/AISwiftAssistTests/APIs/MessagesAPITests.swift +++ b/Tests/AISwiftAssistTests/APIs/MessagesAPITests.swift @@ -109,7 +109,8 @@ final class MessagesAPITests: XCTestCase { return (response, mockData) } - let listResponse: ASAMessagesListResponse = try await messagesAPI.getMessages(by: "thread_abc123", parameters: nil) + let listResponse: ASAMessagesListResponse = try await messagesAPI.getMessages(by: "thread_abc123", + parameters: nil) XCTAssertEqual(listResponse.object, "list") XCTAssertEqual(listResponse.data.count, 2) @@ -122,5 +123,4 @@ final class MessagesAPITests: XCTestCase { } } - } diff --git a/Tests/AISwiftAssistTests/APIs/ModelsAPITests.swift b/Tests/AISwiftAssistTests/APIs/ModelsAPITests.swift index c6bfb72..f386526 100644 --- a/Tests/AISwiftAssistTests/APIs/ModelsAPITests.swift +++ b/Tests/AISwiftAssistTests/APIs/ModelsAPITests.swift @@ -35,7 +35,7 @@ final class ModelsAPITests: XCTestCase { return (response, mockData) } - let listResponse: ASAListModelsResponse = try await modelsAPI.get() + let listResponse: ASAModelsListResponse = try await modelsAPI.get() XCTAssertEqual(listResponse.data[0].id, "model-id-0") XCTAssertEqual(listResponse.data[0].object, "model") diff --git a/Tests/AISwiftAssistTests/APIs/RunsAPITests.swift b/Tests/AISwiftAssistTests/APIs/RunsAPITests.swift new file mode 100644 index 0000000..cf447c1 --- /dev/null +++ b/Tests/AISwiftAssistTests/APIs/RunsAPITests.swift @@ -0,0 +1,179 @@ +// +// RunsAPITests.swift +// +// +// Created by Alexey on 11/20/23. +// + +import XCTest +@testable import AISwiftAssist + +final class RunsAPITests: XCTestCase { + + var runsAPI: IRunsAPI! + + override func setUp() { + super.setUp() + let configuration = URLSessionConfiguration.default + configuration.protocolClasses = [MockURLProtocol.self] + let mockURLSession = URLSession(configuration: configuration) + runsAPI = RunsAPI(urlSession: mockURLSession) + } + + override func tearDown() { + runsAPI = nil + super.tearDown() + } + + func testCreateRun() async { + do { + let mockData = Self.create.data(using: .utf8)! + + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)! + return (response, mockData) + } + + let createRunRequest = ASACreateRunRequest(assistantId: "asst_abc123") + let run: ASARun = try await runsAPI.create(by: "thread_abc123", + createRun: createRunRequest) + + XCTAssertEqual(run.id, "run_abc123") + XCTAssertEqual(run.object, "thread.run") + XCTAssertEqual(run.createdAt, 1699063290) + XCTAssertEqual(run.assistantId, "asst_abc123") + XCTAssertEqual(run.threadId, "thread_abc123") + XCTAssertEqual(run.status, "queued") + XCTAssertEqual(run.startedAt, 1699063290) + XCTAssertNil(run.expiresAt) + XCTAssertNil(run.cancelledAt) + XCTAssertNil(run.failedAt) + XCTAssertEqual(run.completedAt, 1699063291) + XCTAssertNil(run.lastError) + XCTAssertEqual(run.model, "gpt-4") + XCTAssertNil(run.instructions) + XCTAssertEqual(run.tools.count, 1) + XCTAssertEqual(run.tools.first?.type, "code_interpreter") + XCTAssertEqual(run.fileIds, ["file-abc123", "file-abc456"]) + XCTAssertTrue(run.metadata?.isEmpty ?? true) + } catch { + XCTFail("Error: \(error.localizedDescription)") + } + } + + func testRetrieveRun() async { + do { + let mockData = Self.retrieve.data(using: .utf8)! + + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)! + return (response, mockData) + } + + let run: ASARun = try await runsAPI.retrieve(by: "thread_abc123", + runId: "run_abc123") + + XCTAssertEqual(run.id, "run_abc123") + XCTAssertEqual(run.object, "thread.run") + XCTAssertEqual(run.createdAt, 1699075072) + XCTAssertEqual(run.assistantId, "asst_abc123") + XCTAssertEqual(run.threadId, "thread_abc123") + XCTAssertEqual(run.status, "completed") + XCTAssertEqual(run.startedAt, 1699075072) + XCTAssertNil(run.expiresAt) + XCTAssertNil(run.cancelledAt) + XCTAssertNil(run.failedAt) + XCTAssertEqual(run.completedAt, 1699075073) + XCTAssertNil(run.lastError) + XCTAssertEqual(run.model, "gpt-3.5-turbo") + XCTAssertNil(run.instructions) + XCTAssertEqual(run.tools.count, 1) + XCTAssertEqual(run.tools.first?.type, "code_interpreter") + XCTAssertEqual(run.fileIds, ["file-abc123", "file-abc456"]) + XCTAssertTrue(run.metadata?.isEmpty ?? true) + } catch { + XCTFail("Error: \(error)") + } + } + + func testModifyRun() async { + do { + let mockData = Self.modify.data(using: .utf8)! + + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)! + return (response, mockData) + } + + let modifyRunRequest = ASAModifyRunRequest(metadata: ["user_id": "user_abc123"]) + let run: ASARun = try await runsAPI.modify(by: "thread_abc123", + runId: "run_abc123", + modifyRun: modifyRunRequest) + + XCTAssertEqual(run.id, "run_abc123") + XCTAssertEqual(run.object, "thread.run") + XCTAssertEqual(run.createdAt, 1699075072) + XCTAssertEqual(run.assistantId, "asst_abc123") + XCTAssertEqual(run.threadId, "thread_abc123") + XCTAssertEqual(run.status, "completed") + XCTAssertEqual(run.startedAt, 1699075072) + XCTAssertNil(run.expiresAt) + XCTAssertNil(run.cancelledAt) + XCTAssertNil(run.failedAt) + XCTAssertEqual(run.completedAt, 1699075073) + XCTAssertNil(run.lastError) + XCTAssertEqual(run.model, "gpt-3.5-turbo") + XCTAssertNil(run.instructions) + XCTAssertEqual(run.tools.count, 1) + XCTAssertEqual(run.tools.first?.type, "code_interpreter") + XCTAssertEqual(run.fileIds, ["file-abc123", "file-abc456"]) + XCTAssertEqual(run.metadata?["user_id"], "user_abc123") + } catch { + XCTFail("Error: \(error)") + } + } + + func testListRuns() async { + do { + let mockData = Self.list.data(using: .utf8)! + + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)! + return (response, mockData) + } + + let listResponse: ASARunsListResponse = try await runsAPI.listRuns(by: "thread_abc123", + parameters: nil) + + XCTAssertEqual(listResponse.object, "list") + XCTAssertEqual(listResponse.data.count, 2) + + let firstRun = listResponse.data[0] + XCTAssertEqual(firstRun.id, "run_abc123") + XCTAssertEqual(firstRun.object, "thread.run") + XCTAssertEqual(firstRun.createdAt, 1699075072) + XCTAssertEqual(firstRun.assistantId, "asst_abc123") + XCTAssertEqual(firstRun.threadId, "thread_abc123") + XCTAssertEqual(firstRun.status, "completed") + XCTAssertEqual(firstRun.startedAt, 1699075072) + XCTAssertNil(firstRun.expiresAt) + XCTAssertNil(firstRun.cancelledAt) + XCTAssertNil(firstRun.failedAt) + XCTAssertEqual(firstRun.completedAt, 1699075073) + XCTAssertNil(firstRun.lastError) + XCTAssertEqual(firstRun.model, "gpt-3.5-turbo") + XCTAssertNil(firstRun.instructions) + XCTAssertEqual(firstRun.tools.count, 1) + XCTAssertEqual(firstRun.tools[0].type, "code_interpreter") + XCTAssertEqual(firstRun.fileIds, ["file-abc123", "file-abc456"]) + XCTAssertTrue(firstRun.metadata?.isEmpty ?? true) + + XCTAssertEqual(listResponse.firstId, "run_abc123") + XCTAssertEqual(listResponse.lastId, "run_abc456") + XCTAssertFalse(listResponse.hasMore) + } catch { + XCTFail("Error: \(error)") + } + } + +} diff --git a/Tests/AISwiftAssistTests/Mosks/RunsMocks.swift b/Tests/AISwiftAssistTests/Mosks/RunsMocks.swift new file mode 100644 index 0000000..97731bd --- /dev/null +++ b/Tests/AISwiftAssistTests/Mosks/RunsMocks.swift @@ -0,0 +1,166 @@ +// +// File.swift +// +// +// Created by Alexey on 11/20/23. +// + +import Foundation + +extension RunsAPITests { + static let create: String = + """ + { + "id": "run_abc123", + "object": "thread.run", + "created_at": 1699063290, + "assistant_id": "asst_abc123", + "thread_id": "thread_abc123", + "status": "queued", + "started_at": 1699063290, + "expires_at": null, + "cancelled_at": null, + "failed_at": null, + "completed_at": 1699063291, + "last_error": null, + "model": "gpt-4", + "instructions": null, + "tools": [ + { + "type": "code_interpreter" + } + ], + "file_ids": [ + "file-abc123", + "file-abc456" + ], + "metadata": {} + } + """ + + static let retrieve: String = + """ + { + "id": "run_abc123", + "object": "thread.run", + "created_at": 1699075072, + "assistant_id": "asst_abc123", + "thread_id": "thread_abc123", + "status": "completed", + "started_at": 1699075072, + "expires_at": null, + "cancelled_at": null, + "failed_at": null, + "completed_at": 1699075073, + "last_error": null, + "model": "gpt-3.5-turbo", + "instructions": null, + "tools": [ + { + "type": "code_interpreter" + } + ], + "file_ids": [ + "file-abc123", + "file-abc456" + ], + "metadata": {} + } + """ + + static let modify: String = + """ + { + "id": "run_abc123", + "object": "thread.run", + "created_at": 1699075072, + "assistant_id": "asst_abc123", + "thread_id": "thread_abc123", + "status": "completed", + "started_at": 1699075072, + "expires_at": null, + "cancelled_at": null, + "failed_at": null, + "completed_at": 1699075073, + "last_error": null, + "model": "gpt-3.5-turbo", + "instructions": null, + "tools": [ + { + "type": "code_interpreter" + } + ], + "file_ids": [ + "file-abc123", + "file-abc456" + ], + "metadata": { + "user_id": "user_abc123" + } + } + """ + + static let list: String = + """ +{ + "object": "list", + "data": [ + { + "id": "run_abc123", + "object": "thread.run", + "created_at": 1699075072, + "assistant_id": "asst_abc123", + "thread_id": "thread_abc123", + "status": "completed", + "started_at": 1699075072, + "expires_at": null, + "cancelled_at": null, + "failed_at": null, + "completed_at": 1699075073, + "last_error": null, + "model": "gpt-3.5-turbo", + "instructions": null, + "tools": [ + { + "type": "code_interpreter" + } + ], + "file_ids": [ + "file-abc123", + "file-abc456" + ], + "metadata": {} + }, + { + "id": "run_abc456", + "object": "thread.run", + "created_at": 1699063290, + "assistant_id": "asst_abc123", + "thread_id": "thread_abc123", + "status": "completed", + "started_at": 1699063290, + "expires_at": null, + "cancelled_at": null, + "failed_at": null, + "completed_at": 1699063291, + "last_error": null, + "model": "gpt-3.5-turbo", + "instructions": null, + "tools": [ + { + "type": "code_interpreter" + } + ], + "file_ids": [ + "file-abc123", + "file-abc456" + ], + "metadata": {} + } + ], + "first_id": "run_abc123", + "last_id": "run_abc456", + "has_more": false +} +""" +}