ollama-haskell-0.2.0.0: src/Data/Ollama/Common/Types.hs
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{- |
Module : Data.Ollama.Common.Types
Copyright : (c) 2025 Tushar Adhatrao
License : MIT
Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>
Stability : experimental
Description : Shared data types for request and response structures used throughout the Ollama client.
== 📋 Overview
This module defines common types for working with Ollama's API, including:
- Chat messages and roles
- Text generation responses
- Structured function/tool calling
- Model metadata
- Streaming handling
- Custom model parameters
These types are consumed and returned by higher-level modules
like `Data.Ollama.Chat`, `Data.Ollama.Generate`, and others.
== Includes
- Chat message structure and roles
- Generate and chat response records
- ModelOptions and advanced config
- Structured function/tool call interfaces
- JSON format hints and schema wrapping
- Helper class 'HasDone' for streaming termination
Most types implement `ToJSON`/`FromJSON` for direct API interaction.
-}
module Data.Ollama.Common.Types
( ModelDetails (..)
, Format (..)
, GenerateResponse (..)
, Message (..)
, Role (..)
, ChatResponse (..)
, HasDone (..)
, ModelOptions (..)
, InputTool (..)
, FunctionDef (..)
, FunctionParameters (..)
, ToolCall (..)
, OutputFunction (..)
, Version (..)
) where
import Data.Aeson
import Data.Map qualified as HM
import Data.Maybe (catMaybes)
import Data.Ollama.Common.SchemaBuilder
import Data.Text (Text)
import Data.Time (UTCTime)
import GHC.Generics
import GHC.Int (Int64)
-- | Metadata describing a specific model's identity and configuration.
data ModelDetails = ModelDetails
{ parentModel :: !(Maybe Text)
-- ^ The parent model from which this model was derived, if any.
, format :: !Text
-- ^ The format used for the model (e.g., "gguf").
, family :: !Text
-- ^ The family name of the model (e.g., "llama", "mistral").
, families :: ![Text]
-- ^ Alternative or related family identifiers.
, parameterSize :: !Text
-- ^ The size of the model's parameters, typically expressed as a string (e.g., "7B").
, quantizationLevel :: Text
-- ^ The quantization level used (e.g., "Q4", "Q8").
}
deriving (Eq, Show)
instance FromJSON ModelDetails where
parseJSON = withObject "ModelDetails" $ \v ->
ModelDetails
<$> v .: "parent_model"
<*> v .: "format"
<*> v .: "family"
<*> v .:? "families" .!= []
<*> v .: "parameter_size"
<*> v .: "quantization_level"
{- | Format specification for the chat output.
@since 0.1.3.0
-}
data Format = JsonFormat | SchemaFormat Schema
deriving (Show, Eq)
instance ToJSON Format where
toJSON JsonFormat = String "json"
toJSON (SchemaFormat schema) = toJSON schema
{- |
Result type for 'generate' function containing the model's response and meta-information.
-}
data GenerateResponse = GenerateResponse
{ model :: !Text
-- ^ The name of the model that generated the response.
, createdAt :: !UTCTime
-- ^ The timestamp when the response was created.
, genResponse :: !Text
-- ^ The generated response from the model.
, done :: !Bool
-- ^ A flag indicating whether the generation process is complete.
, totalDuration :: !(Maybe Int64)
-- ^ Optional total duration in milliseconds for the generation process.
, loadDuration :: !(Maybe Int64)
-- ^ Optional load duration in milliseconds for loading the model.
, promptEvalCount :: !(Maybe Int64)
-- ^ Optional count of prompt evaluations during the generation process.
, promptEvalDuration :: !(Maybe Int64)
-- ^ Optional duration in milliseconds for evaluating the prompt.
, evalCount :: !(Maybe Int64)
-- ^ Optional count of evaluations during the generation process.
, evalDuration :: !(Maybe Int64)
-- ^ Optional duration in milliseconds for evaluations during the generation process.
, thinking :: !(Maybe Text)
-- ^ Thinking of reasoning models; if think is set to true
--
-- @since 0.2.0.0
}
deriving (Show, Eq)
instance FromJSON GenerateResponse where
parseJSON = withObject "GenerateResponse" $ \v ->
GenerateResponse
<$> v .: "model"
<*> v .: "created_at"
<*> v .: "response"
<*> v .: "done"
<*> v .:? "total_duration"
<*> v .:? "load_duration"
<*> v .:? "prompt_eval_count"
<*> v .:? "prompt_eval_duration"
<*> v .:? "eval_count"
<*> v .:? "eval_duration"
<*> v .:? "thinking"
-- | Enumerated roles that can participate in a chat.
data Role = System | User | Assistant | Tool
deriving (Show, Eq)
instance ToJSON Role where
toJSON System = String "system"
toJSON User = String "user"
toJSON Assistant = String "assistant"
toJSON Tool = String "tool"
instance FromJSON Role where
parseJSON = withText "Role" $ \t ->
case t of
"system" -> pure System
"user" -> pure User
"assistant" -> pure Assistant
"tool" -> pure Tool
_ -> fail $ "Invalid Role value: " <> show t
-- | Represents a message within a chat, including its role and content.
data Message = Message
{ role :: !Role
-- ^ The role of the entity sending the message (e.g., 'User', 'Assistant').
, content :: !Text
-- ^ The textual content of the message.
, images :: !(Maybe [Text])
-- ^ Optional list of base64 encoded images that accompany the message.
, tool_calls :: !(Maybe [ToolCall])
-- ^ a list of tools in JSON that the model wants to use
--
-- @since 0.1.3.0
, thinking :: !(Maybe Text)
--
-- @since 0.2.0.0
}
deriving (Show, Eq, Generic, ToJSON, FromJSON)
data ChatResponse = ChatResponse
{ model :: !Text
-- ^ The name of the model that generated this response.
, createdAt :: !UTCTime
-- ^ The timestamp when the response was created.
, message :: !(Maybe Message)
-- ^ The message content of the response, if any.
, done :: !Bool
-- ^ Indicates whether the chat process has completed.
, totalDuration :: !(Maybe Int64)
-- ^ Optional total duration in milliseconds for the chat process.
, loadDuration :: !(Maybe Int64)
-- ^ Optional load duration in milliseconds for loading the model.
, promptEvalCount :: !(Maybe Int64)
-- ^ Optional count of prompt evaluations during the chat process.
, promptEvalDuration :: !(Maybe Int64)
-- ^ Optional duration in milliseconds for evaluating the prompt.
, evalCount :: !(Maybe Int64)
-- ^ Optional count of evaluations during the chat process.
, evalDuration :: !(Maybe Int64)
-- ^ Optional duration in milliseconds for evaluations during the chat process.
}
deriving (Show, Eq)
instance FromJSON ChatResponse where
parseJSON = withObject "ChatResponse" $ \v ->
ChatResponse
<$> v .: "model"
<*> v .: "created_at"
<*> v .: "message"
<*> v .: "done"
<*> v .:? "total_duration"
<*> v .:? "load_duration"
<*> v .:? "prompt_eval_count"
<*> v .:? "prompt_eval_duration"
<*> v .:? "eval_count"
<*> v .:? "eval_duration"
-- | A workaround to use done field within commonStreamHandler
class HasDone a where
getDone :: a -> Bool
instance HasDone GenerateResponse where
getDone GenerateResponse {..} = done
instance HasDone ChatResponse where
getDone ChatResponse {..} = done
-- | Optional model tuning parameters that influence generation behavior.
--
-- @since 0.2.0.0
data ModelOptions = ModelOptions
{ numKeep :: Maybe Int
, -- \^ Number of tokens to keep from the previous context.
seed :: Maybe Int
, -- \^ Random seed for reproducibility.
numPredict :: Maybe Int
, -- \^ Maximum number of tokens to predict.
topK :: Maybe Int
, -- \^ Top-K sampling parameter.
topP :: Maybe Double
, -- \^ Top-P (nucleus) sampling parameter.
minP :: Maybe Double
, -- \^ Minimum probability for nucleus sampling.
typicalP :: Maybe Double
, -- \^ Typical sampling probability.
repeatLastN :: Maybe Int
, -- \^ Number of tokens to consider for repetition penalty.
temperature :: Maybe Double
, -- \^ Sampling temperature. Higher = more randomness.
repeatPenalty :: Maybe Double
, -- \^ Penalty for repeating the same tokens.
presencePenalty :: Maybe Double
, -- \^ Penalty for introducing new tokens.
frequencyPenalty :: Maybe Double
, -- \^ Penalty for frequent tokens.
penalizeNewline :: Maybe Bool
, -- \^ Whether to penalize newline tokens.
stop :: Maybe [Text]
, -- \^ List of stop sequences to end generation.
numa :: Maybe Bool
, -- \^ Whether to enable NUMA-aware optimizations.
numCtx :: Maybe Int
, -- \^ Number of context tokens.
numBatch :: Maybe Int
, -- \^ Batch size used during generation.
numGpu :: Maybe Int
, -- \^ Number of GPUs to use.
mainGpu :: Maybe Int
, -- \^ Index of the primary GPU to use.
useMmap :: Maybe Bool
, -- \^ Whether to memory-map the model.
numThread :: Maybe Int
}
-- \^ Number of threads to use for inference.
deriving (Show, Eq)
-- | Custom ToJSON instance for Options
instance ToJSON ModelOptions where
toJSON opts =
object $
catMaybes
[ ("num_keep" .=) <$> numKeep opts
, ("seed" .=) <$> seed opts
, ("num_predict" .=) <$> numPredict opts
, ("top_k" .=) <$> topK opts
, ("top_p" .=) <$> topP opts
, ("min_p" .=) <$> minP opts
, ("typical_p" .=) <$> typicalP opts
, ("repeat_last_n" .=) <$> repeatLastN opts
, ("temperature" .=) <$> temperature opts
, ("repeat_penalty" .=) <$> repeatPenalty opts
, ("presence_penalty" .=) <$> presencePenalty opts
, ("frequency_penalty" .=) <$> frequencyPenalty opts
, ("penalize_newline" .=) <$> penalizeNewline opts
, ("stop" .=) <$> stop opts
, ("numa" .=) <$> numa opts
, ("num_ctx" .=) <$> numCtx opts
, ("num_batch" .=) <$> numBatch opts
, ("num_gpu" .=) <$> numGpu opts
, ("main_gpu" .=) <$> mainGpu opts
, ("use_mmap" .=) <$> useMmap opts
, ("num_thread" .=) <$> numThread opts
]
-- | A wrapper for the Ollama engine version string.
newtype Version = Version Text
deriving (Eq, Show)
instance FromJSON Version where
parseJSON = withObject "version" $ \v -> do
Version <$> v .: "version"
-- | Represents a tool that can be used in the conversation.
--
-- @since 0.2.0.0
data InputTool = InputTool
{ toolType :: Text
-- ^ The type of the tool
, function :: FunctionDef
-- ^ The function associated with the tool
}
deriving (Show, Eq, Generic)
instance ToJSON InputTool where
toJSON InputTool {..} =
object
[ "type" .= toolType
, "function" .= function
]
instance FromJSON InputTool where
parseJSON = withObject "Tool" $ \v ->
InputTool
<$> v .: "type"
<*> v .: "function"
-- | Represents a function that can be called by the model.
--
-- @since 0.2.0.0
data FunctionDef = FunctionDef
{ functionName :: Text
-- ^ The name of the function
, functionDescription :: Maybe Text
-- ^ Optional description of the function
, functionParameters :: Maybe FunctionParameters
-- ^ Optional parameters for the function
, functionStrict :: Maybe Bool
-- ^ Optional strictness flag
}
deriving (Show, Eq, Generic)
instance ToJSON FunctionDef where
toJSON FunctionDef {..} =
object $
[ "name" .= functionName
]
++ maybe [] (\d -> ["description" .= d]) functionDescription
++ maybe [] (\p -> ["parameters" .= p]) functionParameters
++ maybe [] (\s -> ["strict" .= s]) functionStrict
instance FromJSON FunctionDef where
parseJSON = withObject "Function" $ \v ->
FunctionDef
<$> v .: "name"
<*> v .:? "description"
<*> v .:? "parameters"
<*> v .:? "strict"
-- | Parameters definition for a function call used in structured output or tool calls.
--
-- @since 0.2.0.0
data FunctionParameters = FunctionParameters
{ parameterType :: Text
-- ^ Type of the parameter (usually "object").
, parameterProperties :: Maybe (HM.Map Text FunctionParameters)
-- ^ Optional nested parameters as a property map.
, requiredParams :: Maybe [Text]
-- ^ List of required parameter names.
, additionalProperties :: Maybe Bool
-- ^ Whether additional (unspecified) parameters are allowed.
}
deriving (Show, Eq)
instance ToJSON FunctionParameters where
toJSON FunctionParameters {..} =
object
[ "type" .= parameterType
, "properties" .= parameterProperties
, "required" .= requiredParams
, "additionalProperties" .= additionalProperties
]
instance FromJSON FunctionParameters where
parseJSON = withObject "parameters" $ \v ->
FunctionParameters
<$> v .: "type"
<*> v .: "properties"
<*> v .: "required"
<*> v .: "additionalProperties"
-- | A single tool call returned from the model, containing the function to be invoked.
--
-- @since 0.2.0.0
newtype ToolCall = ToolCall
{ outputFunction :: OutputFunction
-- ^ The function the model intends to call, with arguments.
}
deriving (Show, Eq)
-- | Output representation of a function to be called, including its name and arguments.
--
-- @since 0.2.0.0
data OutputFunction = OutputFunction
{ outputFunctionName :: Text
-- ^ The name of the function to invoke.
, arguments :: HM.Map Text Value
-- ^ A key-value map of argument names to values (JSON values).
}
deriving (Eq, Show)
instance ToJSON OutputFunction where
toJSON OutputFunction {..} =
object
[ "name" .= outputFunctionName
, "arguments" .= arguments
]
instance FromJSON OutputFunction where
parseJSON = withObject "function" $ \v ->
OutputFunction
<$> v .: "name"
<*> v .: "arguments"
instance ToJSON ToolCall where
toJSON ToolCall {..} = object ["function" .= outputFunction]
instance FromJSON ToolCall where
parseJSON = withObject "tool_calls" $ \v ->
ToolCall <$> v .: "function"