Skip to content

Commit

Permalink
Assistant Files
Browse files Browse the repository at this point in the history
Add new assistant methods
  • Loading branch information
DeveloperZelentsov authored Dec 5, 2023
2 parents 484f907 + cce014a commit d39c8ae
Show file tree
Hide file tree
Showing 8 changed files with 328 additions and 10 deletions.
47 changes: 47 additions & 0 deletions Sources/AISwiftAssist/APIs/AssistantsAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,34 @@ public protocol IAssistantsAPI: AnyObject {
/// - Parameter assistantId: The ID of the assistant to delete.
/// - Returns: Deletion status
func delete(by assistantId: String) async throws -> ASADeleteModelResponse

/// Create an assistant file by attaching a File to an assistant.
/// - Parameters:
/// - assistantId: The ID of the assistant for which to create a File.
/// - request: The request object containing the File ID.
/// - Returns: An assistant file object.
func createFile(for assistantId: String, with request: ASACreateAssistantFileRequest) async throws -> ASAAssistantFile

/// Retrieves an assistant file.
/// - Parameters:
/// - assistantId: The ID of the assistant who the file belongs to.
/// - fileId: The ID of the file to retrieve.
/// - Returns: The assistant file object matching the specified ID.
func retrieveFile(for assistantId: String, fileId: String) async throws -> ASAAssistantFile

/// Delete an assistant file.
/// - Parameters:
/// - assistantId: The ID of the assistant that the file belongs to.
/// - fileId: The ID of the file to delete.
/// - Returns: Deletion status.
func deleteFile(for assistantId: String, fileId: String) async throws -> ASADeleteModelResponse

/// Returns a list of assistant files.
/// - Parameters:
/// - assistantId: The ID of the assistant the file belongs to.
/// - parameters: Parameters for the list of assistant files.
/// - Returns: A list of assistant file objects.
func listFiles(for assistantId: String, with parameters: ASAListFilesParameters?) async throws -> ASAAssistantFilesListResponse
}

public final class AssistantsAPI: HTTPClient, IAssistantsAPI {
Expand Down Expand Up @@ -82,6 +110,25 @@ public final class AssistantsAPI: HTTPClient, IAssistantsAPI {
let endpoint = AssistantEndpoint.deleteAssistant(assistantId)
return try await sendRequest(session: urlSession, endpoint: endpoint, responseModel: ASADeleteModelResponse.self)
}

public func createFile(for assistantId: String, with request: ASACreateAssistantFileRequest) async throws -> ASAAssistantFile {
let endpoint = AssistantEndpoint.createFile(assistantId, request)
return try await sendRequest(session: urlSession, endpoint: endpoint, responseModel: ASAAssistantFile.self)
}

public func retrieveFile(for assistantId: String, fileId: String) async throws -> ASAAssistantFile {
let endpoint = AssistantEndpoint.retrieveFile(assistantId, fileId)
return try await sendRequest(session: urlSession, endpoint: endpoint, responseModel: ASAAssistantFile.self)
}

public func deleteFile(for assistantId: String, fileId: String) async throws -> ASADeleteModelResponse {
let endpoint = AssistantEndpoint.deleteFile(assistantId, fileId)
return try await sendRequest(session: urlSession, endpoint: endpoint, responseModel: ASADeleteModelResponse.self)
}

public func listFiles(for assistantId: String, with parameters: ASAListFilesParameters? = nil) async throws -> ASAAssistantFilesListResponse {
let endpoint = AssistantEndpoint.listFiles(assistantId, parameters)
return try await sendRequest(session: urlSession, endpoint: endpoint, responseModel: ASAAssistantFilesListResponse.self)
}
}

19 changes: 16 additions & 3 deletions Sources/AISwiftAssist/Endpoints/AssistantEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ enum AssistantEndpoint {
case retrieveAssistant(String)
case modifyAssistant(String, ASAModifyAssistantRequest)
case deleteAssistant(String)
case createFile(String, ASACreateAssistantFileRequest)
case retrieveFile(String, String)
case deleteFile(String, String)
case listFiles(String, ASAListFilesParameters?)
}

