-- | @\/v1\/uploads@ {-# LANGUAGE InstanceSigs #-} module OpenAI.V1.Uploads ( -- * Main types UploadID(..) , CreateUpload(..) , _CreateUpload , AddUploadPart(..) , _AddUploadPart , CompleteUpload(..) , _CompleteUpload , UploadObject(..) , PartObject(..) -- * Other types , Status(..) -- * Servant , API ) where import OpenAI.Prelude import OpenAI.V1.Files (FileObject, Purpose) import qualified Data.Text as Text -- | Upload ID newtype UploadID = UploadID{ text :: Text } deriving newtype (FromJSON, IsString, Show, ToHttpApiData, ToJSON) -- | Request body for @\/v1\/uploads@ data CreateUpload = CreateUpload { filename :: Text , purpose :: Purpose , bytes :: Natural , mime_type :: Text } deriving stock (Generic, Show) deriving anyclass (ToJSON) -- | Default `CreateUpload` _CreateUpload :: CreateUpload _CreateUpload = CreateUpload{ } -- | Request body for @\/v1\/uploads\/:upload_id\/parts@ data AddUploadPart = AddUploadPart{ data_ :: FilePath } -- | Default `AddUploadPart` _AddUploadPart :: AddUploadPart _AddUploadPart = AddUploadPart{ } instance ToMultipart Tmp AddUploadPart where toMultipart AddUploadPart{..} = MultipartData{..} where inputs = mempty files = [ FileData{..} ] where fdInputName = "data" fdFileName = Text.pack data_ fdFileCType = "application/octet-stream" fdPayload = data_ -- | Request body for @\/v1\/uploads\/:upload_id\/complete@ data CompleteUpload = CompleteUpload { part_ids :: Vector Text , md5 :: Maybe Text } deriving stock (Generic, Show) -- | Default `CompleteUpload` _CompleteUpload :: CompleteUpload _CompleteUpload = CompleteUpload { md5 = Nothing } -- OpenAI says that the `md5` field is optional, but what they really mean is -- that it can be set to the empty string. We still model it as `Maybe Text`, -- but convert `Nothing` to an empty string at encoding time. instance ToJSON CompleteUpload where toJSON completeUpload = genericToJSON aesonOptions (fix completeUpload) where fix CompleteUpload{ md5 = Nothing, .. } = CompleteUpload{ md5 = Just "", .. } fix x = x -- | The status of the Upload data Status = Pending | Completed | Cancelled | Expired deriving stock (Generic, Show) instance FromJSON Status where parseJSON = genericParseJSON aesonOptions -- | The Upload object can accept byte chunks in the form of Parts. data UploadObject file = UploadObject { id :: UploadID , created_at :: POSIXTime , filename :: Text , bytes :: Natural , purpose :: Purpose , status :: Status , expires_at :: POSIXTime , object :: Text , file :: file } deriving stock (Generic, Show) instance FromJSON (UploadObject (Maybe Void)) instance FromJSON (UploadObject FileObject) -- | The upload part represents a chunk of bytes we can add to an upload -- object data PartObject = PartObject { id :: Text , created_at :: POSIXTime , upload_id :: UploadID , object :: Text } deriving stock (Generic, Show) deriving anyclass (FromJSON) -- | Servant API type API = "uploads" :> ( ReqBody '[JSON] CreateUpload :> Post '[JSON] (UploadObject (Maybe Void)) :<|> Capture "upload_id" UploadID :> "parts" :> MultipartForm Tmp AddUploadPart :> Post '[JSON] PartObject :<|> Capture "upload_id" UploadID :> "complete" :> ReqBody '[JSON] CompleteUpload :> Post '[JSON] (UploadObject FileObject) :<|> Capture "upload_id" UploadID :> "cancel" :> Post '[JSON] (UploadObject (Maybe Void)) )