packages feed

langchain-hs-0.0.2.0: test/Test/Langchain/LLM/Core.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}

module Test.Langchain.LLM.Core (tests) where

import Test.Tasty
import Test.Tasty.HUnit

import Data.Aeson (Result (..), decode, encode, fromJSON, toJSON)
import Data.List.NonEmpty (NonEmpty (..))
import Data.Text (Text)
import Langchain.LLM.Core
import Data.Maybe (fromMaybe)

data TestLLM = TestLLM
  { responseText :: Text
  , shouldSucceed :: Bool
  }

instance LLM TestLLM where
  type LLMParams TestLLM = Text
 
  generate m _ mbParams =
    pure $
      if shouldSucceed m
        then Right (fromMaybe (responseText m) mbParams)
        else Left "Test error"

  chat m _ _ =
    pure $
      if shouldSucceed m
        then Right (responseText m)
        else Left "Test error"

  stream m _ handler _ = do
    if shouldSucceed m
      then do
        onToken handler (responseText m)
        onComplete handler
        pure (Right ())
      else pure (Left "Test error")

tests :: TestTree
tests =
  testGroup
    "LLMCoreTest"
    [ 
    testGroup
        "Role"
        [ testCase "has correct equality" $ do
            assertEqual "System equals System" System System
            assertEqual "User equals User" User User
            assertEqual "Assistant equals Assistant" Assistant Assistant
            assertEqual "Tool equals Tool" Tool Tool
            assertBool "System should not equal User" (System /= User)
        , testCase "can be converted to and from JSON" $ do
            case fromJSON (toJSON System) of
              Success r -> assertEqual "JSON roundtrip for System" System r
              _ -> assertFailure "JSON conversion failed for System"
            case fromJSON (toJSON User) of
              Success r -> assertEqual "JSON roundtrip for User" User r
              _ -> assertFailure "JSON conversion failed for User"
            case fromJSON (toJSON Assistant) of
              Success r -> assertEqual "JSON roundtrip for Assistant" Assistant r
              _ -> assertFailure "JSON conversion failed for Assistant"
            case fromJSON (toJSON Tool) of
              Success r -> assertEqual "JSON roundtrip for Tool" Tool r
              _ -> assertFailure "JSON conversion failed for Tool"
        ]
    , testGroup
        "Message"
        [ testCase "creates messages with correct fields" $ do
            let msg = Message User "Hello" defaultMessageData
            assertEqual "role should be User" User (role msg)
            assertEqual "content should be 'Hello'" "Hello" (content msg)
            assertEqual "messageData should be default" defaultMessageData (messageData msg)
        , testCase "creates messages with custom message data" $ do
            let customData = defaultMessageData {name = Just "Alice"}
            let msg = Message User "Hello" customData
            assertEqual "role should be User" User (role msg)
            assertEqual "content should be 'Hello'" "Hello" (content msg)
            assertEqual "name should be Just 'Alice'" (Just "Alice") (name (messageData msg))
            assertEqual "toolCalls should be Nothing" Nothing (toolCalls (messageData msg))
        ]
    , testGroup
        "MessageData"
        [ testCase "creates default message data with all Nothing fields" $ do
            let md = defaultMessageData
            assertEqual "name should be Nothing" Nothing (name md)
            assertEqual "toolCalls should be Nothing" Nothing (toolCalls md)
        , testCase "serializes to correct JSON structure" $ do
            let md = MessageData (Just "Alice") (Just ["tool1", "tool2"])
                expected = "{\"name\":\"Alice\",\"tool_calls\":[\"tool1\",\"tool2\"]}"
            assertEqual "JSON encoding of MessageData" expected (encode md)
        , testCase "deserializes from JSON correctly" $ do
            let json = "{\"name\":\"Bob\",\"tool_calls\":[\"tool3\"]}"
                expected = MessageData (Just "Bob") (Just ["tool3"])
            assertEqual "JSON decoding of MessageData" (Just expected) (decode json)
        , testCase "handles partial JSON correctly" $ do
            let json = "{\"name\":\"Charlie\"}"
                expected = MessageData (Just "Charlie") Nothing
            assertEqual "Partial JSON decoding of MessageData" (Just expected) (decode json)
        ]
    , testGroup
        "LLM Typeclass"
        [ testGroup
            "generate"
            [ 
              testCase "generate uses provided LLMParams" $ do
            let testLLM = TestLLM { responseText = "Default", shouldSucceed = True }
            result <- generate testLLM "Prompt" (Just "CustomParam")
            assertEqual "Should return CustomParam" (Right "CustomParam") result

             , testCase "returns Right with response for successful generation" $ do
                let successLLM = TestLLM "Success response" True
                result <- generate successLLM "Test prompt" Nothing
                assertEqual "Successful generation" (Right "Success response") result
            , testCase "returns Left with error for failed generation" $ do
                let failureLLM = TestLLM "Failure response" False
                result <- generate failureLLM "Test prompt" Nothing
                assertEqual "Failed generation" (Left "Test error") result
            ]
        , testGroup
            "chat"
            [ testCase "returns Right with response for successful chat" $ do
                let successLLM = TestLLM "Success response" True
                    singleMsg = Message User "Test prompt" defaultMessageData
                    chatMsgs = singleMsg :| []
                result <- chat successLLM chatMsgs Nothing
                assertEqual "Successful chat" (Right "Success response") result
            , testCase "returns Left with error for failed chat" $ do
                let failureLLM = TestLLM "Failure response" False
                    singleMsg = Message User "Test prompt" defaultMessageData
                    chatMsgs = singleMsg :| []
                result <- chat failureLLM chatMsgs Nothing
                assertEqual "Failed chat" (Left "Test error") result
            ]
        , testGroup
            "stream"
            [ testCase "calls handlers and returns Right for successful stream" $ do
                let successLLM = TestLLM "Success response" True
                    singleMsg = Message User "Test prompt" defaultMessageData
                    chatMsgs = singleMsg :| []
                    handler =
                      StreamHandler
                        { onToken = \_ -> pure ()
                        , onComplete = pure ()
                        }
                result <- stream successLLM chatMsgs handler Nothing
                assertEqual "Successful stream" (Right ()) result
            , testCase "returns Left with error for failed stream" $ do
                let failureLLM = TestLLM "Failure response" False
                    singleMsg = Message User "Test prompt" defaultMessageData
                    chatMsgs = singleMsg :| []
                    handler =
                      StreamHandler
                        { onToken = \_ -> pure ()
                        , onComplete = pure ()
                        }
                result <- stream failureLLM chatMsgs handler Nothing
                assertEqual "Failed stream" (Left "Test error") result
            ]
        ]
    , testGroup
        "ChatMessage"
        [ testCase "creates non-empty list of messages" $ do
            let msg1 = Message User "Hello" defaultMessageData
                msg2 = Message Assistant "Hi there" defaultMessageData
                chat_ = msg1 :| [msg2]
            assertEqual "ChatMessage length" 2 (length chat_)
        ]
    ]