packages feed

baikai-openai-0.7.2.0: test/DataInputSpec.hs

module DataInputSpec (tests) where

import Baikai
import Baikai.Prelude
import Baikai.Provider.OpenAI.Cli qualified as Cli
import Baikai.Provider.OpenAI.Internal.Request qualified as R
import Baikai.Provider.OpenAI.Internal.Stream qualified as S
import Baikai.Provider.OpenAI.Responses.Request qualified as RR
import Baikai.Provider.OpenAI.Responses.Stream qualified as RS
import Control.Exception (try)
import Data.Aeson qualified as A
import Data.ByteString.Lazy.Char8 qualified as BL
import Data.IORef
import Data.Text qualified as T
import Data.Vector qualified as V
import Streamly.Data.Stream qualified as Stream
import Test.Tasty
import Test.Tasty.HUnit

modelFixture :: Model
modelFixture = emptyModel & #modelId .~ "fixture" & #api .~ OpenAIChatCompletions

dataFixture :: A.Value
dataFixture = A.object ["z" A..= [1 :: Int, 2], "a" A..= ("日本語" :: T.Text)]

history :: Context
history = contextOf [AssistantMessage ((emptyResponse ^. #message) & #content .~ V.singleton (AssistantData (DataContent A.Null Nothing Nothing Nothing)))]

tests :: TestTree
tests =
  testGroup
    "structured input and history"
    [ testCase "Responses sends structured input canonically and refuses assistant data before driver" $ do
        req <- either (assertFailure . T.unpack) pure (RR.mapRequest (modelFixture & #api .~ OpenAIResponses) (contextOf [userData dataFixture]) emptyOptions)
        let bytes = BL.unpack (A.encode (req ^. #requestBody))
        assertBool "canonical data reaches Responses wire" (T.pack (BL.unpack (A.encode (canonicalDataText dataFixture))) `T.isInfixOf` T.pack bytes)
        calls <- newIORef (0 :: Int)
        es <- Stream.toList (RS.openaiResponsesStreamWith (\_ _ _ _ _ -> modifyIORef' calls (+ 1)) (modelFixture & #api .~ OpenAIResponses) history emptyOptions)
        readIORef calls >>= (@?= 0)
        case [e | EventError p <- es, Just e <- [p ^. #errorInfo]] of
          [e] -> e ^. #requestProblem @?= Just (UnsupportedFeature "messages[0].content[0]")
          _ -> assertFailure (show es),
      testCase "actual wire request contains canonical JSON in block position" $ do
        let ctx = contextOf [UserMessage (UserPayload (V.fromList [UserText (TextContent "before"), UserData dataFixture, UserText (TextContent "after")]) Nothing)]
        req <- either (assertFailure . T.unpack) pure (R.mapRequest modelFixture ctx emptyOptions)
        let wire = A.toJSON req
            bytes = BL.unpack (A.encode wire)
            jsonText = BL.unpack (A.encode (canonicalDataText dataFixture))
        assertBool ("structured text missing: " <> bytes) (T.pack jsonText `T.isInfixOf` T.pack bytes),
      testCase "OpenAIChatCompletions refuses assistant data with zero driver calls" $ do
        calls <- newIORef (0 :: Int)
        let driver _ _ _ _ _ = modifyIORef' calls (+ 1)
        es <- Stream.toList (S.openaiChatStreamWith driver modelFixture history emptyOptions)
        readIORef calls >>= (@?= 0)
        case [e | EventError p <- es, Just e <- [p ^. #errorInfo]] of
          [e] -> e ^. #requestProblem @?= Just (UnsupportedFeature "messages[0].content[0]")
          _ -> assertFailure (show es),
      testCase "CLI request carries JSON and refuses data history before spawning" $ do
        let cfg = Cli.defaultCodexCliConfig & #executable .~ "/nonexistent-baikai-data-test"
            (_, args) = Cli.codexCliCommand cfg modelFixture (contextOf [userData dataFixture]) emptyOptions
        assertBool "canonical JSON reaches argument vector" (any (T.isInfixOf (canonicalDataText dataFixture) . T.pack) args)
        result <- try ((Cli.codexCliProvider cfg ^. #complete) modelFixture history emptyOptions) :: IO (Either BaikaiError Response)
        case result of
          Left e -> e ^. #requestProblem @?= Just (UnsupportedFeature "messages[0].content[0]")
          Right r -> assertFailure (show r)
    ]