packages feed

llm-simple-0.4.3.0: test/LLM/OptionalFields.hs

-- | A structured-output type with every kind of optional field
-- autodocodec offers, for the strict-schema tests (P3 in fixes-043).
module LLM.OptionalFields
  ( Optionals (..),
    Inner (..),
    Node (..),
    innerDefaults,
  )
where

import Autodocodec qualified as AC
import Data.Aeson (FromJSON)
import Data.Text (Text)

data Inner = Inner
  { innerA :: Maybe Text,
    innerB :: Int
  }
  deriving (Eq, Show)

-- | The value of an 'Inner' whose fields were all left out.
innerDefaults :: Inner
innerDefaults = Inner Nothing 3

instance AC.HasCodec Inner where
  codec =
    AC.object "Inner" $
      Inner
        <$> AC.optionalField "a" "optional" AC..= (.innerA)
        <*> AC.optionalFieldWithDefault "b" 3 "with default" AC..= (.innerB)

data Optionals = Optionals
  { reqMaybe :: Maybe Text,
    orNull :: Maybe Text,
    opt :: Maybe Text,
    withDefault :: Int,
    omitted :: Int,
    nested :: Maybe Inner,
    named :: Maybe Inner,
    list :: [Inner],
    union :: Either Inner Int,
    plain :: Text
  }
  deriving (Eq, Show)
  deriving (FromJSON) via (AC.Autodocodec Optionals)

instance AC.HasCodec Optionals where
  codec =
    AC.object "Optionals" $
      Optionals
        <$> AC.requiredField "req_maybe" "required, Maybe" AC..= (.reqMaybe)
        <*> AC.optionalFieldOrNull "or_null" "optionalFieldOrNull" AC..= (.orNull)
        <*> AC.optionalField "opt" "optionalField" AC..= (.opt)
        <*> AC.optionalFieldWithDefault "def" 5 "optionalFieldWithDefault" AC..= (.withDefault)
        <*> AC.optionalFieldWithOmittedDefault "omit" 5 "optionalFieldWithOmittedDefault" AC..= (.omitted)
        <*> AC.optionalField "nested" "nested object" AC..= (.nested)
        <*> AC.optionalFieldWith "named" (AC.named "NamedInner" AC.codec) "named object ($ref)" AC..= (.named)
        <*> AC.requiredField "list" "objects in an array" AC..= (.list)
        <*> AC.requiredField "union" "Either (oneOf)" AC..= (.union)
        <*> AC.requiredField "plain" "required, not nullable" AC..= (.plain)

-- | A recursive type: its schema refers to itself through @$ref@.
data Node = Node
  { label :: Text,
    next :: Maybe Node
  }
  deriving (Eq, Show)
  deriving (FromJSON) via (AC.Autodocodec Node)

instance AC.HasCodec Node where
  codec =
    AC.named "Node" $
      AC.object "Node" $
        Node
          <$> AC.requiredField "label" "label" AC..= (.label)
          <*> AC.optionalField "next" "next node" AC..= (.next)