{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -cpp -pgmPcpphs -optP--cpp #-}

module OpenAI.Client
  ( -- * Basics
    ApiKey,
    OpenAIClient,
    makeOpenAIClient,
    ClientError (..),

    -- * Helper types
    TimeStamp (..),
    OpenAIList (..),

    -- * Engine
    EngineId (..),
    Engine (..),
    listEngines,
    getEngine,

    -- * Text completion
    TextCompletionId (..),
    TextCompletionChoice (..),
    TextCompletion (..),
    TextCompletionCreate (..),
    defaultTextCompletionCreate,
    completeText,

    -- * Embeddings
    EmbeddingCreate (..),
    Embedding (..),
    createEmbedding,

    -- * Fine tunes
    FineTuneId (..),
    FineTuneCreate (..),
    defaultFineTuneCreate,
    FineTune (..),
    FineTuneEvent (..),
    createFineTune,
    listFineTunes,
    getFineTune,
    cancelFineTune,
    listFineTuneEvents,

    -- * Searching
    SearchResult (..),
    SearchResultCreate (..),
    searchDocuments,

    -- * File API
    FileCreate (..),
    File (..),
    FileId (..),
    FileHunk (..),
    SearchHunk (..),
    ClassificationHunk (..),
    FineTuneHunk (..),
    FileDeleteConfirmation (..),
    createFile,
    deleteFile,

    -- * Answer API
    getAnswer,
    AnswerReq (..),
    AnswerResp (..),
  )
where

import qualified Data.ByteString.Lazy as BSL
import Data.Proxy
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Network.HTTP.Client (Manager)
import OpenAI.Api
import OpenAI.Client.Internal.Helpers
import OpenAI.Resources
import Servant.API
import Servant.Client
import qualified Servant.Multipart.Client as MP

-- | Your OpenAI API key. Can be obtained from the OpenAI dashboard. Format: @sk-<redacted>@
type ApiKey = T.Text

-- | Holds a 'Manager' and your API key.
data OpenAIClient = OpenAIClient
  { OpenAIClient -> BasicAuthData
scBasicAuthData :: BasicAuthData,
    OpenAIClient -> Manager
scManager :: Manager,
    OpenAIClient -> Int
scMaxRetries :: Int
  }

-- | Construct a 'OpenAIClient'. Note that the passed 'Manager' must support https (e.g. via @http-client-tls@)
makeOpenAIClient ::
  ApiKey ->
  Manager ->
  -- | Number of automatic retries the library should attempt.
  Int ->
  OpenAIClient
makeOpenAIClient :: ApiKey -> Manager -> Int -> OpenAIClient
makeOpenAIClient ApiKey
k = BasicAuthData -> Manager -> Int -> OpenAIClient
OpenAIClient (ByteString -> ByteString -> BasicAuthData
BasicAuthData ByteString
"" (ApiKey -> ByteString
T.encodeUtf8 ApiKey
k))

api :: Proxy OpenAIApi
api :: Proxy OpenAIApi
api = Proxy OpenAIApi
forall k (t :: k). Proxy t
Proxy

openaiBaseUrl :: BaseUrl
openaiBaseUrl :: BaseUrl
openaiBaseUrl = Scheme -> String -> Int -> String -> BaseUrl
BaseUrl Scheme
Https String
"api.openai.com" Int
443 String
""

#define EP0(N, R) \
    N##' :: BasicAuthData -> ClientM R;\
    N :: OpenAIClient -> IO (Either ClientError R);\
    N sc = runRequest (scMaxRetries sc) 0 $ runClientM (N##' (scBasicAuthData sc)) (mkClientEnv (scManager sc) openaiBaseUrl)

#define EP(N, ARG, R) \
    N##' :: BasicAuthData -> ARG -> ClientM R;\
    N :: OpenAIClient -> ARG -> IO (Either ClientError R);\
    N sc a = runRequest (scMaxRetries sc) 0 $ runClientM (N##' (scBasicAuthData sc) a) (mkClientEnv (scManager sc) openaiBaseUrl)

#define EP2(N, ARG, ARG2, R) \
    N##' :: BasicAuthData -> ARG -> ARG2 -> ClientM R;\
    N :: OpenAIClient -> ARG -> ARG2 -> IO (Either ClientError R);\
    N sc a b = runRequest (scMaxRetries sc) 0 $ runClientM (N##' (scBasicAuthData sc) a b) (mkClientEnv (scManager sc) openaiBaseUrl)

EP2 (completeText, EngineId, TextCompletionCreate, TextCompletion)
EP2 (searchDocuments, EngineId, SearchResultCreate, (OpenAIList SearchResult))
EP2 (createEmbedding, EngineId, EmbeddingCreate, (OpenAIList Embedding))

EP (createFineTune, FineTuneCreate, FineTune)
EP0 (listFineTunes, (OpenAIList FineTune))
EP (getFineTune, FineTuneId, FineTune)
EP (cancelFineTune, FineTuneId, FineTune)
EP (listFineTuneEvents, FineTuneId, (OpenAIList FineTuneEvent))

EP0 (listEngines, (OpenAIList Engine))
EP (getEngine, EngineId, Engine)

createFile :: OpenAIClient -> FileCreate -> IO (Either ClientError File)
createFile :: OpenAIClient -> FileCreate -> IO (Either ClientError File)
createFile OpenAIClient
sc FileCreate
rfc =
  do
    ByteString
bnd <- IO ByteString
MP.genBoundary
    OpenAIClient
-> (ByteString, FileCreate) -> IO (Either ClientError File)
createFileInternal OpenAIClient
sc (ByteString
bnd, FileCreate
rfc)

EP (createFileInternal, (BSL.ByteString, FileCreate), File)
EP (deleteFile, FileId, FileDeleteConfirmation)

EP (getAnswer, AnswerReq, AnswerResp)

( BasicAuthData -> ClientM (OpenAIList Engine)
listEngines'
    :<|> BasicAuthData -> EngineId -> ClientM Engine
getEngine'
    :<|> BasicAuthData
-> EngineId -> TextCompletionCreate -> ClientM TextCompletion
completeText'
    :<|> BasicAuthData
-> EngineId
-> SearchResultCreate
-> ClientM (OpenAIList SearchResult)
searchDocuments'
    :<|> BasicAuthData
-> EngineId -> EmbeddingCreate -> ClientM (OpenAIList Embedding)
createEmbedding'
  )
  :<|> (BasicAuthData -> (ByteString, FileCreate) -> ClientM File
createFileInternal' :<|> BasicAuthData -> FileId -> ClientM FileDeleteConfirmation
deleteFile')
  :<|> BasicAuthData -> AnswerReq -> ClientM AnswerResp
getAnswer'
  :<|> ( BasicAuthData -> FineTuneCreate -> ClientM FineTune
createFineTune'
           :<|> BasicAuthData -> ClientM (OpenAIList FineTune)
listFineTunes'
           :<|> BasicAuthData -> FineTuneId -> ClientM FineTune
getFineTune'
           :<|> BasicAuthData -> FineTuneId -> ClientM FineTune
cancelFineTune'
           :<|> BasicAuthData -> FineTuneId -> ClientM (OpenAIList FineTuneEvent)
listFineTuneEvents'
         ) =
    Proxy OpenAIApi -> Client ClientM OpenAIApi
forall api.
HasClient ClientM api =>
Proxy api -> Client ClientM api
client Proxy OpenAIApi
api