packages feed

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

{-# LANGUAGE OverloadedStrings #-}

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

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.PromptTemplate.FewShot
import Langchain.PromptTemplate.Prompt

-- QuickCheck helper to generate safe variable names [a-z]+
newtype SafeVar = SafeVar Text
  deriving (Show, Eq)

instance Arbitrary SafeVar where
  arbitrary = SafeVar . T.pack <$> listOf1 (elements ['a' .. 'z'])

-- Safe text without braces
newtype PlainText = PlainText Text
  deriving (Show, Eq)

instance Arbitrary PlainText where
  arbitrary = PlainText . T.pack <$> listOf1 (elements (['a' .. 'z'] ++ ['0' .. '9'] ++ " ,.!-"))

tests :: TestTree
tests =
  testGroup
    "Langchain.Property.PromptTemplateSpec (QuickCheck)"
    [ testProperty "Static templates without braces render unchanged" $
        \(PlainText txt) ->
          renderPrompt (fromTemplate txt) Map.empty === Right txt
    , testProperty "Single variable interpolation replaces {var} with value" $
        \(SafeVar var) (PlainText val) ->
          let tmpl = "Hello {" <> var <> "}!"
              vars = Map.singleton var val
              expected = "Hello " <> val <> "!"
           in renderPrompt (fromTemplate tmpl) vars === Right expected
    , testProperty "Missing variable causes render error" $
        \(SafeVar var) ->
          let tmpl = "Prefix {" <> var <> "} Suffix"
              vars = Map.empty
           in case renderPrompt (fromTemplate tmpl) vars of
                Left _ -> property True
                Right _ -> property False
    , testProperty "Two variable interpolation succeeds when all vars present" $
        \(SafeVar v1) (SafeVar v2) (PlainText val1) (PlainText val2) ->
          v1 /= v2 ==>
            let tmpl = "{" <> v1 <> "} and {" <> v2 <> "}"
                vars = Map.fromList [(v1, val1), (v2, val2)]
                expected = val1 <> " and " <> val2
             in renderPrompt (fromTemplate tmpl) vars === Right expected
    , testProperty "FewShotPromptTemplate renders all examples" $
        \(PlainText prefix) (PlainText suffix) (PlainText ex1) (PlainText ex2) ->
          let examples =
                [ Map.singleton "content" ex1
                , Map.singleton "content" ex2
                ]
              fewShot =
                FewShotPromptTemplate
                  { fsPrefix = prefix
                  , fsExamples = examples
                  , fsExampleTemplate = "Ex: {content}"
                  , fsExampleSeparator = "\n"
                  , fsSuffix = suffix
                  }
           in case renderFewShotPrompt fewShot of
                Right rendered ->
                  property (prefix `T.isInfixOf` rendered && suffix `T.isInfixOf` rendered)
                Left _ -> property False
    ]