packages feed

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 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 ()