packages feed

langchain-hs-0.0.3.0: test/Test/Langchain/PromptTemplate.hs

{-# LANGUAGE OverloadedStrings #-}

module Test.Langchain.PromptTemplate (tests) where

import qualified Data.Map.Strict as HM
import qualified Data.Text as T
import Langchain.PromptTemplate
import Langchain.Runnable.Core (invoke)
import Test.Tasty
import Test.Tasty.HUnit

tests :: TestTree
tests =
  testGroup
    "PromptTemplate Tests"
    [ testGroup
        "PromptTemplate"
        [ testCase "correctly interpolates all variables" $
            renderPrompt template vars @?= Right "Hello, Alice! Welcome to Wonderland."
        , testCase "handles templates with no variables" $
            let noVarTemplate = PromptTemplate "Hello, world!"
             in renderPrompt noVarTemplate HM.empty @?= Right "Hello, world!"
        , testCase "handles templates with repeated variables" $
            let repeatTemplate = PromptTemplate "{name} likes {food}. {name} eats {food} every day."
                repeatVars = HM.fromList [("name", "Bob"), ("food", "pizza")]
             in renderPrompt repeatTemplate repeatVars @?= Right "Bob likes pizza. Bob eats pizza every day."
        , testCase "returns an error for missing variables" $
            let missingVars = HM.fromList [("name", "Charlie")]
             in case renderPrompt template missingVars of
                  Left err -> "place" `T.isInfixOf` T.pack (show err) @? "Expected error to contain 'place'"
                  Right _ -> assertFailure "Expected an error for missing variable"
                  {- TODO: Need to take care of incomplete brace cases
                  , testCase "handles unclosed braces" $
                      let invalidTemplate = PromptTemplate "Hello, {name! Welcome to {place}."
                       in case renderPrompt invalidTemplate vars of
                            Left err -> err @?= "Unclosed brace"
                            Right _ -> assertFailure "Expected an error for unclosed brace"
                  , testCase "handles complex nesting of placeholders" $
                      let complexTemplate = PromptTemplate "{{name}} is not a placeholder but {name} is."
                       in renderPrompt complexTemplate vars @?= Right "{Alice} is not a placeholder but Alice is."
                       -}
        ]
    , testCase "Runnable instance for PromptTemplate - invoke with variables" $ do
        let template1 = PromptTemplate "Hello, {name}!"
            vars1 = HM.fromList [("name", "Dave")]
        result <- invoke template1 vars1
        result @?= Right "Hello, Dave!"
    , testGroup
        "FewShotPromptTemplate"
        [ testCase "correctly formats a few-shot prompt" $
            let expected =
                  "Examples of {type}:\nInput: Hello\nOutput: Bonjour\n\nInput: Goodbye\nOutput: Au revoir\nNow translate: {query}"
             in renderFewShotPrompt fewShotTemplate @?= Right expected
        , testCase "handles empty examples list" $
            let emptyExamples = fewShotTemplate {fsExamples = []}
             in renderFewShotPrompt emptyExamples @?= Right "Examples of {type}:\n\nNow translate: {query}"
        , testCase "handles empty prefix and suffix" $
            let noPreSuf = fewShotTemplate {fsPrefix = "", fsSuffix = ""}
             in renderFewShotPrompt noPreSuf
                  @?= Right "Input: Hello\nOutput: Bonjour\n\nInput: Goodbye\nOutput: Au revoir"
        , testCase "returns an error when example variables are missing" $
            let badExamples =
                  fewShotTemplate
                    { fsExamples = [HM.fromList [("wrong", "value")]]
                    , fsExampleTemplate = "{input} translates to {output}"
                    }
             in case renderFewShotPrompt badExamples of
                  Left err ->
                    "input" `T.isInfixOf` T.pack (show err)
                      @? "Expected error to contain 'input'"
                  Right _ ->
                    assertFailure
                      "Expected an error for missing example variable"
        , testCase "correctly uses the example separator" $
            let customSep = fewShotTemplate {fsExampleSeparator = " ### "}
             in renderFewShotPrompt customSep
                  @?= Right
                    "Examples of {type}:\nInput: Hello\nOutput: Bonjour ### Input: Goodbye\nOutput: Au revoir\nNow translate: {query}"
        ]
    ]
  where
    template = PromptTemplate "Hello, {name}! Welcome to {place}."
    vars = HM.fromList [("name", "Alice"), ("place", "Wonderland")]
    fewShotTemplate =
      FewShotPromptTemplate
        { fsPrefix = "Examples of {type}:\n"
        , fsExamples =
            [ HM.fromList [("input", "Hello"), ("output", "Bonjour")]
            , HM.fromList [("input", "Goodbye"), ("output", "Au revoir")]
            ]
        , fsExampleTemplate = "Input: {input}\nOutput: {output}"
        , fsExampleSeparator = "\n\n"
        , fsSuffix = "\nNow translate: {query}"
        }