packages feed

langchain-hs-0.0.5.0: test/Test/Langchain/Property/MessageSpec.hs

{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module Test.Langchain.Property.MessageSpec (tests) where

import Data.Aeson (decode, encode, toJSON)
import qualified Data.ByteString as BS
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Map.Strict as Map
import Data.Text (Text)
import qualified Data.Text as T
import Test.QuickCheck
import Test.Tasty
import Test.Tasty.QuickCheck

import Langchain.Core.Model

-- Arbitrary instances for Core Message types

instance Arbitrary Role where
  arbitrary = elements [System, User, Assistant, Tool, Developer, Function]

instance Arbitrary ContentBlock where
  arbitrary =
    oneof
      [ TextBlock . T.pack
          <$> listOf1 (elements (['a' .. 'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9'] ++ " \t\n.,!?-"))
      , ImageBlock
          <$> ( ImageContent
                  <$> ( ImageBase64 . Just
                          <$> elements ["image/png", "image/jpeg", "image/webp"]
                          <*> (T.pack <$> listOf1 (elements ['a' .. 'z']))
                      )
                  <*> pure Nothing
                  <*> pure Nothing
              )
      , AudioBlock <$> elements ["audio/mp3", "audio/wav"] <*> (T.pack <$> listOf1 (elements ['a' .. 'z']))
      , DataBlock . BS.pack <$> listOf1 arbitrary
      ]

instance Arbitrary ToolCall where
  arbitrary = do
    tcId <- T.pack <$> listOf1 (elements ['a' .. 'z'])
    name <- T.pack <$> listOf1 (elements ['a' .. 'z'])
    pure $ ToolCall tcId "function" name (toJSON ("{}" :: Text))

instance Arbitrary Message where
  arbitrary = do
    r <- arbitrary
    blocks <- listOf1 arbitrary
    let neBlocks = NonEmpty.fromList blocks
    mbName <- oneof [pure Nothing, Just . T.pack <$> listOf1 (elements ['a' .. 'z'])]
    mbToolId <- oneof [pure Nothing, Just . T.pack <$> listOf1 (elements ['a' .. 'z'])]
    pure $
      Message r neBlocks mbName Nothing mbToolId (Map.singleton "example" $ toJSON ("value" :: Text))

tests :: TestTree
tests =
  testGroup
    "Langchain.Property.MessageSpec (QuickCheck)"
    [ testProperty "Role JSON round-trip: decode (encode r) == Just r" $
        \r -> decode (encode (r :: Role)) === Just r
    , testProperty "ContentBlock JSON round-trip: decode (encode cb) == Just cb" $
        \cb -> decode (encode (cb :: ContentBlock)) === Just cb
    , testProperty "Message JSON round-trip: decode (encode msg) == Just msg" $
        \msg -> decode (encode (msg :: Message)) === Just msg
    , testProperty "extractMessageText preserves text block contents" $
        \t ->
          let txt = T.pack t
              msg = userMessage txt
           in extractMessageText msg === txt
    ]