Skip to content

Commit

Permalink
Update Gemini (Incomplete)
Browse files Browse the repository at this point in the history
  • Loading branch information
pmanot committed Jan 16, 2025
1 parent 68d193a commit 3aba2ad
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,31 @@ extension _Gemini.APISpecification {
}
}

public struct FileUploadInput: Codable, HTTPRequest.Multipart.ContentConvertible {
public struct FinalizeFileUploadInput {
public let data: Data
public let uploadUrl: String
public let fileSize: Int

public init(data: Data, uploadUrl: String, fileSize: Int) {
self.data = data
self.uploadUrl = uploadUrl
self.fileSize = fileSize
}
}

public struct StartFileUploadInput: Codable {
public struct UploadMetadata: Codable {
let file: FileMetadata

struct FileMetadata: Codable {
let display_name: String
}
}

public let fileData: Data
public let mimeType: String
public let displayName: String
public let metadata: UploadMetadata

public init(
fileData: Data,
Expand All @@ -155,11 +176,12 @@ extension _Gemini.APISpecification {
self.fileData = fileData
self.mimeType = mimeType
self.displayName = displayName
self.metadata = .init(file: .init(display_name: displayName))
}

/*
public func __conversion() throws -> HTTPRequest.Multipart.Content {
var result = HTTPRequest.Multipart.Content()

// TODO: - Add this to `HTTPMediaType` @jared @vmanot
let fileExtension: String = {
guard let subtype = mimeType.split(separator: "/").last else {
Expand Down Expand Up @@ -188,17 +210,11 @@ extension _Gemini.APISpecification {
}
}()

result.append(
.file(
named: "file",
data: fileData,
filename: "\(displayName).\(fileExtension)",
contentType: .init(rawValue: mimeType)
)
)
result.ap

return result
}
*/
}

public struct DeleteFileInput: Codable {
Expand Down
53 changes: 48 additions & 5 deletions Sources/_Gemini/Intramodular/API/_Gemini.APISpecification.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,29 @@ extension _Gemini {
// Initial Upload Request endpoint
@POST
@Path("/upload/v1beta/files")
@Header([
"X-Goog-Upload-Command": "start, upload, finalize"
])
@Body(multipart: .input)
var uploadFile = Endpoint<RequestBodies.FileUploadInput, ResponseBodies.FileUpload, Void>()
@Header({ context in
[
HTTPHeaderField(key: "X-Goog-Upload-Protocol", value: "resumable"),
HTTPHeaderField(key: "X-Goog-Upload-Command", value: "start"),
HTTPHeaderField(key: "X-Goog-Upload-Header-Content-Length", value: "\(context.input.fileData.count)"),
HTTPHeaderField(key: "X-Goog-Upload-Header-Content-Type", value: context.input.mimeType),
HTTPHeaderField.contentType(.json)
]
})
@Body(json: \RequestBodies.StartFileUploadInput.metadata)
var startFileUpload = Endpoint<RequestBodies.StartFileUploadInput, String, Self.Options>()

@POST
@Path({ context in context.input.uploadUrl })
@Header({ context in
[
HTTPHeaderField(key: "Content-Length", value: "\(context.input.fileSize)"),
HTTPHeaderField(key: "X-Goog-Upload-Offset", value: "0"),
HTTPHeaderField(key: "X-Goog-Upload-Command", value: "upload, finalize")
]
})
@Body(json: \RequestBodies.FinalizeFileUploadInput.data)
var finalizeFileUpload = Endpoint<RequestBodies.FinalizeFileUploadInput, ResponseBodies.FileUpload, Void>()

// File Status endpoint
@GET
Expand Down Expand Up @@ -157,6 +175,7 @@ extension _Gemini.APISpecification {
context: context
)

// FIXME: (@jared) - why are you replacing the query instead of appending a new query item? is this intentional?
if let apiKey = context.root.configuration.apiKey {
request = request.query([.init(name: "key", value: apiKey)])
}
Expand All @@ -173,10 +192,34 @@ extension _Gemini.APISpecification {

try response.validate()


if let options: _Gemini.APISpecification.Options = context.options as? _Gemini.APISpecification.Options, let headerKey = options.outputHeaderKey {
print("HEADERS: \(response.headerFields)")
let stringValue: String? = response.headerFields.first (where: { $0.key == headerKey })?.value
print(stringValue)

switch Output.self {
case String.self:
return (try stringValue.unwrap()) as! Output
case Optional<String>.self:
return stringValue as! Output
default:
throw _Gemini.APIError.invalidContentType
}
}

return try response.decode(
Output.self,
keyDecodingStrategy: .convertFromSnakeCase
)
}
}

public class Options {
var outputHeaderKey: HTTPHeaderField.Key?

init(outputHeaderKey: HTTPHeaderField.Key? = nil) {
self.outputHeaderKey = outputHeaderKey
}
}
}
46 changes: 28 additions & 18 deletions Sources/_Gemini/Intramodular/_Gemini.Client+Files.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ import Merge
import NetworkKit
import Swallow

fileprivate enum TempError: CustomStringError, Error {
case fetchedResponse

public var description: String {
switch self {
case .fetchedResponse:
return "Got response url from header"
}
}
}

extension _Gemini.Client {
public func uploadFile(
from data: Data,
Expand All @@ -20,27 +31,26 @@ extension _Gemini.Client {
throw FileProcessingError.invalidFileName
}

do {
var mimeType: String? = mimeType?.rawValue ?? _MediaAssetFileType(data)?.mimeType

if mimeType == nil, let swiftType {
mimeType = HTTPMediaType(_swiftType: swiftType)?.rawValue
}

let input = _Gemini.APISpecification.RequestBodies.FileUploadInput(
fileData: data,
mimeType: try mimeType.unwrap(),
displayName: displayName
)

let response = try await run(\.uploadFile, with: input)

return response.file
} catch {
throw _Gemini.APIError.unknown(message: "File upload failed: \(error.localizedDescription)")
var mimeType: String? = mimeType?.rawValue ?? _MediaAssetFileType(data)?.mimeType

if mimeType == nil, let swiftType {
mimeType = HTTPMediaType(_swiftType: swiftType)?.rawValue
}

let input = _Gemini.APISpecification.RequestBodies.StartFileUploadInput(
fileData: data,
mimeType: try mimeType.unwrap(),
displayName: displayName
)

let uploadURLString: String = try await run(\.startFileUpload, with: input, options: _Gemini.APISpecification.Options(outputHeaderKey: .custom("x-goog-upload-url"))).value

let result: _Gemini.APISpecification.ResponseBodies.FileUpload = try await run(\.finalizeFileUpload, with: _Gemini.APISpecification.RequestBodies.FinalizeFileUploadInput(data: data, uploadUrl: uploadURLString, fileSize: data.count))

return result.file
}


public func uploadFile(
from url: URL,
mimeType: HTTPMediaType?,
Expand Down

0 comments on commit 3aba2ad

Please sign in to comment.