extension AssistantEndpoint: CustomEndpoint {
Expand All @@ -26,8 +30,9 @@ extension AssistantEndpoint: CustomEndpoint {
public var queryItems: [URLQueryItem]? {
var items: [URLQueryItem]?
switch self {
case .createAssistant, .deleteAssistant, .retrieveAssistant, .modifyAssistant: items = nil
case .createAssistant, .deleteAssistant, .retrieveAssistant, .modifyAssistant, .createFile, .retrieveFile, .deleteFile: items = nil
case .getAssistants(let params): items = Utils.createURLQueryItems(from: params)
case .listFiles(_, let params): items = Utils.createURLQueryItems(from: params)
}
return items
}
Expand All @@ -38,6 +43,10 @@ extension AssistantEndpoint: CustomEndpoint {
case .retrieveAssistant(let assistantId): return "assistants/\(assistantId)"
case .modifyAssistant(let assistantId, _): return "assistants/\(assistantId)"
case .deleteAssistant(let assistantId): return "assistants/\(assistantId)"
case .createFile(let assistantId, _): return "assistants/\(assistantId)/files"
case .retrieveFile(let assistantId, let fileId): return "assistants/\(assistantId)/files/\(fileId)"
case .deleteFile(let assistantId, let fileId): return "assistants/\(assistantId)/files/\(fileId)"
case .listFiles(let assistantId, _): return "assistants/\(assistantId)/files"
}
}

Expand All @@ -48,6 +57,10 @@ extension AssistantEndpoint: CustomEndpoint {
case .retrieveAssistant: return .get
case .modifyAssistant: return .post
case .deleteAssistant: return .delete
case .createFile: return .post
case .retrieveFile: return .get
case .deleteFile: return .delete
case .listFiles: return .get
}
}

Expand All @@ -61,8 +74,8 @@ extension AssistantEndpoint: CustomEndpoint {
switch self {
case .createAssistant(let createAssistant): return .init(object: createAssistant)
case .modifyAssistant(_, let request): return .init(object: request)
case .deleteAssistant, .retrieveAssistant, .getAssistants: return nil

case .createFile(_, let request): return .init(object: request)
case .deleteAssistant, .retrieveAssistant, .getAssistants, .retrieveFile, .deleteFile, .listFiles: return nil
}
}
}
27 changes: 27 additions & 0 deletions Sources/AISwiftAssist/Models/Main/ASAAssistantFile.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// File.swift
//
//
// Created by Alexey on 12/5/23.
//

import Foundation

