openai-hs 0.1.1.0 → 0.2.0.0
raw patch · 3 files changed
+98/−13 lines, 3 filesdep +servant-multipartPVP ok
version bump matches the API change (PVP)
Dependencies added: servant-multipart
API changes (from Hackage documentation)
+ OpenAI.Client: AnswerReq :: Maybe FileId -> Maybe (Vector Text) -> Text -> EngineId -> EngineId -> Text -> [[Text]] -> Bool -> AnswerReq
+ OpenAI.Client: AnswerResp :: [Text] -> AnswerResp
+ OpenAI.Client: File :: FileId -> TimeStamp -> Text -> File
+ OpenAI.Client: FileCreate :: Text -> [FileHunk] -> FileCreate
+ OpenAI.Client: FileDeleteConfirmation :: FileId -> FileDeleteConfirmation
+ OpenAI.Client: FileHunk :: Text -> Maybe Text -> FileHunk
+ OpenAI.Client: FileId :: Text -> FileId
+ OpenAI.Client: [arDocuments] :: AnswerReq -> Maybe (Vector Text)
+ OpenAI.Client: [arExamplesContext] :: AnswerReq -> Text
+ OpenAI.Client: [arExamples] :: AnswerReq -> [[Text]]
+ OpenAI.Client: [arFile] :: AnswerReq -> Maybe FileId
+ OpenAI.Client: [arModel] :: AnswerReq -> EngineId
+ OpenAI.Client: [arQuestion] :: AnswerReq -> Text
+ OpenAI.Client: [arReturnMetadata] :: AnswerReq -> Bool
+ OpenAI.Client: [arSearchModel] :: AnswerReq -> EngineId
+ OpenAI.Client: [arsAnswers] :: AnswerResp -> [Text]
+ OpenAI.Client: [fCreatedAt] :: File -> TimeStamp
+ OpenAI.Client: [fId] :: File -> FileId
+ OpenAI.Client: [fPurpose] :: File -> Text
+ OpenAI.Client: [fcDocuments] :: FileCreate -> [FileHunk]
+ OpenAI.Client: [fcPurpose] :: FileCreate -> Text
+ OpenAI.Client: [fdcId] :: FileDeleteConfirmation -> FileId
+ OpenAI.Client: [fhContent] :: FileHunk -> Text
+ OpenAI.Client: [fhMetadata] :: FileHunk -> Maybe Text
+ OpenAI.Client: [sccrFile] :: SearchResultCreate -> Maybe FileId
+ OpenAI.Client: [sccrReturnMetadata] :: SearchResultCreate -> Bool
+ OpenAI.Client: [srMetadata] :: SearchResult -> Maybe Text
+ OpenAI.Client: [unFileId] :: FileId -> Text
+ OpenAI.Client: createFile :: OpenAIClient -> FileCreate -> IO (Either ClientError File)
+ OpenAI.Client: data AnswerReq
+ OpenAI.Client: data AnswerResp
+ OpenAI.Client: data File
+ OpenAI.Client: data FileCreate
+ OpenAI.Client: data FileDeleteConfirmation
+ OpenAI.Client: data FileHunk
+ OpenAI.Client: deleteFile :: OpenAIClient -> FileId -> IO (Either ClientError FileDeleteConfirmation)
+ OpenAI.Client: getAnswer :: OpenAIClient -> AnswerReq -> IO (Either ClientError AnswerResp)
+ OpenAI.Client: newtype FileId
- OpenAI.Client: SearchResult :: Int -> Double -> SearchResult
+ OpenAI.Client: SearchResult :: Int -> Double -> Maybe Text -> SearchResult
- OpenAI.Client: SearchResultCreate :: Vector Text -> Text -> SearchResultCreate
+ OpenAI.Client: SearchResultCreate :: Maybe FileId -> Maybe (Vector Text) -> Text -> Bool -> SearchResultCreate
- OpenAI.Client: [sccrDocuments] :: SearchResultCreate -> Vector Text
+ OpenAI.Client: [sccrDocuments] :: SearchResultCreate -> Maybe (Vector Text)
Files
- openai-hs.cabal +5/−2
- src/OpenAI/Client.hs +22/−2
- test/ApiSpec.hs +71/−9
openai-hs.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: bc145a17f925189f4d8d879579d0e37dc7d796a46ca6bb0303538c1f3f2c1f16+-- hash: 8f7d9bff4fa5f43dad386049b3579aaa96e640a7ad21fddc4cd1338daa0c972b name: openai-hs-version: 0.1.1.0+version: 0.2.0.0 synopsis: Unofficial OpenAI client description: Unofficial OpenAI client category: Web@@ -39,6 +39,7 @@ build-depends: aeson , base >=4.7 && <5+ , bytestring , casing , cpphs , http-client@@ -46,6 +47,7 @@ , openai-servant , servant , servant-client+ , servant-multipart , text default-language: Haskell2010 @@ -76,6 +78,7 @@ , servant , servant-client , servant-client-core+ , servant-multipart , text , vector default-language: Haskell2010
src/OpenAI/Client.hs view
@@ -16,6 +16,12 @@ -- * Searching , SearchResult(..), SearchResultCreate(..) , searchDocuments+ -- * File API+ , FileCreate(..), File(..), FileId(..), FileHunk(..)+ , FileDeleteConfirmation(..)+ , createFile, deleteFile+ -- * Answer API+ , getAnswer, AnswerReq(..), AnswerResp(..) ) where @@ -27,8 +33,10 @@ import Network.HTTP.Client (Manager) import Servant.API import Servant.Client+import qualified Data.ByteString.Lazy as BSL import qualified Data.Text as T import qualified Data.Text.Encoding as T+import qualified Servant.Multipart as MP -- | Your OpenAI API key. Can be obtained from the OpenAI dashboard. Format: @sk-<redacted>@ type ApiKey = T.Text@@ -77,8 +85,20 @@ EP0(listEngines, (OpenAIList Engine)) EP(getEngine, EngineId, Engine) -listEngines'+createFile :: OpenAIClient -> FileCreate -> IO (Either ClientError File)+createFile sc rfc =+ do bnd <- MP.genBoundary+ createFileInternal sc (bnd, rfc)++EP(createFileInternal, (BSL.ByteString, FileCreate), File)+EP(deleteFile, FileId, FileDeleteConfirmation)++EP(getAnswer, AnswerReq, AnswerResp)++(listEngines' :<|> getEngine' :<|> completeText'- :<|> searchDocuments'+ :<|> searchDocuments')+ :<|> (createFileInternal' :<|> deleteFile')+ :<|> getAnswer' = client api
test/ApiSpec.hs view
@@ -29,17 +29,52 @@ apiTests :: SpecWith () apiTests = beforeAll makeClient $- do describe "engines" $+ do describe "file api" $+ do it "allows creating one" $ \cli ->+ do let file =+ FileCreate+ { fcPurpose = "search"+ , fcDocuments = [FileHunk "Test 1" Nothing, FileHunk "text 2" (Just "foo")]+ }+ res <- forceSuccess $ createFile cli file+ _ <- forceSuccess $ deleteFile cli (fId res)+ pure ()+ describe "answer api" $+ do it "works" $ \cli ->+ do let file =+ FileCreate+ { fcPurpose = "search"+ , fcDocuments =+ [ FileHunk "Cities in California: San Francisco, Los Angeles" (Just "cali")+ , FileHunk "Tasty fruit: Apple, Orange" (Just "fruit")+ , FileHunk "Cities in Germany: Freiburg, Berlin" (Just "germany")+ ]+ }+ res <- forceSuccess $ createFile cli file+ let searchReq =+ AnswerReq+ { arFile = Just (fId res)+ , arDocuments = Nothing+ , arQuestion = "Where is San Francisco?"+ , arSearchModel = EngineId "babbage"+ , arModel = EngineId "davinci"+ , arExamplesContext = "Good programming languages: Haskell, PureScript"+ , arExamples = [["Is PHP a good programming language?", "No, sorry."]]+ , arReturnMetadata = True+ }+ answerRes <- forceSuccess $ getAnswer cli searchReq+ T.unpack (head (arsAnswers answerRes)) `shouldContain` ("California" :: String)+ _ <- forceSuccess $ deleteFile cli (fId res)+ pure ()+ describe "engines" $ do it "lists engines" $ \cli -> do res <- forceSuccess $ listEngines cli V.null (olData res) `shouldBe` False- -- TODO: This doesn't work for some reason, even the cURL example from the docs fail.- --it "retrieve engine" $ \cli ->- -- do engineList <- forceSuccess $ listEngines cli- -- print engineList- -- let firstEngine = V.head (olData engineList)- -- engine <- forceSuccess $ getEngine cli (eId firstEngine)- -- engine `shouldBe` firstEngine+ it "retrieve engine" $ \cli ->+ do engineList <- forceSuccess $ listEngines cli+ let firstEngine = V.head (olData engineList)+ engine <- forceSuccess $ getEngine cli (eId firstEngine)+ engine `shouldBe` firstEngine describe "text completion" $ do it "works (smoke test)" $ \cli -> do firstEngine <- V.head . olData <$> forceSuccess (listEngines cli)@@ -57,7 +92,34 @@ forceSuccess $ searchDocuments cli (eId firstEngine) $ SearchResultCreate- { sccrDocuments = V.fromList ["pool", "gym", "night club"]+ { sccrDocuments = Just $ V.fromList ["pool", "gym", "night club"]+ , sccrFile = Nothing , sccrQuery = "swimmer"+ , sccrReturnMetadata = False } V.length (olData searchResults) `shouldBe` 3+ describe "file based document search" $+ do it "works" $ \cli ->+ do let file =+ FileCreate+ { fcPurpose = "search"+ , fcDocuments =+ [ FileHunk "pool" (Just "pool")+ , FileHunk "gym" (Just "gym")+ , FileHunk "night club" (Just "nc")+ ]+ }+ createRes <- forceSuccess $ createFile cli file+ let searchReq =+ SearchResultCreate+ { sccrFile = Just (fId createRes)+ , sccrDocuments = Nothing+ , sccrQuery = "pool"+ , sccrReturnMetadata = True+ }+ searchRes <- forceSuccess $ searchDocuments cli (EngineId "ada") searchReq+ let res = V.head (olData searchRes)+ srDocument res `shouldBe` 0 -- pool+ srMetadata res `shouldBe` Just "pool"+ _ <- forceSuccess $ deleteFile cli (fId createRes)+ pure ()