packages feed

genai-lib-2.0: src/lib/GenAILib/JSON.hs

module GenAILib.JSON
  ( customJSONOptions
  , flattenOptions
  , jsonToText
  )
  where

import Data.Aeson (Options, Value (Object), defaultOptions, encode,
  fieldLabelModifier, omitNothingFields)
import Data.Aeson.Key (fromText)
import Data.Aeson.KeyMap ((!?), delete, union)
import Data.String.Conv (toS)
import Data.Text.Lazy qualified as TL


stripInitialUnderscore :: String -> String
stripInitialUnderscore ('_' : fieldName) = fieldName
stripInitialUnderscore fieldName = fieldName


customJSONOptions :: Options
customJSONOptions = defaultOptions
  { fieldLabelModifier = stripInitialUnderscore
  , omitNothingFields = True
  }


-- | Most LLM back-ends expect the options like temperature to be at the
--   top-level of the object. This function will move the options Object up and
--   merge it with the other keys, removing the options Object as well.
--
--   Used internally by this library to construct LLM request bodies
--
-- This
--
-- > { "foo" : 1
-- > , "bar" : 5
-- > , "options" : { "bar" : 2, "baz" : 3 }
-- > }
--
-- Becomes this
--
-- > { "foo" : 1
-- > , "bar" : 2
-- > , "baz" : 3
-- > }
flattenOptions :: Value -> Value

flattenOptions (Object origKm) = Object newKm
  where
    optsKey = fromText "options"
    newKm = case origKm !? optsKey of
      (Just (Object optsKm)) -> delete optsKey origKm `union` optsKm
      _ -> origKm

flattenOptions nonObjValue = nonObjValue


-- | Convenience function to textify a JSON @Value@
jsonToText :: Value -> TL.Text
jsonToText = toS . encode