llama-cpp-hs-0.1.0.0: src/Llama/Model.hs
{- |
Module : Llama.Model
Description : High level Model interface for llama-cpp
Copyright : (c) 2025 Tushar Adhatrao
License : MIT
Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>
-}
module Llama.Model (
defaultModelParams
, loadModelFromFile
, initContextFromModel
, getModelVocab
, getContextModel
, getVocabType
, getModelRoPEFreqScale
, getModelNumKVHeads
, getModelNumHeads
, getModelNumLayers
, getModelEmbeddingDim
, getModelTrainingContextSize
, getModelSize
, getModelChatTemplate
, getModelHasEncoder
, getModelNumParams
, getModelHasDecoder
, getModelDecoderStartToken
, getModelIsRecurrent
, quantizeModel
, quantizeModelDefault
, defaultQuantizeParams
, getModelMetaCount
, getModelMetaValue
, getModelMetaKeyByIndex
, getModelMetaValueByIndex
, getModelDescription
, loadModelFromSplits
, getModelRopeType
) where
import Data.Functor
import Foreign
import Foreign.C.String
import Llama.Internal.Foreign
import Llama.Internal.Types
import Llama.Internal.Types.Params
-- | Default model parameters
defaultModelParams :: IO LlamaModelParams
defaultModelParams = do
-- Convert to a pointer to pass to the C function
alloca $ \paramsPtr -> do
c_llama_model_default_params (CLlamaModelParams paramsPtr)
peek paramsPtr
-- | Load a model from a file using the specified parameters
loadModelFromFile :: FilePath -> LlamaModelParams -> IO (Either String Model)
loadModelFromFile path params = do
withCString path $ \cPath -> do
withStorable params $ \paramsPtr -> do
model <- c_llama_model_load_from_file_wrap cPath (CLlamaModelParams paramsPtr)
if model == CLlamaModel nullPtr
then return $ Left "Failed to load model"
else do
let (CLlamaModel modelPtr) = model
fp <- newForeignPtr p_llama_model_free modelPtr
return $ Right $ Model fp
-- | Create a context from a model using the specified parameters
initContextFromModel :: Model -> LlamaContextParams -> IO (Either String Context)
initContextFromModel (Model modelFPtr) params = do
withForeignPtr modelFPtr $ \modelPtr -> do
withStorable params $ \paramsPtr -> do
context <-
c_llama_init_from_model_wrap
(CLlamaModel modelPtr)
(CLlamaContextParams paramsPtr)
if context == CLlamaContext nullPtr
then return $ Left "Failed to initialize context"
else do
let (CLlamaContext contextPtr) = context
fp <- newForeignPtr p_llama_free contextPtr
return $ Right $ Context fp
-- | Get the vocabulary from a model
getModelVocab :: Model -> IO (Either String Vocab)
getModelVocab (Model modelFPtr) = do
withForeignPtr modelFPtr $ \modelPtr -> do
vocab <- c_llama_model_get_vocab (CLlamaModel modelPtr)
if vocab == CLlamaVocab nullPtr
then return $ Left "Failed to get vocabulary"
else do
-- For now, assuming it's owned by the model and doesn't need separate freeing
let (CLlamaVocab vocabPtr) = vocab
-- Using a dummy finalizer since vocab is owned by the model
fp <- newForeignPtr_ vocabPtr
return $ Right $ Vocab fp
-- | Convert Storable Haskell struct to pointer and run an action
withStorable :: Storable a => a -> (Ptr a -> IO b) -> IO b
withStorable x f = alloca $ \ptr -> do
poke ptr x
f ptr
-- | Get the model associated with a context.
getContextModel :: Context -> IO Model
getContextModel (Context ctxFPtr) =
withForeignPtr ctxFPtr $ \ctxPtr -> do
CLlamaModel modelPtr <- c_llama_get_model (CLlamaContext ctxPtr)
fp <- newForeignPtr p_llama_model_free modelPtr
return $ Model fp
-- | Get the vocabulary type.
getVocabType :: Vocab -> IO (Maybe LlamaVocabType)
getVocabType (Vocab vocabFPtr) =
withForeignPtr vocabFPtr $ \vocabPtr ->
fromLlamaRopeVocabType <$> c_llama_vocab_type (CLlamaVocab vocabPtr)
-- | Get RoPE frequency scaling factor used during training.
getModelRoPEFreqScale :: Model -> IO Float
getModelRoPEFreqScale (Model modelFPtr) =
withForeignPtr modelFPtr $ \modelPtr ->
realToFrac <$> c_llama_model_rope_freq_scale_train (CLlamaModel modelPtr)
-- | Get the number of key/value heads in the model.
getModelNumKVHeads :: Model -> IO Int
getModelNumKVHeads (Model modelFPtr) =
withForeignPtr modelFPtr $ \modelPtr ->
fromCInt <$> c_llama_model_n_head_kv (CLlamaModel modelPtr)
-- | Get the number of attention heads in the model.
getModelNumHeads :: Model -> IO Int
getModelNumHeads (Model modelFPtr) =
withForeignPtr modelFPtr $ \modelPtr ->
fromCInt <$> c_llama_model_n_head (CLlamaModel modelPtr)
-- | Get the number of transformer layers in the model.
getModelNumLayers :: Model -> IO Int
getModelNumLayers (Model modelFPtr) =
withForeignPtr modelFPtr $ \modelPtr ->
fromCInt <$> c_llama_model_n_layer (CLlamaModel modelPtr)
-- | Get the embedding dimension of the model.
getModelEmbeddingDim :: Model -> IO Int
getModelEmbeddingDim (Model modelFPtr) =
withForeignPtr modelFPtr $ \modelPtr ->
fromCInt <$> c_llama_model_n_embd (CLlamaModel modelPtr)
-- | Get the training context size of the model.
getModelTrainingContextSize :: Model -> IO Int
getModelTrainingContextSize (Model modelFPtr) =
withForeignPtr modelFPtr $ \modelPtr ->
fromCInt <$> c_llama_model_n_ctx_train (CLlamaModel modelPtr)
fromCInt :: Int32 -> Int
fromCInt = fromIntegral
-- | Get the size of the model in bytes
getModelSize :: Model -> IO Int64
getModelSize (Model modelFPtr) =
withForeignPtr modelFPtr $ \modelPtr ->
fromIntegral <$> c_llama_model_size (CLlamaModel modelPtr)
-- | Check if the model has an encoder
getModelHasEncoder :: Model -> IO Bool
getModelHasEncoder (Model modelFPtr) = do
withForeignPtr modelFPtr $ \modelPtr -> do
c_llama_model_has_encoder (CLlamaModel modelPtr) <&> (/= 0)
-- | Get the chat template from a model, optionally by name
getModelChatTemplate :: Model -> Maybe String -> IO (Either String String)
getModelChatTemplate (Model modelFPtr) mName = do
withForeignPtr modelFPtr $ \modelPtr -> do
cName <- maybe (pure nullPtr) newCString mName
template <- c_llama_model_chat_template (CLlamaModel modelPtr) cName
if template == nullPtr
then return $ Left "Failed to get chat template"
else Right <$> peekCString template
-- | Get the number of parameters in the model
getModelNumParams :: Model -> IO (Either String Int64)
getModelNumParams (Model modelFPtr) = do
withForeignPtr modelFPtr $ \modelPtr -> do
params <- c_llama_model_n_params (CLlamaModel modelPtr)
if params == 0
then return $ Left "Failed to get number of parameters"
else return $ Right $ fromIntegral params
-- | Check if the model has a decoder
getModelHasDecoder :: Model -> IO Bool
getModelHasDecoder (Model modelFPtr) = do
withForeignPtr modelFPtr $ \modelPtr -> do
c_llama_model_has_decoder (CLlamaModel modelPtr) <&> (/= 0)
-- | Get the decoder start token from the model
getModelDecoderStartToken :: Model -> IO (Either String LlamaToken)
getModelDecoderStartToken (Model modelFPtr) = do
withForeignPtr modelFPtr $ \modelPtr -> do
token_ <- c_llama_model_decoder_start_token (CLlamaModel modelPtr)
if token_ == -1
then return $ Left "Failed to get decoder start token"
else return $ Right token_
-- | Check if the model is recurrent
getModelIsRecurrent :: Model -> IO Bool
getModelIsRecurrent (Model modelFPtr) = do
withForeignPtr modelFPtr $ \modelPtr -> do
c_llama_model_is_recurrent (CLlamaModel modelPtr) <&> (/= 0)
-- | Quantize a model from a file to another file using specified parameters
quantizeModel ::
FilePath ->
FilePath ->
LlamaModelQuantizeParams ->
IO (Either String Word32)
quantizeModel inpPath outPath params = do
withCString inpPath $ \cInpPath -> do
withCString outPath $ \cOutPath -> do
withStorable params $ \paramsPtr -> do
result <-
c_llama_model_quantize
cInpPath
cOutPath
(CLlamaModelQuantizeParams paramsPtr)
if result == 0
then return $ Left "Failed to quantize model"
else return $ Right result
-- | Quantize a model from a file to another file using default parameters
quantizeModelDefault :: FilePath -> FilePath -> IO (Either String Word32)
quantizeModelDefault inpPath outPath = do
params <- defaultQuantizeParams
quantizeModel inpPath outPath params
-- | Get the default quantization parameters
defaultQuantizeParams :: IO LlamaModelQuantizeParams
defaultQuantizeParams = do
(CLlamaModelQuantizeParams paramsPtr) <- c_llama_model_quantize_default_params
peek paramsPtr
-- | Get a metadata value as a string from a model
getModelMetaValue :: Model -> String -> IO (Either String String)
getModelMetaValue (Model modelFPtr) key = do
withForeignPtr modelFPtr $ \modelPtr -> do
withCString key $ \cKey -> do
allocaArray 256 $ \bufPtr -> do
result <- c_llama_model_meta_val_str (CLlamaModel modelPtr) cKey bufPtr 256
if result == -1
then return $ Left "Failed to get metadata value"
else do
str <- peekCString bufPtr
return $ Right str
-- | Get the number of metadata entries in a model
getModelMetaCount :: Model -> IO Int
getModelMetaCount (Model modelFPtr) = do
withForeignPtr modelFPtr $ \modelPtr -> do
fromIntegral <$> c_llama_model_meta_count (CLlamaModel modelPtr)
-- | Get a metadata key by index from a model
getModelMetaKeyByIndex :: Model -> Int -> IO (Either String String)
getModelMetaKeyByIndex (Model modelFPtr) index = do
withForeignPtr modelFPtr $ \modelPtr -> do
allocaArray 256 $ \bufPtr -> do
result <-
c_llama_model_meta_key_by_index
(CLlamaModel modelPtr)
(fromIntegral index)
bufPtr
256
if result == -1
then return $ Left "Failed to get metadata key"
else do
str <- peekCString bufPtr
return $ Right str
-- | Get a metadata value by index from a model
getModelMetaValueByIndex :: Model -> Int -> IO (Either String String)
getModelMetaValueByIndex (Model modelFPtr) index = do
withForeignPtr modelFPtr $ \modelPtr -> do
allocaArray 256 $ \bufPtr -> do
result <-
c_llama_model_meta_val_str_by_index
(CLlamaModel modelPtr)
(fromIntegral index)
bufPtr
256
if result == -1
then return $ Left "Failed to get metadata value"
else do
str <- peekCString bufPtr
return $ Right str
-- | Get a model description
getModelDescription :: Model -> IO String
getModelDescription (Model modelFPtr) = do
withForeignPtr modelFPtr $ \modelPtr -> do
allocaArray 256 $ \bufPtr -> do
_ <- c_llama_model_desc (CLlamaModel modelPtr) bufPtr 256
peekCString bufPtr
-- | Load a model from multiple file paths using specified parameters
loadModelFromSplits :: [FilePath] -> LlamaModelParams -> IO (Either String Model)
loadModelFromSplits paths params = do
withStorable params $ \paramsPtr -> do
pathsPtr <- newArrayOfPtrs paths
withForeignPtr pathsPtr $ \pathsPtr' -> do
model <-
c_llama_model_load_from_splits
pathsPtr'
(fromIntegral (length paths))
(CLlamaModelParams paramsPtr)
if model == CLlamaModel nullPtr
then return $ Left "Failed to load model"
else do
let (CLlamaModel modelPtr) = model
fp <- newForeignPtr p_llama_model_free modelPtr
return $ Right $ Model fp
-- | Get the RoPE type from a model
getModelRopeType :: Model -> IO (Maybe LlamaRopeTypeScaling)
getModelRopeType (Model modelFPtr) = do
withForeignPtr modelFPtr $ \modelPtr -> do
alloca $ \outPtr -> do
c_llama_model_rope_type_into (CLlamaModel modelPtr) outPtr
val <- peek outPtr
return $ fromLlamaRopeTypeScaling val
newArrayOfPtrs :: [FilePath] -> IO (ForeignPtr CString)
newArrayOfPtrs xs = do
ptrs <- mallocBytes (length xs * sizeOf (undefined :: Ptr CString))
mapM_
( \(i, x) -> withCString x $ \cstr ->
poke
(castPtr ptrs `plusPtr` (i * sizeOf (undefined :: Ptr CString)))
cstr
)
(zip [0 ..] xs)
newForeignPtr_ ptrs