/// Represents an assistant file that can be used by the assistant.
public struct ASAAssistantFile: Codable {
/// The identifier of the assistant file.
public let id: String

/// The object type, which is always 'assistant.file'.
public let objectType: String

/// The Unix timestamp (in seconds) for when the assistant file was created.
public let createdAt: Int

/// The identifier of the assistant to which this file belongs.
public let assistantId: String

enum CodingKeys: String, CodingKey {
case id, objectType = "object", createdAt = "created_at", assistantId = "assistant_id"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// File.swift
//
//
// Created by Alexey on 12/5/23.
//

import Foundation

/// Represents a request to create an assistant file.
public struct ASACreateAssistantFileRequest: Codable {
/// A File ID (with purpose="assistants") that the assistant should use.
/// Useful for tools like retrieval and code_interpreter that can access files.
public let fileId: String

public init(fileId: String) {
self.fileId = fileId
}

enum CodingKeys: String, CodingKey {
case fileId = "file_id"
}
}
34 changes: 34 additions & 0 deletions Sources/AISwiftAssist/Models/Request/ASAListFilesParameters.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// File.swift
//
//
// Created by Alexey on 12/5/23.
//

import Foundation

/// Parameters for listing assistant files.
public struct ASAListFilesParameters: Encodable {
/// A limit on the number of objects to be returned.
/// Can range between 1 and 100. Defaults to 20.
public let limit: Int?

/// Sort order by the created_at timestamp of the objects.
/// 'asc' for ascending order and 'desc' for descending order.
public let order: String?

/// A cursor for use in pagination. 'after' is an object ID that defines
/// your place in the list, to fetch the next page of the list.
public let after: String?

/// A cursor for use in pagination. 'before' is an object ID that defines
/// your place in the list, to fetch the previous page of the list.
public let before: String?

public init(limit: Int? = nil, order: String? = nil, after: String? = nil, before: String? = nil) {
self.limit = limit
self.order = order
self.after = after
self.before = before
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// File.swift
//
//
// Created by Alexey on 12/5/23.
//

import Foundation

/// Represents a response containing a list of assistant files.
public struct ASAAssistantFilesListResponse: Codable {
/// The object type, which is always 'list'.
public let object: String

/// The list of assistant files.
public let data: [ASAAssistantFile]

/// The ID of the first file in the list.
public let firstId: String

/// The ID of the last file in the list.
public let lastId: String

/// Boolean indicating if there are more files available.
public let hasMore: Bool

enum CodingKeys: String, CodingKey {
case data, firstId = "first_id", lastId = "last_id", hasMore = "has_more", object
}
}
92 changes: 92 additions & 0 deletions Tests/AISwiftAssistTests/APIs/AssistantsAPITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -188,5 +188,97 @@ final class AssistantsAPITests: XCTestCase {
}
}

func testCreateFile() async {
do {
// Simulate server response
let mockData = AssistantsAPITests.createFile.data(using: .utf8)!

MockURLProtocol.requestHandler = { request in
let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!
return (response, mockData)
}

let createRequest = ASACreateAssistantFileRequest(fileId: "file-abc123")
let file: ASAAssistantFile = try await assistantsAPI.createFile(for: "asst_abc123", with: createRequest)

// Checks
XCTAssertEqual(file.id, "file-abc123")
XCTAssertEqual(file.objectType, "assistant.file")
XCTAssertEqual(file.createdAt, 1699055364)
XCTAssertEqual(file.assistantId, "asst_abc123")
} catch {
XCTFail("Error: \(error)")
}
}

func testRetrieveFile() async {
do {
// Simulate server response
let mockData = AssistantsAPITests.retrieveFile.data(using: .utf8)!

MockURLProtocol.requestHandler = { request in
let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!
return (response, mockData)
}

let file: ASAAssistantFile = try await assistantsAPI.retrieveFile(for: "asst_abc123", fileId: "file-abc123")

// Checks
XCTAssertEqual(file.id, "file-abc123")
XCTAssertEqual(file.objectType, "assistant.file")
XCTAssertEqual(file.createdAt, 1699055364)
XCTAssertEqual(file.assistantId, "asst_abc123")
} catch {
XCTFail("Error: \(error)")
}
}

func testDeleteFile() async {
do {
// Simulate server response
let mockData = AssistantsAPITests.deleteFile.data(using: .utf8)!

MockURLProtocol.requestHandler = { request in
let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!
return (response, mockData)
}

let deleteResponse: ASADeleteModelResponse = try await assistantsAPI.deleteFile(for: "asst_abc123", fileId: "file-abc123")

// Checks
XCTAssertEqual(deleteResponse.id, "file-abc123")
XCTAssertEqual(deleteResponse.object, "assistant.file.deleted")
XCTAssertTrue(deleteResponse.deleted)
} catch {
XCTFail("Error: \(error)")
}
}

func testListFiles() async {
do {
// Simulate server response
let mockData = AssistantsAPITests.listFiles.data(using: .utf8)!

MockURLProtocol.requestHandler = { request in
let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!
return (response, mockData)
}

let listParameters = ASAListFilesParameters()
let fileList: ASAAssistantFilesListResponse = try await assistantsAPI.listFiles(for: "asst_abc123", with: listParameters)

// Checks
XCTAssertEqual(fileList.object, "list")
XCTAssertEqual(fileList.data.count, 2)
XCTAssertEqual(fileList.firstId, "file-abc123")
XCTAssertEqual(fileList.lastId, "file-abc456")
XCTAssertFalse(fileList.hasMore)
XCTAssertEqual(fileList.data[0].id, "file-abc123")
XCTAssertEqual(fileList.data[1].id, "file-abc456")
} catch {
XCTFail("Error: \(error)")
}
}


}
Loading

0 comments on commit d39c8ae

Please sign in to comment.