ollama-haskell 0.1.3.0 → 0.2.0.0
raw patch · 27 files changed
+3500/−1425 lines, 27 filesdep +http-client-tlsdep +mtldep +scientificdep ~aesondep ~base64-bytestringdep ~bytestringPVP ok
version bump matches the API change (PVP)
Dependencies added: http-client-tls, mtl, scientific, stm
Dependency ranges changed: aeson, base64-bytestring, bytestring, containers, directory, filepath, http-client, http-types, tasty, text, time
API changes (from Hackage documentation)
- Data.Ollama.Chat: [responseTimeOut] :: ChatOps -> Maybe Int
- Data.Ollama.Chat: chatJson :: (FromJSON jsonResult, ToJSON jsonResult) => ChatOps -> jsonResult -> Maybe Int -> IO (Either String jsonResult)
- Data.Ollama.Chat: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Chat.ChatResponse
- Data.Ollama.Chat: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Chat.Message
- Data.Ollama.Chat: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Chat.Role
- Data.Ollama.Chat: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Chat.Message
- Data.Ollama.Chat: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Chat.Role
- Data.Ollama.Chat: instance GHC.Classes.Eq Data.Ollama.Chat.ChatResponse
- Data.Ollama.Chat: instance GHC.Classes.Eq Data.Ollama.Chat.Message
- Data.Ollama.Chat: instance GHC.Classes.Eq Data.Ollama.Chat.Role
- Data.Ollama.Chat: instance GHC.Generics.Generic Data.Ollama.Chat.Message
- Data.Ollama.Chat: instance GHC.Show.Show Data.Ollama.Chat.ChatResponse
- Data.Ollama.Chat: instance GHC.Show.Show Data.Ollama.Chat.Message
- Data.Ollama.Chat: instance GHC.Show.Show Data.Ollama.Chat.Role
- Data.Ollama.Chat: schemaFromType :: ToJSON a => a -> ByteString
- Data.Ollama.Common.Types: OllamaClient :: Text -> OllamaClient
- Data.Ollama.Common.Types: [$sel:families:ModelDetails] :: ModelDetails -> [Text]
- Data.Ollama.Common.Types: [$sel:familiy:ModelDetails] :: ModelDetails -> Text
- Data.Ollama.Common.Types: [$sel:format:ModelDetails] :: ModelDetails -> Text
- Data.Ollama.Common.Types: [$sel:host:OllamaClient] :: OllamaClient -> Text
- Data.Ollama.Common.Types: [$sel:parameterSize:ModelDetails] :: ModelDetails -> Text
- Data.Ollama.Common.Types: [$sel:parentModel:ModelDetails] :: ModelDetails -> Maybe Text
- Data.Ollama.Common.Types: [$sel:quantizationLevel:ModelDetails] :: ModelDetails -> Text
- Data.Ollama.Common.Types: instance GHC.Classes.Eq Data.Ollama.Common.Types.OllamaClient
- Data.Ollama.Common.Types: instance GHC.Show.Show Data.Ollama.Common.Types.OllamaClient
- Data.Ollama.Common.Types: newtype OllamaClient
- Data.Ollama.Common.Utils: OllamaClient :: Text -> OllamaClient
- Data.Ollama.Common.Utils: [$sel:host:OllamaClient] :: OllamaClient -> Text
- Data.Ollama.Common.Utils: defaultOllamaUrl :: Text
- Data.Ollama.Common.Utils: newtype OllamaClient
- Data.Ollama.Create: createModelOps :: Text -> Maybe Text -> Maybe Bool -> Maybe FilePath -> IO ()
- Data.Ollama.Embeddings: [$sel:embedding_:EmbeddingResp] :: EmbeddingResp -> [[Float]]
- Data.Ollama.Embeddings: [$sel:input:EmbeddingOps] :: EmbeddingOps -> Text
- Data.Ollama.Embeddings: [$sel:keepAlive:EmbeddingOps] :: EmbeddingOps -> Maybe Text
- Data.Ollama.Embeddings: [$sel:model:EmbeddingOps] :: EmbeddingOps -> Text
- Data.Ollama.Embeddings: [$sel:model:EmbeddingResp] :: EmbeddingResp -> Text
- Data.Ollama.Embeddings: [$sel:truncate:EmbeddingOps] :: EmbeddingOps -> Maybe Bool
- Data.Ollama.Generate: [$sel:createdAt:GenerateResponse] :: GenerateResponse -> UTCTime
- Data.Ollama.Generate: [$sel:done:GenerateResponse] :: GenerateResponse -> Bool
- Data.Ollama.Generate: [$sel:evalCount:GenerateResponse] :: GenerateResponse -> Maybe Int64
- Data.Ollama.Generate: [$sel:evalDuration:GenerateResponse] :: GenerateResponse -> Maybe Int64
- Data.Ollama.Generate: [$sel:format:GenerateOps] :: GenerateOps -> Maybe Format
- Data.Ollama.Generate: [$sel:hostUrl:GenerateOps] :: GenerateOps -> Maybe Text
- Data.Ollama.Generate: [$sel:images:GenerateOps] :: GenerateOps -> Maybe [Text]
- Data.Ollama.Generate: [$sel:keepAlive:GenerateOps] :: GenerateOps -> Maybe Text
- Data.Ollama.Generate: [$sel:loadDuration:GenerateResponse] :: GenerateResponse -> Maybe Int64
- Data.Ollama.Generate: [$sel:model:GenerateResponse] :: GenerateResponse -> Text
- Data.Ollama.Generate: [$sel:modelName:GenerateOps] :: GenerateOps -> Text
- Data.Ollama.Generate: [$sel:options:GenerateOps] :: GenerateOps -> Maybe Value
- Data.Ollama.Generate: [$sel:prompt:GenerateOps] :: GenerateOps -> Text
- Data.Ollama.Generate: [$sel:promptEvalCount:GenerateResponse] :: GenerateResponse -> Maybe Int64
- Data.Ollama.Generate: [$sel:promptEvalDuration:GenerateResponse] :: GenerateResponse -> Maybe Int64
- Data.Ollama.Generate: [$sel:raw:GenerateOps] :: GenerateOps -> Maybe Bool
- Data.Ollama.Generate: [$sel:responseTimeOut:GenerateOps] :: GenerateOps -> Maybe Int
- Data.Ollama.Generate: [$sel:response_:GenerateResponse] :: GenerateResponse -> Text
- Data.Ollama.Generate: [$sel:stream:GenerateOps] :: GenerateOps -> Maybe (GenerateResponse -> IO (), IO ())
- Data.Ollama.Generate: [$sel:suffix:GenerateOps] :: GenerateOps -> Maybe Text
- Data.Ollama.Generate: [$sel:system:GenerateOps] :: GenerateOps -> Maybe Text
- Data.Ollama.Generate: [$sel:template:GenerateOps] :: GenerateOps -> Maybe Text
- Data.Ollama.Generate: [$sel:totalDuration:GenerateResponse] :: GenerateResponse -> Maybe Int64
- Data.Ollama.Generate: generateJson :: (ToJSON jsonResult, FromJSON jsonResult) => GenerateOps -> jsonResult -> Maybe Int -> IO (Either String jsonResult)
- Data.Ollama.Generate: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Generate.GenerateResponse
- Data.Ollama.Generate: instance GHC.Classes.Eq Data.Ollama.Generate.GenerateResponse
- Data.Ollama.Generate: instance GHC.Show.Show Data.Ollama.Generate.GenerateResponse
- Data.Ollama.Push: pushOps :: Text -> Maybe Bool -> Maybe Bool -> IO ()
- Data.Ollama.Show: [$sel:details:ShowModelResponse] :: ShowModelResponse -> ModelDetails
- Data.Ollama.Show: [$sel:modelFile:ShowModelResponse] :: ShowModelResponse -> Text
- Data.Ollama.Show: [$sel:modelInfo:ShowModelResponse] :: ShowModelResponse -> ModelInfo
- Data.Ollama.Show: [$sel:parameters:ShowModelResponse] :: ShowModelResponse -> Text
- Data.Ollama.Show: [$sel:template:ShowModelResponse] :: ShowModelResponse -> Text
- Data.Ollama.Show: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Show.ModelDetails
- Data.Ollama.Show: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Show.ModelInfo
- Data.Ollama.Show: instance GHC.Classes.Eq Data.Ollama.Show.ModelDetails
- Data.Ollama.Show: instance GHC.Classes.Eq Data.Ollama.Show.ModelInfo
- Data.Ollama.Show: instance GHC.Show.Show Data.Ollama.Show.ModelDetails
- Data.Ollama.Show: instance GHC.Show.Show Data.Ollama.Show.ModelInfo
- Ollama: [$sel:createdAt:GenerateResponse] :: GenerateResponse -> UTCTime
- Ollama: [$sel:details:ShowModelResponse] :: ShowModelResponse -> ModelDetails
- Ollama: [$sel:done:GenerateResponse] :: GenerateResponse -> Bool
- Ollama: [$sel:embedding_:EmbeddingResp] :: EmbeddingResp -> [[Float]]
- Ollama: [$sel:evalCount:GenerateResponse] :: GenerateResponse -> Maybe Int64
- Ollama: [$sel:evalDuration:GenerateResponse] :: GenerateResponse -> Maybe Int64
- Ollama: [$sel:format:GenerateOps] :: GenerateOps -> Maybe Format
- Ollama: [$sel:hostUrl:GenerateOps] :: GenerateOps -> Maybe Text
- Ollama: [$sel:images:GenerateOps] :: GenerateOps -> Maybe [Text]
- Ollama: [$sel:input:EmbeddingOps] :: EmbeddingOps -> Text
- Ollama: [$sel:keepAlive:EmbeddingOps] :: EmbeddingOps -> Maybe Text
- Ollama: [$sel:keepAlive:GenerateOps] :: GenerateOps -> Maybe Text
- Ollama: [$sel:loadDuration:GenerateResponse] :: GenerateResponse -> Maybe Int64
- Ollama: [$sel:model:EmbeddingOps] :: EmbeddingOps -> Text
- Ollama: [$sel:model:EmbeddingResp] :: EmbeddingResp -> Text
- Ollama: [$sel:model:GenerateResponse] :: GenerateResponse -> Text
- Ollama: [$sel:modelFile:ShowModelResponse] :: ShowModelResponse -> Text
- Ollama: [$sel:modelInfo:ShowModelResponse] :: ShowModelResponse -> ModelInfo
- Ollama: [$sel:modelName:GenerateOps] :: GenerateOps -> Text
- Ollama: [$sel:options:GenerateOps] :: GenerateOps -> Maybe Value
- Ollama: [$sel:parameters:ShowModelResponse] :: ShowModelResponse -> Text
- Ollama: [$sel:prompt:GenerateOps] :: GenerateOps -> Text
- Ollama: [$sel:promptEvalCount:GenerateResponse] :: GenerateResponse -> Maybe Int64
- Ollama: [$sel:promptEvalDuration:GenerateResponse] :: GenerateResponse -> Maybe Int64
- Ollama: [$sel:raw:GenerateOps] :: GenerateOps -> Maybe Bool
- Ollama: [$sel:responseTimeOut:GenerateOps] :: GenerateOps -> Maybe Int
- Ollama: [$sel:response_:GenerateResponse] :: GenerateResponse -> Text
- Ollama: [$sel:stream:GenerateOps] :: GenerateOps -> Maybe (GenerateResponse -> IO (), IO ())
- Ollama: [$sel:suffix:GenerateOps] :: GenerateOps -> Maybe Text
- Ollama: [$sel:system:GenerateOps] :: GenerateOps -> Maybe Text
- Ollama: [$sel:template:GenerateOps] :: GenerateOps -> Maybe Text
- Ollama: [$sel:template:ShowModelResponse] :: ShowModelResponse -> Text
- Ollama: [$sel:totalDuration:GenerateResponse] :: GenerateResponse -> Maybe Int64
- Ollama: [$sel:truncate:EmbeddingOps] :: EmbeddingOps -> Maybe Bool
- Ollama: [responseTimeOut] :: ChatOps -> Maybe Int
- Ollama: chatJson :: (FromJSON jsonResult, ToJSON jsonResult) => ChatOps -> jsonResult -> Maybe Int -> IO (Either String jsonResult)
- Ollama: createModelOps :: Text -> Maybe Text -> Maybe Bool -> Maybe FilePath -> IO ()
- Ollama: generateJson :: (ToJSON jsonResult, FromJSON jsonResult) => GenerateOps -> jsonResult -> Maybe Int -> IO (Either String jsonResult)
- Ollama: pushOps :: Text -> Maybe Bool -> Maybe Bool -> IO ()
+ Data.Ollama.Chat: ApiError :: Text -> OllamaError
+ Data.Ollama.Chat: DecodeError :: DecodingErrorMessage -> DecodingFailedValue -> OllamaError
+ Data.Ollama.Chat: FileError :: IOException -> OllamaError
+ Data.Ollama.Chat: FunctionDef :: Text -> Maybe Text -> Maybe FunctionParameters -> Maybe Bool -> FunctionDef
+ Data.Ollama.Chat: FunctionParameters :: Text -> Maybe (Map Text FunctionParameters) -> Maybe [Text] -> Maybe Bool -> FunctionParameters
+ Data.Ollama.Chat: HttpError :: HttpException -> OllamaError
+ Data.Ollama.Chat: InputTool :: Text -> FunctionDef -> InputTool
+ Data.Ollama.Chat: InvalidRequest :: String -> OllamaError
+ Data.Ollama.Chat: JsonSchemaError :: String -> OllamaError
+ Data.Ollama.Chat: ModelOptions :: Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Int -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Bool -> Maybe [Text] -> Maybe Bool -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Bool -> Maybe Int -> ModelOptions
+ Data.Ollama.Chat: OllamaConfig :: Text -> Int -> Maybe (IO ()) -> Maybe (IO ()) -> Maybe (IO ()) -> Maybe Int -> Maybe Int -> Maybe Manager -> OllamaConfig
+ Data.Ollama.Chat: OutputFunction :: Text -> Map Text Value -> OutputFunction
+ Data.Ollama.Chat: TimeoutError :: String -> OllamaError
+ Data.Ollama.Chat: ToolCall :: OutputFunction -> ToolCall
+ Data.Ollama.Chat: [additionalProperties] :: FunctionParameters -> Maybe Bool
+ Data.Ollama.Chat: [arguments] :: OutputFunction -> Map Text Value
+ Data.Ollama.Chat: [commonManager] :: OllamaConfig -> Maybe Manager
+ Data.Ollama.Chat: [frequencyPenalty] :: ModelOptions -> Maybe Double
+ Data.Ollama.Chat: [functionDescription] :: FunctionDef -> Maybe Text
+ Data.Ollama.Chat: [functionName] :: FunctionDef -> Text
+ Data.Ollama.Chat: [functionParameters] :: FunctionDef -> Maybe FunctionParameters
+ Data.Ollama.Chat: [functionStrict] :: FunctionDef -> Maybe Bool
+ Data.Ollama.Chat: [function] :: InputTool -> FunctionDef
+ Data.Ollama.Chat: [mainGpu] :: ModelOptions -> Maybe Int
+ Data.Ollama.Chat: [minP] :: ModelOptions -> Maybe Double
+ Data.Ollama.Chat: [numBatch] :: ModelOptions -> Maybe Int
+ Data.Ollama.Chat: [numCtx] :: ModelOptions -> Maybe Int
+ Data.Ollama.Chat: [numGpu] :: ModelOptions -> Maybe Int
+ Data.Ollama.Chat: [numKeep] :: ModelOptions -> Maybe Int
+ Data.Ollama.Chat: [numPredict] :: ModelOptions -> Maybe Int
+ Data.Ollama.Chat: [numThread] :: ModelOptions -> Maybe Int
+ Data.Ollama.Chat: [numa] :: ModelOptions -> Maybe Bool
+ Data.Ollama.Chat: [onModelError] :: OllamaConfig -> Maybe (IO ())
+ Data.Ollama.Chat: [onModelFinish] :: OllamaConfig -> Maybe (IO ())
+ Data.Ollama.Chat: [onModelStart] :: OllamaConfig -> Maybe (IO ())
+ Data.Ollama.Chat: [outputFunctionName] :: OutputFunction -> Text
+ Data.Ollama.Chat: [outputFunction] :: ToolCall -> OutputFunction
+ Data.Ollama.Chat: [parameterProperties] :: FunctionParameters -> Maybe (Map Text FunctionParameters)
+ Data.Ollama.Chat: [parameterType] :: FunctionParameters -> Text
+ Data.Ollama.Chat: [penalizeNewline] :: ModelOptions -> Maybe Bool
+ Data.Ollama.Chat: [presencePenalty] :: ModelOptions -> Maybe Double
+ Data.Ollama.Chat: [repeatLastN] :: ModelOptions -> Maybe Int
+ Data.Ollama.Chat: [repeatPenalty] :: ModelOptions -> Maybe Double
+ Data.Ollama.Chat: [requiredParams] :: FunctionParameters -> Maybe [Text]
+ Data.Ollama.Chat: [retryCount] :: OllamaConfig -> Maybe Int
+ Data.Ollama.Chat: [retryDelay] :: OllamaConfig -> Maybe Int
+ Data.Ollama.Chat: [seed] :: ModelOptions -> Maybe Int
+ Data.Ollama.Chat: [stop] :: ModelOptions -> Maybe [Text]
+ Data.Ollama.Chat: [temperature] :: ModelOptions -> Maybe Double
+ Data.Ollama.Chat: [think] :: ChatOps -> !Maybe Bool
+ Data.Ollama.Chat: [thinking] :: Message -> !Maybe Text
+ Data.Ollama.Chat: [timeout] :: OllamaConfig -> Int
+ Data.Ollama.Chat: [toolType] :: InputTool -> Text
+ Data.Ollama.Chat: [topK] :: ModelOptions -> Maybe Int
+ Data.Ollama.Chat: [topP] :: ModelOptions -> Maybe Double
+ Data.Ollama.Chat: [typicalP] :: ModelOptions -> Maybe Double
+ Data.Ollama.Chat: [useMmap] :: ModelOptions -> Maybe Bool
+ Data.Ollama.Chat: assistantMessage :: Text -> Message
+ Data.Ollama.Chat: chatM :: MonadIO m => ChatOps -> Maybe OllamaConfig -> m (Either OllamaError ChatResponse)
+ Data.Ollama.Chat: data FunctionDef
+ Data.Ollama.Chat: data FunctionParameters
+ Data.Ollama.Chat: data InputTool
+ Data.Ollama.Chat: data ModelOptions
+ Data.Ollama.Chat: data OllamaConfig
+ Data.Ollama.Chat: data OllamaError
+ Data.Ollama.Chat: data OutputFunction
+ Data.Ollama.Chat: defaultModelOptions :: ModelOptions
+ Data.Ollama.Chat: defaultOllamaConfig :: OllamaConfig
+ Data.Ollama.Chat: genMessage :: Role -> Text -> Message
+ Data.Ollama.Chat: newtype ToolCall
+ Data.Ollama.Chat: systemMessage :: Text -> Message
+ Data.Ollama.Chat: toolMessage :: Text -> Message
+ Data.Ollama.Chat: userMessage :: Text -> Message
+ Data.Ollama.Common.Config: OllamaConfig :: Text -> Int -> Maybe (IO ()) -> Maybe (IO ()) -> Maybe (IO ()) -> Maybe Int -> Maybe Int -> Maybe Manager -> OllamaConfig
+ Data.Ollama.Common.Config: [commonManager] :: OllamaConfig -> Maybe Manager
+ Data.Ollama.Common.Config: [hostUrl] :: OllamaConfig -> Text
+ Data.Ollama.Common.Config: [onModelError] :: OllamaConfig -> Maybe (IO ())
+ Data.Ollama.Common.Config: [onModelFinish] :: OllamaConfig -> Maybe (IO ())
+ Data.Ollama.Common.Config: [onModelStart] :: OllamaConfig -> Maybe (IO ())
+ Data.Ollama.Common.Config: [retryCount] :: OllamaConfig -> Maybe Int
+ Data.Ollama.Common.Config: [retryDelay] :: OllamaConfig -> Maybe Int
+ Data.Ollama.Common.Config: [timeout] :: OllamaConfig -> Int
+ Data.Ollama.Common.Config: data OllamaConfig
+ Data.Ollama.Common.Config: defaultOllamaConfig :: OllamaConfig
+ Data.Ollama.Common.Config: instance GHC.Generics.Generic Data.Ollama.Common.Config.OllamaConfig
+ Data.Ollama.Common.Config: withOnModelError :: IO () -> OllamaConfig -> OllamaConfig
+ Data.Ollama.Common.Config: withOnModelFinish :: IO () -> OllamaConfig -> OllamaConfig
+ Data.Ollama.Common.Config: withOnModelStart :: IO () -> OllamaConfig -> OllamaConfig
+ Data.Ollama.Common.Error: ApiError :: Text -> OllamaError
+ Data.Ollama.Common.Error: DecodeError :: DecodingErrorMessage -> DecodingFailedValue -> OllamaError
+ Data.Ollama.Common.Error: FileError :: IOException -> OllamaError
+ Data.Ollama.Common.Error: HttpError :: HttpException -> OllamaError
+ Data.Ollama.Common.Error: InvalidRequest :: String -> OllamaError
+ Data.Ollama.Common.Error: JsonSchemaError :: String -> OllamaError
+ Data.Ollama.Common.Error: TimeoutError :: String -> OllamaError
+ Data.Ollama.Common.Error: data OllamaError
+ Data.Ollama.Common.Error: instance GHC.Classes.Eq Data.Ollama.Common.Error.OllamaError
+ Data.Ollama.Common.Error: instance GHC.Exception.Type.Exception Data.Ollama.Common.Error.OllamaError
+ Data.Ollama.Common.Error: instance GHC.Generics.Generic Data.Ollama.Common.Error.OllamaError
+ Data.Ollama.Common.Error: instance GHC.Show.Show Data.Ollama.Common.Error.OllamaError
+ Data.Ollama.Common.Error: type DecodingErrorMessage = String
+ Data.Ollama.Common.Error: type DecodingFailedValue = String
+ Data.Ollama.Common.SchemaBuilder: (|!!) :: SchemaBuilder -> [Text] -> SchemaBuilder
+ Data.Ollama.Common.SchemaBuilder: (|!) :: SchemaBuilder -> Text -> SchemaBuilder
+ Data.Ollama.Common.SchemaBuilder: (|+) :: SchemaBuilder -> (Text, JsonType) -> SchemaBuilder
+ Data.Ollama.Common.SchemaBuilder: (|++) :: SchemaBuilder -> (Text, Schema) -> SchemaBuilder
+ Data.Ollama.Common.SchemaBuilder: JArray :: JsonType -> JsonType
+ Data.Ollama.Common.SchemaBuilder: JBoolean :: JsonType
+ Data.Ollama.Common.SchemaBuilder: JInteger :: JsonType
+ Data.Ollama.Common.SchemaBuilder: JNull :: JsonType
+ Data.Ollama.Common.SchemaBuilder: JNumber :: JsonType
+ Data.Ollama.Common.SchemaBuilder: JObject :: Schema -> JsonType
+ Data.Ollama.Common.SchemaBuilder: JString :: JsonType
+ Data.Ollama.Common.SchemaBuilder: Property :: JsonType -> Property
+ Data.Ollama.Common.SchemaBuilder: Schema :: Map Text Property -> [Text] -> Schema
+ Data.Ollama.Common.SchemaBuilder: [schemaProperties] :: Schema -> Map Text Property
+ Data.Ollama.Common.SchemaBuilder: [schemaRequired] :: Schema -> [Text]
+ Data.Ollama.Common.SchemaBuilder: addObjectProperty :: Text -> Schema -> SchemaBuilder -> SchemaBuilder
+ Data.Ollama.Common.SchemaBuilder: addProperty :: Text -> JsonType -> SchemaBuilder -> SchemaBuilder
+ Data.Ollama.Common.SchemaBuilder: arrayOf :: JsonType -> JsonType
+ Data.Ollama.Common.SchemaBuilder: buildSchema :: SchemaBuilder -> Schema
+ Data.Ollama.Common.SchemaBuilder: data JsonType
+ Data.Ollama.Common.SchemaBuilder: data Schema
+ Data.Ollama.Common.SchemaBuilder: emptyObject :: SchemaBuilder
+ Data.Ollama.Common.SchemaBuilder: infixl 6 |!!
+ Data.Ollama.Common.SchemaBuilder: infixl 7 |++
+ Data.Ollama.Common.SchemaBuilder: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Common.SchemaBuilder.JsonType
+ Data.Ollama.Common.SchemaBuilder: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Common.SchemaBuilder.Property
+ Data.Ollama.Common.SchemaBuilder: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Common.SchemaBuilder.Schema
+ Data.Ollama.Common.SchemaBuilder: instance GHC.Classes.Eq Data.Ollama.Common.SchemaBuilder.JsonType
+ Data.Ollama.Common.SchemaBuilder: instance GHC.Classes.Eq Data.Ollama.Common.SchemaBuilder.Property
+ Data.Ollama.Common.SchemaBuilder: instance GHC.Classes.Eq Data.Ollama.Common.SchemaBuilder.Schema
+ Data.Ollama.Common.SchemaBuilder: instance GHC.Classes.Eq Data.Ollama.Common.SchemaBuilder.SchemaBuilder
+ Data.Ollama.Common.SchemaBuilder: instance GHC.Generics.Generic Data.Ollama.Common.SchemaBuilder.JsonType
+ Data.Ollama.Common.SchemaBuilder: instance GHC.Generics.Generic Data.Ollama.Common.SchemaBuilder.Property
+ Data.Ollama.Common.SchemaBuilder: instance GHC.Generics.Generic Data.Ollama.Common.SchemaBuilder.Schema
+ Data.Ollama.Common.SchemaBuilder: instance GHC.Show.Show Data.Ollama.Common.SchemaBuilder.JsonType
+ Data.Ollama.Common.SchemaBuilder: instance GHC.Show.Show Data.Ollama.Common.SchemaBuilder.Property
+ Data.Ollama.Common.SchemaBuilder: instance GHC.Show.Show Data.Ollama.Common.SchemaBuilder.Schema
+ Data.Ollama.Common.SchemaBuilder: instance GHC.Show.Show Data.Ollama.Common.SchemaBuilder.SchemaBuilder
+ Data.Ollama.Common.SchemaBuilder: newtype Property
+ Data.Ollama.Common.SchemaBuilder: objectOf :: SchemaBuilder -> JsonType
+ Data.Ollama.Common.SchemaBuilder: printSchema :: Schema -> IO ()
+ Data.Ollama.Common.SchemaBuilder: requireField :: Text -> SchemaBuilder -> SchemaBuilder
+ Data.Ollama.Common.SchemaBuilder: requireFields :: [Text] -> SchemaBuilder -> SchemaBuilder
+ Data.Ollama.Common.SchemaBuilder: toOllamaFormat :: Schema -> Value
+ Data.Ollama.Common.Types: Assistant :: Role
+ Data.Ollama.Common.Types: ChatResponse :: !Text -> !UTCTime -> !Maybe Message -> !Bool -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> ChatResponse
+ Data.Ollama.Common.Types: FunctionDef :: Text -> Maybe Text -> Maybe FunctionParameters -> Maybe Bool -> FunctionDef
+ Data.Ollama.Common.Types: FunctionParameters :: Text -> Maybe (Map Text FunctionParameters) -> Maybe [Text] -> Maybe Bool -> FunctionParameters
+ Data.Ollama.Common.Types: GenerateResponse :: !Text -> !UTCTime -> !Text -> !Bool -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Text -> GenerateResponse
+ Data.Ollama.Common.Types: InputTool :: Text -> FunctionDef -> InputTool
+ Data.Ollama.Common.Types: Message :: !Role -> !Text -> !Maybe [Text] -> !Maybe [ToolCall] -> !Maybe Text -> Message
+ Data.Ollama.Common.Types: ModelOptions :: Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Int -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Bool -> Maybe [Text] -> Maybe Bool -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Bool -> Maybe Int -> ModelOptions
+ Data.Ollama.Common.Types: OutputFunction :: Text -> Map Text Value -> OutputFunction
+ Data.Ollama.Common.Types: System :: Role
+ Data.Ollama.Common.Types: Tool :: Role
+ Data.Ollama.Common.Types: ToolCall :: OutputFunction -> ToolCall
+ Data.Ollama.Common.Types: User :: Role
+ Data.Ollama.Common.Types: Version :: Text -> Version
+ Data.Ollama.Common.Types: [additionalProperties] :: FunctionParameters -> Maybe Bool
+ Data.Ollama.Common.Types: [arguments] :: OutputFunction -> Map Text Value
+ Data.Ollama.Common.Types: [content] :: Message -> !Text
+ Data.Ollama.Common.Types: [createdAt] :: ChatResponse -> !UTCTime
+ Data.Ollama.Common.Types: [done] :: ChatResponse -> !Bool
+ Data.Ollama.Common.Types: [evalCount] :: ChatResponse -> !Maybe Int64
+ Data.Ollama.Common.Types: [evalDuration] :: ChatResponse -> !Maybe Int64
+ Data.Ollama.Common.Types: [families] :: ModelDetails -> ![Text]
+ Data.Ollama.Common.Types: [family] :: ModelDetails -> !Text
+ Data.Ollama.Common.Types: [format] :: ModelDetails -> !Text
+ Data.Ollama.Common.Types: [frequencyPenalty] :: ModelOptions -> Maybe Double
+ Data.Ollama.Common.Types: [functionDescription] :: FunctionDef -> Maybe Text
+ Data.Ollama.Common.Types: [functionName] :: FunctionDef -> Text
+ Data.Ollama.Common.Types: [functionParameters] :: FunctionDef -> Maybe FunctionParameters
+ Data.Ollama.Common.Types: [functionStrict] :: FunctionDef -> Maybe Bool
+ Data.Ollama.Common.Types: [function] :: InputTool -> FunctionDef
+ Data.Ollama.Common.Types: [genResponse] :: GenerateResponse -> !Text
+ Data.Ollama.Common.Types: [images] :: Message -> !Maybe [Text]
+ Data.Ollama.Common.Types: [loadDuration] :: ChatResponse -> !Maybe Int64
+ Data.Ollama.Common.Types: [mainGpu] :: ModelOptions -> Maybe Int
+ Data.Ollama.Common.Types: [message] :: ChatResponse -> !Maybe Message
+ Data.Ollama.Common.Types: [minP] :: ModelOptions -> Maybe Double
+ Data.Ollama.Common.Types: [model] :: ChatResponse -> !Text
+ Data.Ollama.Common.Types: [numBatch] :: ModelOptions -> Maybe Int
+ Data.Ollama.Common.Types: [numCtx] :: ModelOptions -> Maybe Int
+ Data.Ollama.Common.Types: [numGpu] :: ModelOptions -> Maybe Int
+ Data.Ollama.Common.Types: [numKeep] :: ModelOptions -> Maybe Int
+ Data.Ollama.Common.Types: [numPredict] :: ModelOptions -> Maybe Int
+ Data.Ollama.Common.Types: [numThread] :: ModelOptions -> Maybe Int
+ Data.Ollama.Common.Types: [numa] :: ModelOptions -> Maybe Bool
+ Data.Ollama.Common.Types: [outputFunctionName] :: OutputFunction -> Text
+ Data.Ollama.Common.Types: [outputFunction] :: ToolCall -> OutputFunction
+ Data.Ollama.Common.Types: [parameterProperties] :: FunctionParameters -> Maybe (Map Text FunctionParameters)
+ Data.Ollama.Common.Types: [parameterSize] :: ModelDetails -> !Text
+ Data.Ollama.Common.Types: [parameterType] :: FunctionParameters -> Text
+ Data.Ollama.Common.Types: [parentModel] :: ModelDetails -> !Maybe Text
+ Data.Ollama.Common.Types: [penalizeNewline] :: ModelOptions -> Maybe Bool
+ Data.Ollama.Common.Types: [presencePenalty] :: ModelOptions -> Maybe Double
+ Data.Ollama.Common.Types: [promptEvalCount] :: ChatResponse -> !Maybe Int64
+ Data.Ollama.Common.Types: [promptEvalDuration] :: ChatResponse -> !Maybe Int64
+ Data.Ollama.Common.Types: [quantizationLevel] :: ModelDetails -> Text
+ Data.Ollama.Common.Types: [repeatLastN] :: ModelOptions -> Maybe Int
+ Data.Ollama.Common.Types: [repeatPenalty] :: ModelOptions -> Maybe Double
+ Data.Ollama.Common.Types: [requiredParams] :: FunctionParameters -> Maybe [Text]
+ Data.Ollama.Common.Types: [role] :: Message -> !Role
+ Data.Ollama.Common.Types: [seed] :: ModelOptions -> Maybe Int
+ Data.Ollama.Common.Types: [stop] :: ModelOptions -> Maybe [Text]
+ Data.Ollama.Common.Types: [temperature] :: ModelOptions -> Maybe Double
+ Data.Ollama.Common.Types: [thinking] :: Message -> !Maybe Text
+ Data.Ollama.Common.Types: [toolType] :: InputTool -> Text
+ Data.Ollama.Common.Types: [tool_calls] :: Message -> !Maybe [ToolCall]
+ Data.Ollama.Common.Types: [topK] :: ModelOptions -> Maybe Int
+ Data.Ollama.Common.Types: [topP] :: ModelOptions -> Maybe Double
+ Data.Ollama.Common.Types: [totalDuration] :: ChatResponse -> !Maybe Int64
+ Data.Ollama.Common.Types: [typicalP] :: ModelOptions -> Maybe Double
+ Data.Ollama.Common.Types: [useMmap] :: ModelOptions -> Maybe Bool
+ Data.Ollama.Common.Types: class HasDone a
+ Data.Ollama.Common.Types: data ChatResponse
+ Data.Ollama.Common.Types: data FunctionDef
+ Data.Ollama.Common.Types: data FunctionParameters
+ Data.Ollama.Common.Types: data GenerateResponse
+ Data.Ollama.Common.Types: data InputTool
+ Data.Ollama.Common.Types: data Message
+ Data.Ollama.Common.Types: data ModelOptions
+ Data.Ollama.Common.Types: data OutputFunction
+ Data.Ollama.Common.Types: data Role
+ Data.Ollama.Common.Types: getDone :: HasDone a => a -> Bool
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Common.Types.ChatResponse
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Common.Types.FunctionDef
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Common.Types.FunctionParameters
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Common.Types.GenerateResponse
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Common.Types.InputTool
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Common.Types.Message
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Common.Types.OutputFunction
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Common.Types.Role
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Common.Types.ToolCall
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Common.Types.Version
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Common.Types.FunctionDef
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Common.Types.FunctionParameters
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Common.Types.InputTool
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Common.Types.Message
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Common.Types.ModelOptions
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Common.Types.OutputFunction
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Common.Types.Role
+ Data.Ollama.Common.Types: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Common.Types.ToolCall
+ Data.Ollama.Common.Types: instance Data.Ollama.Common.Types.HasDone Data.Ollama.Common.Types.ChatResponse
+ Data.Ollama.Common.Types: instance Data.Ollama.Common.Types.HasDone Data.Ollama.Common.Types.GenerateResponse
+ Data.Ollama.Common.Types: instance GHC.Classes.Eq Data.Ollama.Common.Types.ChatResponse
+ Data.Ollama.Common.Types: instance GHC.Classes.Eq Data.Ollama.Common.Types.FunctionDef
+ Data.Ollama.Common.Types: instance GHC.Classes.Eq Data.Ollama.Common.Types.FunctionParameters
+ Data.Ollama.Common.Types: instance GHC.Classes.Eq Data.Ollama.Common.Types.GenerateResponse
+ Data.Ollama.Common.Types: instance GHC.Classes.Eq Data.Ollama.Common.Types.InputTool
+ Data.Ollama.Common.Types: instance GHC.Classes.Eq Data.Ollama.Common.Types.Message
+ Data.Ollama.Common.Types: instance GHC.Classes.Eq Data.Ollama.Common.Types.ModelOptions
+ Data.Ollama.Common.Types: instance GHC.Classes.Eq Data.Ollama.Common.Types.OutputFunction
+ Data.Ollama.Common.Types: instance GHC.Classes.Eq Data.Ollama.Common.Types.Role
+ Data.Ollama.Common.Types: instance GHC.Classes.Eq Data.Ollama.Common.Types.ToolCall
+ Data.Ollama.Common.Types: instance GHC.Classes.Eq Data.Ollama.Common.Types.Version
+ Data.Ollama.Common.Types: instance GHC.Generics.Generic Data.Ollama.Common.Types.FunctionDef
+ Data.Ollama.Common.Types: instance GHC.Generics.Generic Data.Ollama.Common.Types.InputTool
+ Data.Ollama.Common.Types: instance GHC.Generics.Generic Data.Ollama.Common.Types.Message
+ Data.Ollama.Common.Types: instance GHC.Show.Show Data.Ollama.Common.Types.ChatResponse
+ Data.Ollama.Common.Types: instance GHC.Show.Show Data.Ollama.Common.Types.FunctionDef
+ Data.Ollama.Common.Types: instance GHC.Show.Show Data.Ollama.Common.Types.FunctionParameters
+ Data.Ollama.Common.Types: instance GHC.Show.Show Data.Ollama.Common.Types.GenerateResponse
+ Data.Ollama.Common.Types: instance GHC.Show.Show Data.Ollama.Common.Types.InputTool
+ Data.Ollama.Common.Types: instance GHC.Show.Show Data.Ollama.Common.Types.Message
+ Data.Ollama.Common.Types: instance GHC.Show.Show Data.Ollama.Common.Types.ModelOptions
+ Data.Ollama.Common.Types: instance GHC.Show.Show Data.Ollama.Common.Types.OutputFunction
+ Data.Ollama.Common.Types: instance GHC.Show.Show Data.Ollama.Common.Types.Role
+ Data.Ollama.Common.Types: instance GHC.Show.Show Data.Ollama.Common.Types.ToolCall
+ Data.Ollama.Common.Types: instance GHC.Show.Show Data.Ollama.Common.Types.Version
+ Data.Ollama.Common.Types: newtype ToolCall
+ Data.Ollama.Common.Types: newtype Version
+ Data.Ollama.Common.Utils: commonNonStreamingHandler :: FromJSON a => Response BodyReader -> IO (Either OllamaError a)
+ Data.Ollama.Common.Utils: commonStreamHandler :: (HasDone a, FromJSON a) => (a -> IO ()) -> IO () -> Response BodyReader -> IO (Either OllamaError a)
+ Data.Ollama.Common.Utils: defaultModelOptions :: ModelOptions
+ Data.Ollama.Common.Utils: getVersion :: IO (Either OllamaError Version)
+ Data.Ollama.Common.Utils: nonJsonHandler :: Response BodyReader -> IO (Either OllamaError ByteString)
+ Data.Ollama.Common.Utils: withOllamaRequest :: ToJSON payload => Text -> ByteString -> Maybe payload -> Maybe OllamaConfig -> (Response BodyReader -> IO (Either OllamaError response)) -> IO (Either OllamaError response)
+ Data.Ollama.Common.Utils: withRetry :: Int -> Int -> IO (Either OllamaError a) -> IO (Either OllamaError a)
+ Data.Ollama.Conversation: Conversation :: !Text -> ![Message] -> !Text -> !UTCTime -> !UTCTime -> Conversation
+ Data.Ollama.Conversation: ConvoM :: ReaderT InMemoryStore IO a -> ConvoM a
+ Data.Ollama.Conversation: InMemoryStore :: TVar (Map Text Conversation) -> InMemoryStore
+ Data.Ollama.Conversation: [conversationId] :: Conversation -> !Text
+ Data.Ollama.Conversation: [createdAt] :: Conversation -> !UTCTime
+ Data.Ollama.Conversation: [lastUpdated] :: Conversation -> !UTCTime
+ Data.Ollama.Conversation: [messages] :: Conversation -> ![Message]
+ Data.Ollama.Conversation: [model] :: Conversation -> !Text
+ Data.Ollama.Conversation: [runConvoM] :: ConvoM a -> ReaderT InMemoryStore IO a
+ Data.Ollama.Conversation: class Monad m => ConversationStore (m :: Type -> Type)
+ Data.Ollama.Conversation: data Conversation
+ Data.Ollama.Conversation: deleteConversation :: ConversationStore m => Text -> m Bool
+ Data.Ollama.Conversation: initInMemoryStore :: IO InMemoryStore
+ Data.Ollama.Conversation: instance Control.Monad.IO.Class.MonadIO Data.Ollama.Conversation.ConvoM
+ Data.Ollama.Conversation: instance Control.Monad.Reader.Class.MonadReader Data.Ollama.Conversation.InMemoryStore Data.Ollama.Conversation.ConvoM
+ Data.Ollama.Conversation: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Conversation.Conversation
+ Data.Ollama.Conversation: instance Data.Aeson.Types.ToJSON.ToJSON Data.Ollama.Conversation.Conversation
+ Data.Ollama.Conversation: instance Data.Ollama.Conversation.ConversationStore Data.Ollama.Conversation.ConvoM
+ Data.Ollama.Conversation: instance GHC.Base.Applicative Data.Ollama.Conversation.ConvoM
+ Data.Ollama.Conversation: instance GHC.Base.Functor Data.Ollama.Conversation.ConvoM
+ Data.Ollama.Conversation: instance GHC.Base.Monad Data.Ollama.Conversation.ConvoM
+ Data.Ollama.Conversation: instance GHC.Classes.Eq Data.Ollama.Conversation.Conversation
+ Data.Ollama.Conversation: instance GHC.Generics.Generic Data.Ollama.Conversation.Conversation
+ Data.Ollama.Conversation: instance GHC.Show.Show Data.Ollama.Conversation.Conversation
+ Data.Ollama.Conversation: listConversations :: ConversationStore m => m [Conversation]
+ Data.Ollama.Conversation: loadConversation :: ConversationStore m => Text -> m (Maybe Conversation)
+ Data.Ollama.Conversation: newtype ConvoM a
+ Data.Ollama.Conversation: newtype InMemoryStore
+ Data.Ollama.Conversation: runInMemoryConvo :: InMemoryStore -> ConvoM a -> IO a
+ Data.Ollama.Conversation: saveConversation :: ConversationStore m => Conversation -> m ()
+ Data.Ollama.Conversation: validateConversation :: Conversation -> Either Text Conversation
+ Data.Ollama.Copy: copyModelM :: MonadIO m => Text -> Text -> Maybe OllamaConfig -> m (Either OllamaError ())
+ Data.Ollama.Create: createModelM :: MonadIO m => Text -> Maybe Text -> Maybe Bool -> Maybe FilePath -> Maybe OllamaConfig -> m ()
+ Data.Ollama.Create: instance Data.Ollama.Common.Types.HasDone Data.Ollama.Create.CreateModelResp
+ Data.Ollama.Delete: deleteModelM :: MonadIO m => Text -> Maybe OllamaConfig -> m (Either OllamaError ())
+ Data.Ollama.Embeddings: ModelOptions :: Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Int -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Bool -> Maybe [Text] -> Maybe Bool -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Bool -> Maybe Int -> ModelOptions
+ Data.Ollama.Embeddings: [frequencyPenalty] :: ModelOptions -> Maybe Double
+ Data.Ollama.Embeddings: [input] :: EmbeddingOps -> ![Text]
+ Data.Ollama.Embeddings: [keepAliveEmbed] :: EmbeddingOps -> !Maybe Int
+ Data.Ollama.Embeddings: [mainGpu] :: ModelOptions -> Maybe Int
+ Data.Ollama.Embeddings: [minP] :: ModelOptions -> Maybe Double
+ Data.Ollama.Embeddings: [modelOptions] :: EmbeddingOps -> !Maybe ModelOptions
+ Data.Ollama.Embeddings: [model] :: EmbeddingOps -> !Text
+ Data.Ollama.Embeddings: [numBatch] :: ModelOptions -> Maybe Int
+ Data.Ollama.Embeddings: [numCtx] :: ModelOptions -> Maybe Int
+ Data.Ollama.Embeddings: [numGpu] :: ModelOptions -> Maybe Int
+ Data.Ollama.Embeddings: [numKeep] :: ModelOptions -> Maybe Int
+ Data.Ollama.Embeddings: [numPredict] :: ModelOptions -> Maybe Int
+ Data.Ollama.Embeddings: [numThread] :: ModelOptions -> Maybe Int
+ Data.Ollama.Embeddings: [numa] :: ModelOptions -> Maybe Bool
+ Data.Ollama.Embeddings: [penalizeNewline] :: ModelOptions -> Maybe Bool
+ Data.Ollama.Embeddings: [presencePenalty] :: ModelOptions -> Maybe Double
+ Data.Ollama.Embeddings: [repeatLastN] :: ModelOptions -> Maybe Int
+ Data.Ollama.Embeddings: [repeatPenalty] :: ModelOptions -> Maybe Double
+ Data.Ollama.Embeddings: [respondedEmbeddings] :: EmbeddingResp -> ![[Float]]
+ Data.Ollama.Embeddings: [respondedModel] :: EmbeddingResp -> !Text
+ Data.Ollama.Embeddings: [seed] :: ModelOptions -> Maybe Int
+ Data.Ollama.Embeddings: [stop] :: ModelOptions -> Maybe [Text]
+ Data.Ollama.Embeddings: [temperature] :: ModelOptions -> Maybe Double
+ Data.Ollama.Embeddings: [topK] :: ModelOptions -> Maybe Int
+ Data.Ollama.Embeddings: [topP] :: ModelOptions -> Maybe Double
+ Data.Ollama.Embeddings: [truncateInput] :: EmbeddingOps -> !Maybe Bool
+ Data.Ollama.Embeddings: [typicalP] :: ModelOptions -> Maybe Double
+ Data.Ollama.Embeddings: [useMmap] :: ModelOptions -> Maybe Bool
+ Data.Ollama.Embeddings: data ModelOptions
+ Data.Ollama.Embeddings: defaultEmbeddingOps :: EmbeddingOps
+ Data.Ollama.Embeddings: defaultModelOptions :: ModelOptions
+ Data.Ollama.Embeddings: embeddingM :: MonadIO m => Text -> [Text] -> m (Either OllamaError EmbeddingResp)
+ Data.Ollama.Embeddings: embeddingOpsM :: MonadIO m => Text -> [Text] -> Maybe Bool -> Maybe Int -> Maybe ModelOptions -> Maybe OllamaConfig -> m (Either OllamaError EmbeddingResp)
+ Data.Ollama.Generate: ApiError :: Text -> OllamaError
+ Data.Ollama.Generate: DecodeError :: DecodingErrorMessage -> DecodingFailedValue -> OllamaError
+ Data.Ollama.Generate: FileError :: IOException -> OllamaError
+ Data.Ollama.Generate: HttpError :: HttpException -> OllamaError
+ Data.Ollama.Generate: InvalidRequest :: String -> OllamaError
+ Data.Ollama.Generate: JsonFormat :: Format
+ Data.Ollama.Generate: JsonSchemaError :: String -> OllamaError
+ Data.Ollama.Generate: ModelOptions :: Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Int -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Bool -> Maybe [Text] -> Maybe Bool -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Bool -> Maybe Int -> ModelOptions
+ Data.Ollama.Generate: OllamaConfig :: Text -> Int -> Maybe (IO ()) -> Maybe (IO ()) -> Maybe (IO ()) -> Maybe Int -> Maybe Int -> Maybe Manager -> OllamaConfig
+ Data.Ollama.Generate: SchemaFormat :: Schema -> Format
+ Data.Ollama.Generate: TimeoutError :: String -> OllamaError
+ Data.Ollama.Generate: [commonManager] :: OllamaConfig -> Maybe Manager
+ Data.Ollama.Generate: [createdAt] :: GenerateResponse -> !UTCTime
+ Data.Ollama.Generate: [done] :: GenerateResponse -> !Bool
+ Data.Ollama.Generate: [evalCount] :: GenerateResponse -> !Maybe Int64
+ Data.Ollama.Generate: [evalDuration] :: GenerateResponse -> !Maybe Int64
+ Data.Ollama.Generate: [format] :: GenerateOps -> !Maybe Format
+ Data.Ollama.Generate: [frequencyPenalty] :: ModelOptions -> Maybe Double
+ Data.Ollama.Generate: [genResponse] :: GenerateResponse -> !Text
+ Data.Ollama.Generate: [hostUrl] :: OllamaConfig -> Text
+ Data.Ollama.Generate: [images] :: GenerateOps -> !Maybe [Text]
+ Data.Ollama.Generate: [keepAlive] :: GenerateOps -> !Maybe Int
+ Data.Ollama.Generate: [loadDuration] :: GenerateResponse -> !Maybe Int64
+ Data.Ollama.Generate: [mainGpu] :: ModelOptions -> Maybe Int
+ Data.Ollama.Generate: [minP] :: ModelOptions -> Maybe Double
+ Data.Ollama.Generate: [modelName] :: GenerateOps -> !Text
+ Data.Ollama.Generate: [model] :: GenerateResponse -> !Text
+ Data.Ollama.Generate: [numBatch] :: ModelOptions -> Maybe Int
+ Data.Ollama.Generate: [numCtx] :: ModelOptions -> Maybe Int
+ Data.Ollama.Generate: [numGpu] :: ModelOptions -> Maybe Int
+ Data.Ollama.Generate: [numKeep] :: ModelOptions -> Maybe Int
+ Data.Ollama.Generate: [numPredict] :: ModelOptions -> Maybe Int
+ Data.Ollama.Generate: [numThread] :: ModelOptions -> Maybe Int
+ Data.Ollama.Generate: [numa] :: ModelOptions -> Maybe Bool
+ Data.Ollama.Generate: [onModelError] :: OllamaConfig -> Maybe (IO ())
+ Data.Ollama.Generate: [onModelFinish] :: OllamaConfig -> Maybe (IO ())
+ Data.Ollama.Generate: [onModelStart] :: OllamaConfig -> Maybe (IO ())
+ Data.Ollama.Generate: [options] :: GenerateOps -> !Maybe ModelOptions
+ Data.Ollama.Generate: [penalizeNewline] :: ModelOptions -> Maybe Bool
+ Data.Ollama.Generate: [presencePenalty] :: ModelOptions -> Maybe Double
+ Data.Ollama.Generate: [promptEvalCount] :: GenerateResponse -> !Maybe Int64
+ Data.Ollama.Generate: [promptEvalDuration] :: GenerateResponse -> !Maybe Int64
+ Data.Ollama.Generate: [prompt] :: GenerateOps -> !Text
+ Data.Ollama.Generate: [raw] :: GenerateOps -> !Maybe Bool
+ Data.Ollama.Generate: [repeatLastN] :: ModelOptions -> Maybe Int
+ Data.Ollama.Generate: [repeatPenalty] :: ModelOptions -> Maybe Double
+ Data.Ollama.Generate: [retryCount] :: OllamaConfig -> Maybe Int
+ Data.Ollama.Generate: [retryDelay] :: OllamaConfig -> Maybe Int
+ Data.Ollama.Generate: [seed] :: ModelOptions -> Maybe Int
+ Data.Ollama.Generate: [stop] :: ModelOptions -> Maybe [Text]
+ Data.Ollama.Generate: [stream] :: GenerateOps -> !Maybe (GenerateResponse -> IO (), IO ())
+ Data.Ollama.Generate: [suffix] :: GenerateOps -> Maybe Text
+ Data.Ollama.Generate: [system] :: GenerateOps -> !Maybe Text
+ Data.Ollama.Generate: [temperature] :: ModelOptions -> Maybe Double
+ Data.Ollama.Generate: [template] :: GenerateOps -> !Maybe Text
+ Data.Ollama.Generate: [think] :: GenerateOps -> !Maybe Bool
+ Data.Ollama.Generate: [thinking] :: GenerateResponse -> !Maybe Text
+ Data.Ollama.Generate: [timeout] :: OllamaConfig -> Int
+ Data.Ollama.Generate: [topK] :: ModelOptions -> Maybe Int
+ Data.Ollama.Generate: [topP] :: ModelOptions -> Maybe Double
+ Data.Ollama.Generate: [totalDuration] :: GenerateResponse -> !Maybe Int64
+ Data.Ollama.Generate: [typicalP] :: ModelOptions -> Maybe Double
+ Data.Ollama.Generate: [useMmap] :: ModelOptions -> Maybe Bool
+ Data.Ollama.Generate: data Format
+ Data.Ollama.Generate: data ModelOptions
+ Data.Ollama.Generate: data OllamaConfig
+ Data.Ollama.Generate: data OllamaError
+ Data.Ollama.Generate: defaultModelOptions :: ModelOptions
+ Data.Ollama.Generate: defaultOllamaConfig :: OllamaConfig
+ Data.Ollama.Generate: generateM :: MonadIO m => GenerateOps -> Maybe OllamaConfig -> m (Either OllamaError GenerateResponse)
+ Data.Ollama.Generate: validateGenerateOps :: GenerateOps -> Either OllamaError GenerateOps
+ Data.Ollama.List: listM :: MonadIO m => Maybe OllamaConfig -> m (Either OllamaError Models)
+ Data.Ollama.Load: loadGenModel :: Text -> IO (Either OllamaError ())
+ Data.Ollama.Load: loadGenModelM :: MonadIO m => Text -> m (Either OllamaError ())
+ Data.Ollama.Load: unloadGenModel :: Text -> IO (Either OllamaError ())
+ Data.Ollama.Load: unloadGenModelM :: MonadIO m => Text -> m (Either OllamaError ())
+ Data.Ollama.Ps: psM :: MonadIO m => Maybe OllamaConfig -> m (Either OllamaError RunningModels)
+ Data.Ollama.Pull: instance Data.Ollama.Common.Types.HasDone Data.Ollama.Pull.PullResp
+ Data.Ollama.Pull: pullM :: MonadIO m => Text -> m (Either OllamaError PullResp)
+ Data.Ollama.Pull: pullOpsM :: MonadIO m => Text -> Maybe Bool -> Maybe Bool -> Maybe OllamaConfig -> m (Either OllamaError PullResp)
+ Data.Ollama.Push: instance Data.Ollama.Common.Types.HasDone Data.Ollama.Push.PushResp
+ Data.Ollama.Push: pushM :: MonadIO m => Text -> Maybe Bool -> Maybe Bool -> Maybe OllamaConfig -> m ()
+ Data.Ollama.Show: ModelDetails :: !Maybe Text -> !Text -> !Text -> ![Text] -> !Text -> Text -> ModelDetails
+ Data.Ollama.Show: ShowModelInfo :: !Maybe Text -> !Maybe Int -> !Maybe Int64 -> !Maybe Int -> !Maybe Int -> !Maybe Int -> !Maybe Float -> !Maybe Int -> !Maybe Int -> !Maybe Int -> !Maybe Int -> !Maybe Int -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int -> !Maybe Int -> !Maybe [Text] -> !Maybe Text -> !Maybe Text -> !Maybe [Text] -> !Maybe [Text] -> ShowModelInfo
+ Data.Ollama.Show: [capabilities] :: ShowModelResponse -> Maybe [Text]
+ Data.Ollama.Show: [details] :: ShowModelResponse -> !ModelDetails
+ Data.Ollama.Show: [families] :: ModelDetails -> ![Text]
+ Data.Ollama.Show: [family] :: ModelDetails -> !Text
+ Data.Ollama.Show: [format] :: ModelDetails -> !Text
+ Data.Ollama.Show: [generalArchitecture] :: ShowModelInfo -> !Maybe Text
+ Data.Ollama.Show: [generalFileType] :: ShowModelInfo -> !Maybe Int
+ Data.Ollama.Show: [generalParameterCount] :: ShowModelInfo -> !Maybe Int64
+ Data.Ollama.Show: [generalQuantizationVersion] :: ShowModelInfo -> !Maybe Int
+ Data.Ollama.Show: [license] :: ShowModelResponse -> !Maybe Text
+ Data.Ollama.Show: [llamaAttentionHeadCountKV] :: ShowModelInfo -> !Maybe Int
+ Data.Ollama.Show: [llamaAttentionHeadCount] :: ShowModelInfo -> !Maybe Int
+ Data.Ollama.Show: [llamaAttentionLayerNormRMSEpsilon] :: ShowModelInfo -> !Maybe Float
+ Data.Ollama.Show: [llamaBlockCount] :: ShowModelInfo -> !Maybe Int
+ Data.Ollama.Show: [llamaContextLength] :: ShowModelInfo -> !Maybe Int
+ Data.Ollama.Show: [llamaEmbeddingLength] :: ShowModelInfo -> !Maybe Int
+ Data.Ollama.Show: [llamaFeedForwardLength] :: ShowModelInfo -> !Maybe Int
+ Data.Ollama.Show: [llamaRopeDimensionCount] :: ShowModelInfo -> !Maybe Int
+ Data.Ollama.Show: [llamaRopeFreqBase] :: ShowModelInfo -> !Maybe Int64
+ Data.Ollama.Show: [llamaVocabSize] :: ShowModelInfo -> !Maybe Int64
+ Data.Ollama.Show: [modelFile] :: ShowModelResponse -> !Text
+ Data.Ollama.Show: [modelInfo] :: ShowModelResponse -> !ShowModelInfo
+ Data.Ollama.Show: [parameterSize] :: ModelDetails -> !Text
+ Data.Ollama.Show: [parameters] :: ShowModelResponse -> !Maybe Text
+ Data.Ollama.Show: [parentModel] :: ModelDetails -> !Maybe Text
+ Data.Ollama.Show: [quantizationLevel] :: ModelDetails -> Text
+ Data.Ollama.Show: [template] :: ShowModelResponse -> !Maybe Text
+ Data.Ollama.Show: [tokenizerGgmlBosToken_id] :: ShowModelInfo -> !Maybe Int
+ Data.Ollama.Show: [tokenizerGgmlEosToken_id] :: ShowModelInfo -> !Maybe Int
+ Data.Ollama.Show: [tokenizerGgmlMerges] :: ShowModelInfo -> !Maybe [Text]
+ Data.Ollama.Show: [tokenizerGgmlMode] :: ShowModelInfo -> !Maybe Text
+ Data.Ollama.Show: [tokenizerGgmlPre] :: ShowModelInfo -> !Maybe Text
+ Data.Ollama.Show: [tokenizerGgmlTokenType] :: ShowModelInfo -> !Maybe [Text]
+ Data.Ollama.Show: [tokenizerGgmlTokens] :: ShowModelInfo -> !Maybe [Text]
+ Data.Ollama.Show: data ModelDetails
+ Data.Ollama.Show: data ShowModelInfo
+ Data.Ollama.Show: instance Data.Aeson.Types.FromJSON.FromJSON Data.Ollama.Show.ShowModelInfo
+ Data.Ollama.Show: instance GHC.Classes.Eq Data.Ollama.Show.ShowModelInfo
+ Data.Ollama.Show: instance GHC.Show.Show Data.Ollama.Show.ShowModelInfo
+ Data.Ollama.Show: showModelM :: MonadIO m => Text -> m (Either OllamaError ShowModelResponse)
+ Data.Ollama.Show: showModelOpsM :: MonadIO m => Text -> Maybe Bool -> Maybe OllamaConfig -> m (Either OllamaError ShowModelResponse)
+ Ollama: ApiError :: Text -> OllamaError
+ Ollama: DecodeError :: DecodingErrorMessage -> DecodingFailedValue -> OllamaError
+ Ollama: FileError :: IOException -> OllamaError
+ Ollama: FunctionDef :: Text -> Maybe Text -> Maybe FunctionParameters -> Maybe Bool -> FunctionDef
+ Ollama: FunctionParameters :: Text -> Maybe (Map Text FunctionParameters) -> Maybe [Text] -> Maybe Bool -> FunctionParameters
+ Ollama: HttpError :: HttpException -> OllamaError
+ Ollama: InputTool :: Text -> FunctionDef -> InputTool
+ Ollama: InvalidRequest :: String -> OllamaError
+ Ollama: JsonSchemaError :: String -> OllamaError
+ Ollama: ModelDetails :: !Maybe Text -> !Text -> !Text -> ![Text] -> !Text -> Text -> ModelDetails
+ Ollama: ModelOptions :: Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Int -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Double -> Maybe Bool -> Maybe [Text] -> Maybe Bool -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Bool -> Maybe Int -> ModelOptions
+ Ollama: OllamaConfig :: Text -> Int -> Maybe (IO ()) -> Maybe (IO ()) -> Maybe (IO ()) -> Maybe Int -> Maybe Int -> Maybe Manager -> OllamaConfig
+ Ollama: OutputFunction :: Text -> Map Text Value -> OutputFunction
+ Ollama: ShowModelInfo :: !Maybe Text -> !Maybe Int -> !Maybe Int64 -> !Maybe Int -> !Maybe Int -> !Maybe Int -> !Maybe Float -> !Maybe Int -> !Maybe Int -> !Maybe Int -> !Maybe Int -> !Maybe Int -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int -> !Maybe Int -> !Maybe [Text] -> !Maybe Text -> !Maybe Text -> !Maybe [Text] -> !Maybe [Text] -> ShowModelInfo
+ Ollama: TimeoutError :: String -> OllamaError
+ Ollama: ToolCall :: OutputFunction -> ToolCall
+ Ollama: Version :: Text -> Version
+ Ollama: [additionalProperties] :: FunctionParameters -> Maybe Bool
+ Ollama: [arguments] :: OutputFunction -> Map Text Value
+ Ollama: [capabilities] :: ShowModelResponse -> Maybe [Text]
+ Ollama: [commonManager] :: OllamaConfig -> Maybe Manager
+ Ollama: [families] :: ModelDetails -> ![Text]
+ Ollama: [family] :: ModelDetails -> !Text
+ Ollama: [frequencyPenalty] :: ModelOptions -> Maybe Double
+ Ollama: [functionDescription] :: FunctionDef -> Maybe Text
+ Ollama: [functionName] :: FunctionDef -> Text
+ Ollama: [functionParameters] :: FunctionDef -> Maybe FunctionParameters
+ Ollama: [functionStrict] :: FunctionDef -> Maybe Bool
+ Ollama: [function] :: InputTool -> FunctionDef
+ Ollama: [genResponse] :: GenerateResponse -> !Text
+ Ollama: [generalArchitecture] :: ShowModelInfo -> !Maybe Text
+ Ollama: [generalFileType] :: ShowModelInfo -> !Maybe Int
+ Ollama: [generalParameterCount] :: ShowModelInfo -> !Maybe Int64
+ Ollama: [generalQuantizationVersion] :: ShowModelInfo -> !Maybe Int
+ Ollama: [input] :: EmbeddingOps -> ![Text]
+ Ollama: [keepAliveEmbed] :: EmbeddingOps -> !Maybe Int
+ Ollama: [license] :: ShowModelResponse -> !Maybe Text
+ Ollama: [llamaAttentionHeadCountKV] :: ShowModelInfo -> !Maybe Int
+ Ollama: [llamaAttentionHeadCount] :: ShowModelInfo -> !Maybe Int
+ Ollama: [llamaAttentionLayerNormRMSEpsilon] :: ShowModelInfo -> !Maybe Float
+ Ollama: [llamaBlockCount] :: ShowModelInfo -> !Maybe Int
+ Ollama: [llamaContextLength] :: ShowModelInfo -> !Maybe Int
+ Ollama: [llamaEmbeddingLength] :: ShowModelInfo -> !Maybe Int
+ Ollama: [llamaFeedForwardLength] :: ShowModelInfo -> !Maybe Int
+ Ollama: [llamaRopeDimensionCount] :: ShowModelInfo -> !Maybe Int
+ Ollama: [llamaRopeFreqBase] :: ShowModelInfo -> !Maybe Int64
+ Ollama: [llamaVocabSize] :: ShowModelInfo -> !Maybe Int64
+ Ollama: [mainGpu] :: ModelOptions -> Maybe Int
+ Ollama: [minP] :: ModelOptions -> Maybe Double
+ Ollama: [modelFile] :: ShowModelResponse -> !Text
+ Ollama: [modelInfo] :: ShowModelResponse -> !ShowModelInfo
+ Ollama: [modelOptions] :: EmbeddingOps -> !Maybe ModelOptions
+ Ollama: [numBatch] :: ModelOptions -> Maybe Int
+ Ollama: [numCtx] :: ModelOptions -> Maybe Int
+ Ollama: [numGpu] :: ModelOptions -> Maybe Int
+ Ollama: [numKeep] :: ModelOptions -> Maybe Int
+ Ollama: [numPredict] :: ModelOptions -> Maybe Int
+ Ollama: [numThread] :: ModelOptions -> Maybe Int
+ Ollama: [numa] :: ModelOptions -> Maybe Bool
+ Ollama: [onModelError] :: OllamaConfig -> Maybe (IO ())
+ Ollama: [onModelFinish] :: OllamaConfig -> Maybe (IO ())
+ Ollama: [onModelStart] :: OllamaConfig -> Maybe (IO ())
+ Ollama: [outputFunctionName] :: OutputFunction -> Text
+ Ollama: [outputFunction] :: ToolCall -> OutputFunction
+ Ollama: [parameterProperties] :: FunctionParameters -> Maybe (Map Text FunctionParameters)
+ Ollama: [parameterSize] :: ModelDetails -> !Text
+ Ollama: [parameterType] :: FunctionParameters -> Text
+ Ollama: [parameters] :: ShowModelResponse -> !Maybe Text
+ Ollama: [parentModel] :: ModelDetails -> !Maybe Text
+ Ollama: [penalizeNewline] :: ModelOptions -> Maybe Bool
+ Ollama: [presencePenalty] :: ModelOptions -> Maybe Double
+ Ollama: [prompt] :: GenerateOps -> !Text
+ Ollama: [quantizationLevel] :: ModelDetails -> Text
+ Ollama: [raw] :: GenerateOps -> !Maybe Bool
+ Ollama: [repeatLastN] :: ModelOptions -> Maybe Int
+ Ollama: [repeatPenalty] :: ModelOptions -> Maybe Double
+ Ollama: [requiredParams] :: FunctionParameters -> Maybe [Text]
+ Ollama: [respondedEmbeddings] :: EmbeddingResp -> ![[Float]]
+ Ollama: [respondedModel] :: EmbeddingResp -> !Text
+ Ollama: [retryCount] :: OllamaConfig -> Maybe Int
+ Ollama: [retryDelay] :: OllamaConfig -> Maybe Int
+ Ollama: [seed] :: ModelOptions -> Maybe Int
+ Ollama: [stop] :: ModelOptions -> Maybe [Text]
+ Ollama: [suffix] :: GenerateOps -> Maybe Text
+ Ollama: [system] :: GenerateOps -> !Maybe Text
+ Ollama: [temperature] :: ModelOptions -> Maybe Double
+ Ollama: [template] :: ShowModelResponse -> !Maybe Text
+ Ollama: [think] :: ChatOps -> !Maybe Bool
+ Ollama: [thinking] :: Message -> !Maybe Text
+ Ollama: [timeout] :: OllamaConfig -> Int
+ Ollama: [tokenizerGgmlBosToken_id] :: ShowModelInfo -> !Maybe Int
+ Ollama: [tokenizerGgmlEosToken_id] :: ShowModelInfo -> !Maybe Int
+ Ollama: [tokenizerGgmlMerges] :: ShowModelInfo -> !Maybe [Text]
+ Ollama: [tokenizerGgmlMode] :: ShowModelInfo -> !Maybe Text
+ Ollama: [tokenizerGgmlPre] :: ShowModelInfo -> !Maybe Text
+ Ollama: [tokenizerGgmlTokenType] :: ShowModelInfo -> !Maybe [Text]
+ Ollama: [tokenizerGgmlTokens] :: ShowModelInfo -> !Maybe [Text]
+ Ollama: [toolType] :: InputTool -> Text
+ Ollama: [topK] :: ModelOptions -> Maybe Int
+ Ollama: [topP] :: ModelOptions -> Maybe Double
+ Ollama: [truncateInput] :: EmbeddingOps -> !Maybe Bool
+ Ollama: [typicalP] :: ModelOptions -> Maybe Double
+ Ollama: [useMmap] :: ModelOptions -> Maybe Bool
+ Ollama: chatM :: MonadIO m => ChatOps -> Maybe OllamaConfig -> m (Either OllamaError ChatResponse)
+ Ollama: copyModelM :: MonadIO m => Text -> Text -> Maybe OllamaConfig -> m (Either OllamaError ())
+ Ollama: createModelM :: MonadIO m => Text -> Maybe Text -> Maybe Bool -> Maybe FilePath -> Maybe OllamaConfig -> m ()
+ Ollama: data FunctionDef
+ Ollama: data FunctionParameters
+ Ollama: data InputTool
+ Ollama: data ModelDetails
+ Ollama: data ModelOptions
+ Ollama: data OllamaConfig
+ Ollama: data OllamaError
+ Ollama: data OutputFunction
+ Ollama: data ShowModelInfo
+ Ollama: defaultModelOptions :: ModelOptions
+ Ollama: defaultOllamaConfig :: OllamaConfig
+ Ollama: deleteModelM :: MonadIO m => Text -> Maybe OllamaConfig -> m (Either OllamaError ())
+ Ollama: embeddingM :: MonadIO m => Text -> [Text] -> m (Either OllamaError EmbeddingResp)
+ Ollama: embeddingOpsM :: MonadIO m => Text -> [Text] -> Maybe Bool -> Maybe Int -> Maybe ModelOptions -> Maybe OllamaConfig -> m (Either OllamaError EmbeddingResp)
+ Ollama: encodeImage :: FilePath -> IO (Maybe Text)
+ Ollama: generateM :: MonadIO m => GenerateOps -> Maybe OllamaConfig -> m (Either OllamaError GenerateResponse)
+ Ollama: getVersion :: IO (Either OllamaError Version)
+ Ollama: loadGenModel :: Text -> IO (Either OllamaError ())
+ Ollama: loadGenModelM :: MonadIO m => Text -> m (Either OllamaError ())
+ Ollama: newtype ToolCall
+ Ollama: newtype Version
+ Ollama: psM :: MonadIO m => Maybe OllamaConfig -> m (Either OllamaError RunningModels)
+ Ollama: pullM :: MonadIO m => Text -> m (Either OllamaError PullResp)
+ Ollama: pullOpsM :: MonadIO m => Text -> Maybe Bool -> Maybe Bool -> Maybe OllamaConfig -> m (Either OllamaError PullResp)
+ Ollama: pushM :: MonadIO m => Text -> Maybe Bool -> Maybe Bool -> Maybe OllamaConfig -> m ()
+ Ollama: showModelM :: MonadIO m => Text -> m (Either OllamaError ShowModelResponse)
+ Ollama: showModelOpsM :: MonadIO m => Text -> Maybe Bool -> Maybe OllamaConfig -> m (Either OllamaError ShowModelResponse)
+ Ollama: unloadGenModel :: Text -> IO (Either OllamaError ())
+ Ollama: unloadGenModelM :: MonadIO m => Text -> m (Either OllamaError ())
+ Ollama: withOllamaRequest :: ToJSON payload => Text -> ByteString -> Maybe payload -> Maybe OllamaConfig -> (Response BodyReader -> IO (Either OllamaError response)) -> IO (Either OllamaError response)
+ Ollama: withOnModelError :: IO () -> OllamaConfig -> OllamaConfig
+ Ollama: withOnModelFinish :: IO () -> OllamaConfig -> OllamaConfig
+ Ollama: withOnModelStart :: IO () -> OllamaConfig -> OllamaConfig
- Data.Ollama.Chat: ChatOps :: Text -> NonEmpty Message -> Maybe [Value] -> Maybe Format -> Maybe (ChatResponse -> IO (), IO ()) -> Maybe Text -> Maybe Text -> Maybe Int -> Maybe Value -> ChatOps
+ Data.Ollama.Chat: ChatOps :: !Text -> !NonEmpty Message -> !Maybe [InputTool] -> !Maybe Format -> !Maybe (ChatResponse -> IO (), IO ()) -> !Maybe Int -> !Maybe ModelOptions -> !Maybe Bool -> ChatOps
- Data.Ollama.Chat: ChatResponse :: Text -> UTCTime -> Maybe Message -> Bool -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> ChatResponse
+ Data.Ollama.Chat: ChatResponse :: !Text -> !UTCTime -> !Maybe Message -> !Bool -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> ChatResponse
- Data.Ollama.Chat: Message :: Role -> Text -> Maybe [Text] -> Maybe [Value] -> Message
+ Data.Ollama.Chat: Message :: !Role -> !Text -> !Maybe [Text] -> !Maybe [ToolCall] -> !Maybe Text -> Message
- Data.Ollama.Chat: SchemaFormat :: Value -> Format
+ Data.Ollama.Chat: SchemaFormat :: Schema -> Format
- Data.Ollama.Chat: [chatModelName] :: ChatOps -> Text
+ Data.Ollama.Chat: [chatModelName] :: ChatOps -> !Text
- Data.Ollama.Chat: [content] :: Message -> Text
+ Data.Ollama.Chat: [content] :: Message -> !Text
- Data.Ollama.Chat: [createdAt] :: ChatResponse -> UTCTime
+ Data.Ollama.Chat: [createdAt] :: ChatResponse -> !UTCTime
- Data.Ollama.Chat: [done] :: ChatResponse -> Bool
+ Data.Ollama.Chat: [done] :: ChatResponse -> !Bool
- Data.Ollama.Chat: [evalCount] :: ChatResponse -> Maybe Int64
+ Data.Ollama.Chat: [evalCount] :: ChatResponse -> !Maybe Int64
- Data.Ollama.Chat: [evalDuration] :: ChatResponse -> Maybe Int64
+ Data.Ollama.Chat: [evalDuration] :: ChatResponse -> !Maybe Int64
- Data.Ollama.Chat: [format] :: ChatOps -> Maybe Format
+ Data.Ollama.Chat: [format] :: ChatOps -> !Maybe Format
- Data.Ollama.Chat: [hostUrl] :: ChatOps -> Maybe Text
+ Data.Ollama.Chat: [hostUrl] :: OllamaConfig -> Text
- Data.Ollama.Chat: [images] :: Message -> Maybe [Text]
+ Data.Ollama.Chat: [images] :: Message -> !Maybe [Text]
- Data.Ollama.Chat: [keepAlive] :: ChatOps -> Maybe Text
+ Data.Ollama.Chat: [keepAlive] :: ChatOps -> !Maybe Int
- Data.Ollama.Chat: [loadDuration] :: ChatResponse -> Maybe Int64
+ Data.Ollama.Chat: [loadDuration] :: ChatResponse -> !Maybe Int64
- Data.Ollama.Chat: [message] :: ChatResponse -> Maybe Message
+ Data.Ollama.Chat: [message] :: ChatResponse -> !Maybe Message
- Data.Ollama.Chat: [messages] :: ChatOps -> NonEmpty Message
+ Data.Ollama.Chat: [messages] :: ChatOps -> !NonEmpty Message
- Data.Ollama.Chat: [model] :: ChatResponse -> Text
+ Data.Ollama.Chat: [model] :: ChatResponse -> !Text
- Data.Ollama.Chat: [options] :: ChatOps -> Maybe Value
+ Data.Ollama.Chat: [options] :: ChatOps -> !Maybe ModelOptions
- Data.Ollama.Chat: [promptEvalCount] :: ChatResponse -> Maybe Int64
+ Data.Ollama.Chat: [promptEvalCount] :: ChatResponse -> !Maybe Int64
- Data.Ollama.Chat: [promptEvalDuration] :: ChatResponse -> Maybe Int64
+ Data.Ollama.Chat: [promptEvalDuration] :: ChatResponse -> !Maybe Int64
- Data.Ollama.Chat: [role] :: Message -> Role
+ Data.Ollama.Chat: [role] :: Message -> !Role
- Data.Ollama.Chat: [stream] :: ChatOps -> Maybe (ChatResponse -> IO (), IO ())
+ Data.Ollama.Chat: [stream] :: ChatOps -> !Maybe (ChatResponse -> IO (), IO ())
- Data.Ollama.Chat: [tool_calls] :: Message -> Maybe [Value]
+ Data.Ollama.Chat: [tool_calls] :: Message -> !Maybe [ToolCall]
- Data.Ollama.Chat: [tools] :: ChatOps -> Maybe [Value]
+ Data.Ollama.Chat: [tools] :: ChatOps -> !Maybe [InputTool]
- Data.Ollama.Chat: [totalDuration] :: ChatResponse -> Maybe Int64
+ Data.Ollama.Chat: [totalDuration] :: ChatResponse -> !Maybe Int64
- Data.Ollama.Chat: chat :: ChatOps -> IO (Either String ChatResponse)
+ Data.Ollama.Chat: chat :: ChatOps -> Maybe OllamaConfig -> IO (Either OllamaError ChatResponse)
- Data.Ollama.Common.Types: ModelDetails :: Maybe Text -> Text -> Text -> [Text] -> Text -> Text -> ModelDetails
+ Data.Ollama.Common.Types: ModelDetails :: !Maybe Text -> !Text -> !Text -> ![Text] -> !Text -> Text -> ModelDetails
- Data.Ollama.Common.Types: SchemaFormat :: Value -> Format
+ Data.Ollama.Common.Types: SchemaFormat :: Schema -> Format
- Data.Ollama.Copy: copyModel :: Text -> Text -> IO ()
+ Data.Ollama.Copy: copyModel :: Text -> Text -> Maybe OllamaConfig -> IO (Either OllamaError ())
- Data.Ollama.Create: createModel :: Text -> Maybe Text -> Maybe FilePath -> IO ()
+ Data.Ollama.Create: createModel :: Text -> Maybe Text -> Maybe Bool -> Maybe FilePath -> Maybe OllamaConfig -> IO ()
- Data.Ollama.Delete: deleteModel :: Text -> IO ()
+ Data.Ollama.Delete: deleteModel :: Text -> Maybe OllamaConfig -> IO (Either OllamaError ())
- Data.Ollama.Embeddings: EmbeddingOps :: Text -> Text -> Maybe Bool -> Maybe Text -> EmbeddingOps
+ Data.Ollama.Embeddings: EmbeddingOps :: !Text -> ![Text] -> !Maybe Bool -> !Maybe Int -> !Maybe ModelOptions -> EmbeddingOps
- Data.Ollama.Embeddings: EmbeddingResp :: Text -> [[Float]] -> EmbeddingResp
+ Data.Ollama.Embeddings: EmbeddingResp :: !Text -> ![[Float]] -> EmbeddingResp
- Data.Ollama.Embeddings: embedding :: Text -> Text -> IO (Either String EmbeddingResp)
+ Data.Ollama.Embeddings: embedding :: Text -> [Text] -> IO (Either OllamaError EmbeddingResp)
- Data.Ollama.Embeddings: embeddingOps :: Text -> Text -> Maybe Bool -> Maybe Text -> IO (Either String EmbeddingResp)
+ Data.Ollama.Embeddings: embeddingOps :: Text -> [Text] -> Maybe Bool -> Maybe Int -> Maybe ModelOptions -> Maybe OllamaConfig -> IO (Either OllamaError EmbeddingResp)
- Data.Ollama.Generate: GenerateOps :: Text -> Text -> Maybe Text -> Maybe [Text] -> Maybe Format -> Maybe Text -> Maybe Text -> Maybe (GenerateResponse -> IO (), IO ()) -> Maybe Bool -> Maybe Text -> Maybe Text -> Maybe Int -> Maybe Value -> GenerateOps
+ Data.Ollama.Generate: GenerateOps :: !Text -> !Text -> Maybe Text -> !Maybe [Text] -> !Maybe Format -> !Maybe Text -> !Maybe Text -> !Maybe (GenerateResponse -> IO (), IO ()) -> !Maybe Bool -> !Maybe Int -> !Maybe ModelOptions -> !Maybe Bool -> GenerateOps
- Data.Ollama.Generate: GenerateResponse :: Text -> UTCTime -> Text -> Bool -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> GenerateResponse
+ Data.Ollama.Generate: GenerateResponse :: !Text -> !UTCTime -> !Text -> !Bool -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Text -> GenerateResponse
- Data.Ollama.Generate: generate :: GenerateOps -> IO (Either String GenerateResponse)
+ Data.Ollama.Generate: generate :: GenerateOps -> Maybe OllamaConfig -> IO (Either OllamaError GenerateResponse)
- Data.Ollama.List: ModelInfo :: Text -> UTCTime -> Int64 -> Text -> ModelDetails -> ModelInfo
+ Data.Ollama.List: ModelInfo :: !Text -> !UTCTime -> !Int64 -> !Text -> !ModelDetails -> ModelInfo
- Data.Ollama.List: [details] :: ModelInfo -> ModelDetails
+ Data.Ollama.List: [details] :: ModelInfo -> !ModelDetails
- Data.Ollama.List: [digest] :: ModelInfo -> Text
+ Data.Ollama.List: [digest] :: ModelInfo -> !Text
- Data.Ollama.List: [modifiedAt] :: ModelInfo -> UTCTime
+ Data.Ollama.List: [modifiedAt] :: ModelInfo -> !UTCTime
- Data.Ollama.List: [name] :: ModelInfo -> Text
+ Data.Ollama.List: [name] :: ModelInfo -> !Text
- Data.Ollama.List: [size] :: ModelInfo -> Int64
+ Data.Ollama.List: [size] :: ModelInfo -> !Int64
- Data.Ollama.List: list :: IO (Maybe Models)
+ Data.Ollama.List: list :: Maybe OllamaConfig -> IO (Either OllamaError Models)
- Data.Ollama.Ps: RunningModel :: Text -> Text -> Int64 -> Text -> ModelDetails -> UTCTime -> Int64 -> RunningModel
+ Data.Ollama.Ps: RunningModel :: !Text -> !Text -> !Int64 -> !Text -> !ModelDetails -> !UTCTime -> !Int64 -> RunningModel
- Data.Ollama.Ps: [expiresAt] :: RunningModel -> UTCTime
+ Data.Ollama.Ps: [expiresAt] :: RunningModel -> !UTCTime
- Data.Ollama.Ps: [modelDetails] :: RunningModel -> ModelDetails
+ Data.Ollama.Ps: [modelDetails] :: RunningModel -> !ModelDetails
- Data.Ollama.Ps: [modelDigest] :: RunningModel -> Text
+ Data.Ollama.Ps: [modelDigest] :: RunningModel -> !Text
- Data.Ollama.Ps: [modelName] :: RunningModel -> Text
+ Data.Ollama.Ps: [modelName] :: RunningModel -> !Text
- Data.Ollama.Ps: [name_] :: RunningModel -> Text
+ Data.Ollama.Ps: [name_] :: RunningModel -> !Text
- Data.Ollama.Ps: [sizeVRam] :: RunningModel -> Int64
+ Data.Ollama.Ps: [sizeVRam] :: RunningModel -> !Int64
- Data.Ollama.Ps: [size_] :: RunningModel -> Int64
+ Data.Ollama.Ps: [size_] :: RunningModel -> !Int64
- Data.Ollama.Ps: ps :: IO (Maybe RunningModels)
+ Data.Ollama.Ps: ps :: Maybe OllamaConfig -> IO (Either OllamaError RunningModels)
- Data.Ollama.Pull: pull :: Text -> IO ()
+ Data.Ollama.Pull: pull :: Text -> IO (Either OllamaError PullResp)
- Data.Ollama.Pull: pullOps :: Text -> Maybe Bool -> Maybe Bool -> IO ()
+ Data.Ollama.Pull: pullOps :: Text -> Maybe Bool -> Maybe Bool -> Maybe OllamaConfig -> IO (Either OllamaError PullResp)
- Data.Ollama.Push: push :: Text -> IO ()
+ Data.Ollama.Push: push :: Text -> Maybe Bool -> Maybe Bool -> Maybe OllamaConfig -> IO ()
- Data.Ollama.Show: ShowModelResponse :: Text -> Text -> Text -> ModelDetails -> ModelInfo -> ShowModelResponse
+ Data.Ollama.Show: ShowModelResponse :: !Text -> !Maybe Text -> !Maybe Text -> !ModelDetails -> !ShowModelInfo -> !Maybe Text -> Maybe [Text] -> ShowModelResponse
- Data.Ollama.Show: showModel :: Text -> IO (Maybe ShowModelResponse)
+ Data.Ollama.Show: showModel :: Text -> IO (Either OllamaError ShowModelResponse)
- Data.Ollama.Show: showModelOps :: Text -> Maybe Bool -> IO (Maybe ShowModelResponse)
+ Data.Ollama.Show: showModelOps :: Text -> Maybe Bool -> Maybe OllamaConfig -> IO (Either OllamaError ShowModelResponse)
- Ollama: ChatOps :: Text -> NonEmpty Message -> Maybe [Value] -> Maybe Format -> Maybe (ChatResponse -> IO (), IO ()) -> Maybe Text -> Maybe Text -> Maybe Int -> Maybe Value -> ChatOps
+ Ollama: ChatOps :: !Text -> !NonEmpty Message -> !Maybe [InputTool] -> !Maybe Format -> !Maybe (ChatResponse -> IO (), IO ()) -> !Maybe Int -> !Maybe ModelOptions -> !Maybe Bool -> ChatOps
- Ollama: ChatResponse :: Text -> UTCTime -> Maybe Message -> Bool -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> ChatResponse
+ Ollama: ChatResponse :: !Text -> !UTCTime -> !Maybe Message -> !Bool -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> ChatResponse
- Ollama: EmbeddingOps :: Text -> Text -> Maybe Bool -> Maybe Text -> EmbeddingOps
+ Ollama: EmbeddingOps :: !Text -> ![Text] -> !Maybe Bool -> !Maybe Int -> !Maybe ModelOptions -> EmbeddingOps
- Ollama: EmbeddingResp :: Text -> [[Float]] -> EmbeddingResp
+ Ollama: EmbeddingResp :: !Text -> ![[Float]] -> EmbeddingResp
- Ollama: GenerateOps :: Text -> Text -> Maybe Text -> Maybe [Text] -> Maybe Format -> Maybe Text -> Maybe Text -> Maybe (GenerateResponse -> IO (), IO ()) -> Maybe Bool -> Maybe Text -> Maybe Text -> Maybe Int -> Maybe Value -> GenerateOps
+ Ollama: GenerateOps :: !Text -> !Text -> Maybe Text -> !Maybe [Text] -> !Maybe Format -> !Maybe Text -> !Maybe Text -> !Maybe (GenerateResponse -> IO (), IO ()) -> !Maybe Bool -> !Maybe Int -> !Maybe ModelOptions -> !Maybe Bool -> GenerateOps
- Ollama: GenerateResponse :: Text -> UTCTime -> Text -> Bool -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> Maybe Int64 -> GenerateResponse
+ Ollama: GenerateResponse :: !Text -> !UTCTime -> !Text -> !Bool -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Int64 -> !Maybe Text -> GenerateResponse
- Ollama: Message :: Role -> Text -> Maybe [Text] -> Maybe [Value] -> Message
+ Ollama: Message :: !Role -> !Text -> !Maybe [Text] -> !Maybe [ToolCall] -> !Maybe Text -> Message
- Ollama: ModelInfo :: Text -> UTCTime -> Int64 -> Text -> ModelDetails -> ModelInfo
+ Ollama: ModelInfo :: !Text -> !UTCTime -> !Int64 -> !Text -> !ModelDetails -> ModelInfo
- Ollama: RunningModel :: Text -> Text -> Int64 -> Text -> ModelDetails -> UTCTime -> Int64 -> RunningModel
+ Ollama: RunningModel :: !Text -> !Text -> !Int64 -> !Text -> !ModelDetails -> !UTCTime -> !Int64 -> RunningModel
- Ollama: SchemaFormat :: Value -> Format
+ Ollama: SchemaFormat :: Schema -> Format
- Ollama: ShowModelResponse :: Text -> Text -> Text -> ModelDetails -> ModelInfo -> ShowModelResponse
+ Ollama: ShowModelResponse :: !Text -> !Maybe Text -> !Maybe Text -> !ModelDetails -> !ShowModelInfo -> !Maybe Text -> Maybe [Text] -> ShowModelResponse
- Ollama: [chatModelName] :: ChatOps -> Text
+ Ollama: [chatModelName] :: ChatOps -> !Text
- Ollama: [content] :: Message -> Text
+ Ollama: [content] :: Message -> !Text
- Ollama: [createdAt] :: ChatResponse -> UTCTime
+ Ollama: [createdAt] :: ChatResponse -> !UTCTime
- Ollama: [details] :: ModelInfo -> ModelDetails
+ Ollama: [details] :: ModelInfo -> !ModelDetails
- Ollama: [digest] :: ModelInfo -> Text
+ Ollama: [digest] :: ModelInfo -> !Text
- Ollama: [done] :: ChatResponse -> Bool
+ Ollama: [done] :: ChatResponse -> !Bool
- Ollama: [evalCount] :: ChatResponse -> Maybe Int64
+ Ollama: [evalCount] :: ChatResponse -> !Maybe Int64
- Ollama: [evalDuration] :: ChatResponse -> Maybe Int64
+ Ollama: [evalDuration] :: ChatResponse -> !Maybe Int64
- Ollama: [expiresAt] :: RunningModel -> UTCTime
+ Ollama: [expiresAt] :: RunningModel -> !UTCTime
- Ollama: [format] :: ChatOps -> Maybe Format
+ Ollama: [format] :: ModelDetails -> !Text
- Ollama: [hostUrl] :: ChatOps -> Maybe Text
+ Ollama: [hostUrl] :: OllamaConfig -> Text
- Ollama: [images] :: Message -> Maybe [Text]
+ Ollama: [images] :: Message -> !Maybe [Text]
- Ollama: [keepAlive] :: ChatOps -> Maybe Text
+ Ollama: [keepAlive] :: ChatOps -> !Maybe Int
- Ollama: [loadDuration] :: ChatResponse -> Maybe Int64
+ Ollama: [loadDuration] :: ChatResponse -> !Maybe Int64
- Ollama: [message] :: ChatResponse -> Maybe Message
+ Ollama: [message] :: ChatResponse -> !Maybe Message
- Ollama: [messages] :: ChatOps -> NonEmpty Message
+ Ollama: [messages] :: ChatOps -> !NonEmpty Message
- Ollama: [modelDetails] :: RunningModel -> ModelDetails
+ Ollama: [modelDetails] :: RunningModel -> !ModelDetails
- Ollama: [modelDigest] :: RunningModel -> Text
+ Ollama: [modelDigest] :: RunningModel -> !Text
- Ollama: [modelName] :: RunningModel -> Text
+ Ollama: [modelName] :: RunningModel -> !Text
- Ollama: [model] :: ChatResponse -> Text
+ Ollama: [model] :: EmbeddingOps -> !Text
- Ollama: [modifiedAt] :: ModelInfo -> UTCTime
+ Ollama: [modifiedAt] :: ModelInfo -> !UTCTime
- Ollama: [name] :: ModelInfo -> Text
+ Ollama: [name] :: ModelInfo -> !Text
- Ollama: [name_] :: RunningModel -> Text
+ Ollama: [name_] :: RunningModel -> !Text
- Ollama: [options] :: ChatOps -> Maybe Value
+ Ollama: [options] :: ChatOps -> !Maybe ModelOptions
- Ollama: [promptEvalCount] :: ChatResponse -> Maybe Int64
+ Ollama: [promptEvalCount] :: ChatResponse -> !Maybe Int64
- Ollama: [promptEvalDuration] :: ChatResponse -> Maybe Int64
+ Ollama: [promptEvalDuration] :: ChatResponse -> !Maybe Int64
- Ollama: [role] :: Message -> Role
+ Ollama: [role] :: Message -> !Role
- Ollama: [sizeVRam] :: RunningModel -> Int64
+ Ollama: [sizeVRam] :: RunningModel -> !Int64
- Ollama: [size] :: ModelInfo -> Int64
+ Ollama: [size] :: ModelInfo -> !Int64
- Ollama: [size_] :: RunningModel -> Int64
+ Ollama: [size_] :: RunningModel -> !Int64
- Ollama: [stream] :: ChatOps -> Maybe (ChatResponse -> IO (), IO ())
+ Ollama: [stream] :: ChatOps -> !Maybe (ChatResponse -> IO (), IO ())
- Ollama: [tool_calls] :: Message -> Maybe [Value]
+ Ollama: [tool_calls] :: Message -> !Maybe [ToolCall]
- Ollama: [tools] :: ChatOps -> Maybe [Value]
+ Ollama: [tools] :: ChatOps -> !Maybe [InputTool]
- Ollama: [totalDuration] :: ChatResponse -> Maybe Int64
+ Ollama: [totalDuration] :: ChatResponse -> !Maybe Int64
- Ollama: chat :: ChatOps -> IO (Either String ChatResponse)
+ Ollama: chat :: ChatOps -> Maybe OllamaConfig -> IO (Either OllamaError ChatResponse)
- Ollama: copyModel :: Text -> Text -> IO ()
+ Ollama: copyModel :: Text -> Text -> Maybe OllamaConfig -> IO (Either OllamaError ())
- Ollama: createModel :: Text -> Maybe Text -> Maybe FilePath -> IO ()
+ Ollama: createModel :: Text -> Maybe Text -> Maybe Bool -> Maybe FilePath -> Maybe OllamaConfig -> IO ()
- Ollama: deleteModel :: Text -> IO ()
+ Ollama: deleteModel :: Text -> Maybe OllamaConfig -> IO (Either OllamaError ())
- Ollama: embedding :: Text -> Text -> IO (Either String EmbeddingResp)
+ Ollama: embedding :: Text -> [Text] -> IO (Either OllamaError EmbeddingResp)
- Ollama: embeddingOps :: Text -> Text -> Maybe Bool -> Maybe Text -> IO (Either String EmbeddingResp)
+ Ollama: embeddingOps :: Text -> [Text] -> Maybe Bool -> Maybe Int -> Maybe ModelOptions -> Maybe OllamaConfig -> IO (Either OllamaError EmbeddingResp)
- Ollama: generate :: GenerateOps -> IO (Either String GenerateResponse)
+ Ollama: generate :: GenerateOps -> Maybe OllamaConfig -> IO (Either OllamaError GenerateResponse)
- Ollama: list :: IO (Maybe Models)
+ Ollama: list :: Maybe OllamaConfig -> IO (Either OllamaError Models)
- Ollama: ps :: IO (Maybe RunningModels)
+ Ollama: ps :: Maybe OllamaConfig -> IO (Either OllamaError RunningModels)
- Ollama: pull :: Text -> IO ()
+ Ollama: pull :: Text -> IO (Either OllamaError PullResp)
- Ollama: pullOps :: Text -> Maybe Bool -> Maybe Bool -> IO ()
+ Ollama: pullOps :: Text -> Maybe Bool -> Maybe Bool -> Maybe OllamaConfig -> IO (Either OllamaError PullResp)
- Ollama: push :: Text -> IO ()
+ Ollama: push :: Text -> Maybe Bool -> Maybe Bool -> Maybe OllamaConfig -> IO ()
- Ollama: showModel :: Text -> IO (Maybe ShowModelResponse)
+ Ollama: showModel :: Text -> IO (Either OllamaError ShowModelResponse)
- Ollama: showModelOps :: Text -> Maybe Bool -> IO (Maybe ShowModelResponse)
+ Ollama: showModelOps :: Text -> Maybe Bool -> Maybe OllamaConfig -> IO (Either OllamaError ShowModelResponse)
Files
- CHANGELOG.md +23/−0
- README.md +67/−43
- ollama-haskell.cabal +38/−22
- src/Data/Ollama/Chat.hs +223/−326
- src/Data/Ollama/Common/Config.hs +90/−0
- src/Data/Ollama/Common/Error.hs +64/−0
- src/Data/Ollama/Common/SchemaBuilder.hs +191/−0
- src/Data/Ollama/Common/Types.hs +414/−31
- src/Data/Ollama/Common/Utils.hs +270/−9
- src/Data/Ollama/Conversation.hs +186/−0
- src/Data/Ollama/Copy.hs +63/−32
- src/Data/Ollama/Create.hs +83/−62
- src/Data/Ollama/Delete.hs +56/−29
- src/Data/Ollama/Embeddings.hs +139/−61
- src/Data/Ollama/Generate.hs +127/−289
- src/Data/Ollama/List.hs +74/−30
- src/Data/Ollama/Load.hs +103/−0
- src/Data/Ollama/Ps.hs +75/−27
- src/Data/Ollama/Pull.hs +114/−72
- src/Data/Ollama/Push.hs +88/−55
- src/Data/Ollama/Show.hs +161/−106
- src/Ollama.hs +133/−33
- test/Main.hs +16/−198
- test/Test/Ollama/Chat.hs +278/−0
- test/Test/Ollama/Embedding.hs +46/−0
- test/Test/Ollama/Generate.hs +319/−0
- test/Test/Ollama/Show.hs +59/−0
CHANGELOG.md view
@@ -1,5 +1,28 @@ # Revision history for ollama-haskell +## Unreleased++## 0.2.0.0 -- 2025-06-05++* Added stack matrix to ensure lib is buildable from lts-19.33+* Made parameters & template fields optional in `ShowModelResponse`.+* Added extra parameters fields in `ModelInfo`.+* Added strict annotations for all fields.+* Fixed ToJSON instance for delete model request body.+* Removed duplicate code by using unified `withOllamaRequest` function for all API calls.+* Added unified config type `OllamaConfig` to hold common configuration options.+* Added validation for generate and chat functions to ensure required fields are present.+* Added convience functions for generating Message and ToolCall types.+* Added thinking field for chat and generate function.+* Added ModelOptions type to encapsulate model options.+* Added get ollama version function.+* Added Common Manager, Callback functions and retry option in OllamaConfig.+* Fixed tool_calls.+* Added MonadIO versions of api functions.+* Added more comprehensive error handling for API calls.+* Added more comprehensive test cases for all functions.+* Added schema builder for passing json format for structured output.+ ## 0.1.3.0 -- 2025-03-25 * Added options, tools and tool_calls fields in chat and generate.
README.md view
@@ -1,74 +1,98 @@-# Ollama-haskell+# 🦙 Ollama Haskell -**ollama-haskell** is an unofficial Haskell binding for [Ollama](https://ollama.com), similar to [`ollama-python`](https://github.com/ollama/ollama-python). +**`ollama-haskell`** is an unofficial Haskell client for [Ollama](https://ollama.com), inspired by [`ollama-python`](https://github.com/ollama/ollama-python). It enables interaction with locally running LLMs through the Ollama HTTP API — directly from Haskell. -This library allows you to interact with Ollama, a tool that lets you run large language models (LLMs) locally, from within your Haskell projects. +--- -## Examples+## ✨ Features +* 💬 Chat with models+* ✍️ Text generation (with streaming)+* ✅ Chat with structured messages and tools+* 🧠 Embeddings+* 🧰 Model management (list, pull, push, show, delete)+* 🗃️ In-memory conversation history+* ⚙️ Configurable timeouts, retries, streaming handlers++---++## ⚡ Quick Example+ ```haskell {-# LANGUAGE OverloadedStrings #-}-module Lib where+module Main where -import Ollama (GenerateOps(..), defaultGenerateOps, generate)+import Data.Ollama.Generate+import qualified Data.Text.IO as T main :: IO () main = do- void $- generate+ let ops = defaultGenerateOps- { modelName = "llama3.2"- , prompt = "what is functional programming?"- , stream = Just (T.putStr . Ollama.response_, pure ())+ { modelName = "gemma3"+ , prompt = "What is the meaning of life?" }+ eRes <- generate ops Nothing+ case eRes of+ Left err -> putStrLn $ "Something went wrong: " ++ show err+ Right r -> do+ putStr "LLM response: "+ T.putStrLn (genResponse r) ``` -### Output--```bash-ghci> import Lib-ghci> main+--- -Whether Haskell is a "good" language depends on what you're looking for in a programming language and your personal preferences. Here are some points to consider:+## 📦 Installation -**Pros:**+Add to your `.cabal` file: -1. **Strongly typed**: Haskell's type system ensures that you catch errors early, which leads to fewer bugs and easier maintenance.-2. **Functional programming paradigm**: Haskell encourages declarative coding, making it easier to reason about code and write correct programs.-3. **Garbage collection**: Haskell handles memory management automatically, freeing you from worries about manual memory deallocation.+```cabal+build-depends:+ base >=4.7 && <5,+ ollama-haskell ``` -You can find practical examples demonstrating how to use the library in the `examples/OllamaExamples.hs` file. +Or use with `stack`/`nix-shell`. -## Prerequisite+--- -Make sure you have [Ollama](https://ollama.com) installed and running on your local machine. You can download it from [here](https://ollama.com/download).+## 📚 More Examples -## How to Use It+See [`examples/OllamaExamples.hs`](examples/OllamaExamples.hs) for: -1. Include the `ollama-haskell` package in your `.cabal` file:- ```cabal- build-depends:- base >= 4.7 && < 5,- ollama-haskell- ```+* Chat with conversation memory+* Structured JSON output+* Embeddings+* Tool/function calling+* Multimodal input+* Streaming and non-streaming variants -3. Import the `Ollama` module and start integrating with your local LLM.+--- -## Future Updates+## 🛠 Prerequisite -- [x] Improve documentation-- [x] Add tests.-- [x] Add examples.-- [x] Add CI/CD pipeline.-- [ ] `options` parameter in `generate`.+Make sure you have [Ollama installed and running locally](https://ollama.com/download). Run `ollama pull llama3` to download a model. -Stay tuned for future updates and improvements!+--- -## Author+## 🧪 Dev & Nix Support -This library is developed and maintained by [Tushar](https://github.com/tusharad). Feel free to reach out for any questions or suggestions!+Use Nix: -## Contributions+```bash+nix-shell+``` -Contributions are welcome! If you'd like to improve the library, please submit a pull request or open an issue. Whether it's fixing bugs, adding new features, or improving documentation, all contributions are greatly appreciated.+This will install `stack` and Ollama.++---++## 👨💻 Author++Created and maintained by [@tusharad](https://github.com/tusharad). PRs and feedback are welcome!++---++## 🤝 Contributing++Have ideas or improvements? Feel free to [open an issue](https://github.com/tusharad/ollama-haskell/issues) or submit a PR!
ollama-haskell.cabal view
@@ -1,12 +1,12 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.37.0.+-- This file has been generated from package.yaml by hpack version 0.38.0. -- -- see: https://github.com/sol/hpack name: ollama-haskell-version: 0.1.3.0-synopsis: Haskell bindings for ollama.+version: 0.2.0.0+synopsis: Haskell client for ollama. description: Ollama client for Haskell category: Web homepage: https://github.com/tusharad/ollama-haskell#readme@@ -28,14 +28,19 @@ library exposed-modules: Data.Ollama.Chat+ Data.Ollama.Common.Config+ Data.Ollama.Common.Error+ Data.Ollama.Common.SchemaBuilder Data.Ollama.Common.Types Data.Ollama.Common.Utils+ Data.Ollama.Conversation Data.Ollama.Copy Data.Ollama.Create Data.Ollama.Delete Data.Ollama.Embeddings Data.Ollama.Generate Data.Ollama.List+ Data.Ollama.Load Data.Ollama.Ps Data.Ollama.Pull Data.Ollama.Push@@ -49,23 +54,30 @@ ImportQualifiedPost ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints build-depends:- aeson+ aeson ==2.* , base >=4.7 && <5- , base64-bytestring- , bytestring- , containers- , directory- , filepath- , http-client- , http-types- , text- , time+ , base64-bytestring ==1.*+ , bytestring >=0.10 && <0.13+ , containers >=0.6 && <0.9+ , directory >=1 && <1.4+ , filepath >=1 && <1.6+ , http-client >=0.6 && <0.8+ , http-client-tls >=0.2 && <0.4+ , http-types >=0.7 && <0.13+ , mtl ==2.*+ , stm ==2.*+ , text >=1 && <3+ , time ==1.* default-language: Haskell2010 test-suite ollama-haskell-test type: exitcode-stdio-1.0 main-is: Main.hs other-modules:+ Test.Ollama.Chat+ Test.Ollama.Embedding+ Test.Ollama.Generate+ Test.Ollama.Show Paths_ollama_haskell hs-source-dirs: test@@ -75,17 +87,21 @@ build-depends: aeson , base >=4.7 && <5- , base64-bytestring- , bytestring- , containers- , directory- , filepath- , http-client- , http-types+ , base64-bytestring ==1.*+ , bytestring >=0.10 && <0.13+ , containers >=0.6 && <0.9+ , directory >=1 && <1.4+ , filepath >=1 && <1.6+ , http-client >=0.6 && <0.8+ , http-client-tls >=0.2 && <0.4+ , http-types >=0.7 && <0.13+ , mtl ==2.* , ollama-haskell+ , scientific , silently- , tasty+ , stm ==2.*+ , tasty >=1.5 , tasty-hunit , text- , time+ , time ==1.* default-language: Haskell2010
src/Data/Ollama/Chat.hs view
@@ -1,110 +1,220 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} +{- |+Module : Data.Ollama.Chat+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : Chat functionality for interacting with the Ollama API.++This module provides functions and types for initiating and managing chat interactions with an Ollama model.+It includes APIs for sending chat requests, constructing messages with different roles, and configuring chat+operations. The module supports both streaming and non-streaming responses, as well as optional tools and+structured output formats.++The primary functions are 'chat' and 'chatM' for sending chat requests, and helper functions like+'systemMessage', 'userMessage', 'assistantMessage', and 'toolMessage' for constructing messages.+The 'ChatOps' type allows customization of chat parameters, and 'defaultChatOps' provides a convenient+starting point for configuration.++Example:++>>> let ops = defaultChatOps { chatModelName = "customModel", messages = userMessage "Hello!" :| [] }+>>> chat ops Nothing+Either OllamaError ChatResponse+-} module Data.Ollama.Chat ( -- * Chat APIs chat- , chatJson+ , chatM++ -- * Message Types , Message (..) , Role (..)+ , systemMessage+ , userMessage+ , assistantMessage+ , toolMessage+ , genMessage++ -- * Chat Configuration , defaultChatOps , ChatOps (..)++ -- * Response Types , ChatResponse (..) , Format (..)- , schemaFromType ++ -- * Configuration and Error Types+ , OllamaConfig (..)+ , defaultOllamaConfig+ , OllamaError (..)+ , ModelOptions (..)+ , defaultModelOptions++ -- * Tool and Function Types+ , InputTool (..)+ , FunctionDef (..)+ , FunctionParameters (..)+ , OutputFunction (..)+ , ToolCall (..) ) where -import Control.Exception (try)+import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Aeson-import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy.Char8 as BSL import Data.List.NonEmpty as NonEmpty-import Data.Maybe (fromMaybe, isNothing)+import Data.Maybe (isNothing)+import Data.Ollama.Common.Config+import Data.Ollama.Common.Error (OllamaError (..))+import Data.Ollama.Common.Types+ ( ChatResponse (..)+ , Format (..)+ , FunctionDef (..)+ , FunctionParameters (..)+ , InputTool (..)+ , Message (..)+ , ModelOptions (..)+ , OutputFunction (..)+ , Role (..)+ , ToolCall (..)+ ) import Data.Ollama.Common.Utils as CU-import Data.Ollama.Common.Types (Format(..)) import Data.Text (Text)-import qualified Data.Text as T-import qualified Data.Text.Encoding as T-import Data.Time (UTCTime)-import GHC.Generics-import GHC.Int (Int64)-import Network.HTTP.Client-import qualified Data.Aeson.KeyMap as HM+import Data.Text qualified as T --- | Enumerated roles that can participate in a chat.-data Role = System | User | Assistant | Tool- deriving (Show, Eq)+{- | Constructs a 'Message' with the specified role and content. -instance ToJSON Role where- toJSON System = String "system"- toJSON User = String "user"- toJSON Assistant = String "assistant"- toJSON Tool = String "tool"+Creates a 'Message' with the given 'Role' and textual content, setting optional fields+('images', 'tool_calls', 'thinking') to 'Nothing'. -instance FromJSON Role where- parseJSON (String "system") = pure System- parseJSON (String "user") = pure User- parseJSON (String "assistant") = pure Assistant- parseJSON (String "tool") = pure Tool- parseJSON _ = fail "Invalid Role value"+Example: --- TODO : Add tool_calls parameter+>>> genMessage User "What's the weather like?"+Message {role = User, content = "What's the weather like?", images = Nothing, tool_calls = Nothing, thinking = Nothing}+-}+genMessage :: Role -> Text -> Message+genMessage r c =+ Message+ { role = r+ , content = c+ , images = Nothing+ , tool_calls = Nothing+ , thinking = Nothing+ } --- | 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 [Value]- -- ^ a list of tools in JSON that the model wants to use- -- ^ Since 0.1.3.0- }- deriving (Show, Eq, Generic, ToJSON, FromJSON)+{- | Creates a 'Message' with the 'System' role. --- TODO: Add Options parameter+Example:++>>> systemMessage "You are a helpful assistant."+Message {role = System, content = "You are a helpful assistant.", images = Nothing, tool_calls = Nothing, thinking = Nothing}+-}+systemMessage :: Text -> Message+systemMessage c = genMessage System c++{- | Creates a 'Message' with the 'User' role.++Example:++>>> userMessage "What's 2+2?"+Message {role = User, content = "What's 2+2?", images = Nothing, tool_calls = Nothing, thinking = Nothing}+-}+userMessage :: Text -> Message+userMessage c = genMessage User c++{- | Creates a 'Message' with the 'Assistant' role.++Example:++>>> assistantMessage "2+2 equals 4."+Message {role = Assistant, content = "2+2 equals 4.", images = Nothing, tool_calls = Nothing, thinking = Nothing}+-}+assistantMessage :: Text -> Message+assistantMessage c = genMessage Assistant c++{- | Creates a 'Message' with the 'Tool' role.++Example:++>>> toolMessage "Tool output: success"+Message {role = Tool, content = "Tool output: success", images = Nothing, tool_calls = Nothing, thinking = Nothing}+-}+toolMessage :: Text -> Message+toolMessage c = genMessage Tool c++{- | Validates 'ChatOps' to ensure required fields are non-empty.++Checks that the 'chatModelName' is not empty and that no 'Message' in 'messages' has empty content.+Returns 'Right' with the validated 'ChatOps' or 'Left' with an 'OllamaError' if validation fails.+--+-- @since 0.2.0.0+-}+validateChatOps :: ChatOps -> Either OllamaError ChatOps+validateChatOps ops+ | T.null (chatModelName ops) = Left $ InvalidRequest "Chat model name cannot be empty"+ | any (T.null . content) (messages ops) =+ Left $ InvalidRequest "Messages cannot have empty content"+ | otherwise = Right ops++{- | Configuration for initiating a chat with an Ollama model.++Defines the parameters for a chat request, including the model name, messages, and optional settings+for tools, response format, streaming, timeout, and model options.+-} data ChatOps = ChatOps- { chatModelName :: Text- -- ^ The name of the chat model to be used.- , messages :: NonEmpty Message+ { chatModelName :: !Text+ -- ^ The name of the chat model to be used (e.g., "gemma3").+ , messages :: !(NonEmpty Message) -- ^ A non-empty list of messages forming the conversation context.- , tools :: Maybe [Value]+ , tools :: !(Maybe [InputTool]) -- ^ Optional tools that may be used in the chat.- , format :: Maybe Format- -- ^ An optional format for the chat response (json or JSON schema).- -- ^ Since 0.1.3.0- , stream :: Maybe (ChatResponse -> IO (), IO ())- -- ^ Optional streaming functions where the first handles each chunk of the response, and the second flushes the stream.- , keepAlive :: Maybe Text- -- ^ Optional text to specify keep-alive behavior.- , hostUrl :: Maybe Text- -- ^ Override default Ollama host url. Default url = "http://127.0.0.1:11434"- , responseTimeOut :: Maybe Int- -- ^ Override default response timeout in minutes. Default = 15 minutes- , options :: Maybe Value- -- ^ additional model parameters listed in the documentation for the Modelfile such as temperature- -- ^ Since 0.1.3.0+ , format :: !(Maybe Format)+ -- ^ Optional format for the chat response (e.g., JSON or JSON schema).+ --+ -- @since 0.1.3.0+ , stream :: !(Maybe (ChatResponse -> IO (), IO ()))+ -- ^ Optional streaming functions: the first handles each response chunk, the second flushes the stream.+ , keepAlive :: !(Maybe Int)+ -- ^ Optional override for the response timeout in minutes (default: 15 minutes).+ , options :: !(Maybe ModelOptions)+ -- ^ Optional model parameters (e.g., temperature) as specified in the Modelfile.+ --+ -- @since 0.1.3.0+ , think :: !(Maybe Bool)+ -- ^ Optional flag to enable thinking mode.+ --+ -- @since 0.2.0.0 } instance Show ChatOps where- show (ChatOps {chatModelName = m, messages = ms, tools = t, format = f, keepAlive = ka}) =- let messagesStr = show (toList ms)- toolsStr = show t- formatStr = show f- keepAliveStr = show ka- in T.unpack m- ++ "\nMessages:\n"- ++ messagesStr- ++ "\n"- ++ toolsStr- ++ "\n"- ++ formatStr- ++ "\n"- ++ keepAliveStr+ show+ ( ChatOps+ { chatModelName = m+ , messages = ms+ , tools = t+ , format = f+ , keepAlive = ka+ , think = th+ }+ ) =+ let messagesStr = show (toList ms)+ toolsStr = show t+ formatStr = show f+ keepAliveStr = show ka+ thinkStr = show th+ in T.unpack m+ ++ "\nMessages:\n"+ ++ messagesStr+ ++ "\n"+ ++ toolsStr+ ++ "\n"+ ++ formatStr+ ++ "\n"+ ++ keepAliveStr+ ++ "\n"+ ++ thinkStr instance Eq ChatOps where (==) a b =@@ -114,32 +224,8 @@ && format a == format b && keepAlive a == keepAlive b -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 ToJSON ChatOps where- toJSON (ChatOps model_ messages_ tools_ format_ stream_ keepAlive_ _ _ options) =+ toJSON (ChatOps model_ messages_ tools_ format_ stream_ keepAlive_ options think_) = object [ "model" .= model_ , "messages" .= messages_@@ -148,254 +234,65 @@ , "stream" .= if isNothing stream_ then Just False else Just True , "keep_alive" .= keepAlive_ , "options" .= options+ , "think" .= think_ ] -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"+{- | Default configuration for initiating a chat. -{- |-A default configuration for initiating a chat with a model.-This can be used as a starting point and modified as needed.+Provides a default 'ChatOps' with the "gemma3" model and a sample user message ("What is 2+2?").+Can be customized by modifying fields as needed. Example: -> let ops = defaultChatOps { chatModelName = "customModel" }-> chat ops+>>> let ops = defaultChatOps { chatModelName = "customModel", messages = userMessage "Hello!" :| [] }+>>> chat ops Nothing+Either OllamaError ChatResponse -} defaultChatOps :: ChatOps defaultChatOps = ChatOps- { chatModelName = "llama3.2"- , messages = Message User "What is 2+2?" Nothing Nothing :| []+ { chatModelName = "gemma3"+ , messages = userMessage "What is 2+2?" :| [] , tools = Nothing , format = Nothing , stream = Nothing , keepAlive = Nothing- , hostUrl = Nothing- , responseTimeOut = Nothing , options = Nothing+ , think = Nothing } -{- |-Initiates a chat session with the specified 'ChatOps' configuration and returns either-a 'ChatResponse' or an error message.+{- | Sends a chat request to the Ollama API. -This function sends a request to the Ollama chat API with the given options.+Validates the 'ChatOps' configuration and sends a POST request to the "/api//chat" endpoint.+Supports both streaming and non-streaming responses based on the 'stream' field in 'ChatOps'.+Returns an 'Either' containing an 'OllamaError' on failure or a 'ChatResponse' on success. Example: -> let ops = defaultChatOps-> result <- chat ops-> case result of-> Left errorMsg -> putStrLn ("Error: " ++ errorMsg)-> Right response -> print response--To request a JSON format response:--> let ops = defaultChatOps { format = Just JsonFormat }-> result <- chat ops--To request a structured output with a JSON schema:--> import Data.Aeson (object, (.=))-> let ops = defaultChatOps { format = Just (SchemaFormat schema) }-> result <- chat ops--}-chat :: ChatOps -> IO (Either String ChatResponse)-chat cOps = do- let url = fromMaybe defaultOllamaUrl (hostUrl cOps)- responseTimeout = fromMaybe 15 (responseTimeOut cOps)- manager <-- newManager- defaultManagerSettings -- Setting response timeout to 5 minutes, since llm takes time- { managerResponseTimeout = responseTimeoutMicro (responseTimeout * 60 * 1000000)- }- eInitialRequest <- try $ parseRequest $ T.unpack (url <> "/api/chat") :: IO (Either HttpException Request)- case eInitialRequest of- Left e -> return $ Left $ "Failed to parse host url: " <> show e- Right initialRequest -> do- let reqBody = cOps- request =- initialRequest- { method = "POST"- , requestBody = RequestBodyLBS $ encode reqBody- }- eRes <-- try (withResponse request manager $ handleRequest cOps) ::- IO (Either HttpException (Either String ChatResponse))- case eRes of- Left e -> return $ Left $ "HTTP error occured: " <> show e- Right r -> do - return r--handleRequest :: ChatOps -> Response BodyReader -> IO (Either String ChatResponse)-handleRequest cOps response = do- let streamResponse sendChunk flush = do- bs <- brRead $ responseBody response- if BS.null bs- then putStrLn "" >> pure (Left "")- else do- let eRes = eitherDecode (BSL.fromStrict bs) :: Either String ChatResponse- case eRes of- Left e -> pure (Left e)- Right r -> do- _ <- sendChunk r- _ <- flush- if done r then pure (Left "") else streamResponse sendChunk flush- let genResponse op = do- bs <- brRead $ responseBody response- if BS.null bs- then do- let eRes = eitherDecode (BSL.fromStrict op) :: Either String ChatResponse- case eRes of- Left e -> pure (Left e)- Right r -> pure (Right r)- else genResponse (op <> bs)- case stream cOps of- Nothing -> genResponse ""- Just (sendChunk, flush) -> streamResponse sendChunk flush--{- |- chatJson is a higher level function that takes ChatOps (similar to chat) and also takes- a Haskell type (that has To and From JSON instance) and returns the response in provided type.-- This function simply calls chat with extra prompt appended to it, telling LLM to return the- response in certain JSON format and serializes the response. This function will be helpful when you- want to use the LLM to do something programmatic.-- Note: This function predates the format parameter in the API. For new code, consider using- the `format` parameter with a SchemaFormat instead, which leverages the model's native- JSON output capabilities.-- For Example:- > let expectedJsonStrucutre = Example {- > sortedList = ["sorted List here"]- > , wasListAlreadSorted = False- > }- > let msg0 = Ollama.Message User "Sort given list: [4, 2 , 3, 67]. Also tell whether list was already sorted or not." Nothing- > eRes3 <-- > chatJson- > defaultChatOps- > { Chat.chatModelName = "llama3.2"- > , Chat.messages = msg0 :| []- > }- > expectedJsonStrucutre- > (Just 2)- > print eRes3- Output:- > Example {sortedList = ["1","2","3","4"], wasListAlreadSorted = False}--Note: While Passing the type, construct the type that will help LLM understand the field better.- For example, in the above example, the sortedList's value is written as "Sorted List here". This- will help LLM understand context better.-- You can also provide number of retries in case the LLM field to return the response in correct JSON- in first attempt.+>>> let ops = defaultChatOps { chatModelName = "gemma3", messages = userMessage "What's the capital of France?" :| [] }+>>> chat ops Nothing+Either OllamaError ChatResponse -}-chatJson ::- (FromJSON jsonResult, ToJSON jsonResult) =>- ChatOps ->- -- | Haskell type that you want your result in- jsonResult ->- -- | Max retries- Maybe Int ->- IO (Either String jsonResult)-chatJson cOps@ChatOps {..} jsonStructure mMaxRetries = do- -- For models that support the format parameter, use that directly- --let jsonSchema = encode jsonStructure- let useNativeFormat = False -- Set to True to use the native format parameter when appropriate- - if useNativeFormat- then do- let formattedOps = cOps { format = Just (SchemaFormat (Object $ HM.fromList [("schema", Object $ HM.fromList [("type", String "object")])])) }- chatResponse <- chat formattedOps- case chatResponse of- Left err -> return $ Left err- Right r -> do- let mMessage = message r- case mMessage of- Nothing -> return $ Left "Something went wrong"- Just res -> case decode (BSL.fromStrict . T.encodeUtf8 $ content res) of- Nothing -> return $ Left "Decoding Failed :("- Just resultInType -> return $ Right resultInType- else do- -- Fall back to the original implementation using prompts- let lastMessage = NonEmpty.last messages- jsonHelperPrompt =- "You are an AI that returns only JSON object. \n"- <> "* Your output should be a JSON object that matches the following schema: \n"- <> T.decodeUtf8 (BSL.toStrict $ encode jsonStructure)- <> content lastMessage- <> "\n"- <> "# How to treat the task:\n"- <> "* Stricly follow the schema for the output.\n"- <> "* Never return anything other than a JSON object.\n"- <> "* Do not talk to the user.\n"- chatResponse <-- chat- cOps- { messages =- NonEmpty.fromList $- lastMessage {content = jsonHelperPrompt} : NonEmpty.init messages- }- case chatResponse of- Left err -> return $ Left err- Right r -> do- let mMessage = message r- case mMessage of- Nothing -> return $ Left "Something went wrong"- Just res -> do- case decode (BSL.fromStrict . T.encodeUtf8 $ content res) of- Nothing -> do- case mMaxRetries of- Nothing -> return $ Left "Decoding Failed :("- Just n -> if n < 1 then return $ Left "Decoding Failed :(" else chatJson cOps jsonStructure (Just (n - 1))- Just resultInType -> return $ Right resultInType+chat :: ChatOps -> Maybe OllamaConfig -> IO (Either OllamaError ChatResponse)+chat ops mbConfig =+ case validateChatOps ops of+ Left err -> return $ Left err+ Right _ -> withOllamaRequest "/api//chat" "POST" (Just ops) mbConfig handler+ where+ handler = case stream ops of+ Nothing -> commonNonStreamingHandler+ Just (sc, fl) -> commonStreamHandler sc fl --- | Helper function to create a JSON schema from a Haskell type-schemaFromType :: ToJSON a => a -> BSL.ByteString-schemaFromType = encode -- This is a simplified version; a real implementation would generate a JSON Schema+{- | MonadIO version of 'chat' for use in monadic contexts. -{- |- Example usage of 'Ollama.chat' with a JSON schema format and options field.+Lifts the 'chat' function into a 'MonadIO' context, allowing it to be used in monadic computations. - The first example sends a message requesting a JSON response conforming to a given schema.- The second example uses an alternative JSON format (here, @JsonFormat@).+Example: - >>> import Data.Aeson (Value, object, (.=))- >>> import Data.List.NonEmpty (NonEmpty(..))- >>> import Ollama (defaultChatOps, Message(..), SchemaFormat, JsonFormat)- >>> let x :: Value- ... x = object [ "type" .= ("object" :: String)- ... , "properties" .= object [ "age" .= object ["type" .= ("integer" :: String)] ]- ... ]- >>> let msg = Message User "Ollama is 22 years old and is busy saving the world. Respond using JSON" Nothing- >>> let opts = object ["option" .= ("some value" :: String)]- >>> res <- chat defaultChatOps- ... { chatModelName = "llama3.2"- ... , messages = msg :| []- ... , format = Just (SchemaFormat x)- ... , options = opts- ... }- >>> print (message res)- Just (Message {role = Assistant, content = "{\n \"age\": 22\n}", images = Nothing})- >>> res2 <- chat defaultChatOps- ... { chatModelName = "llama3.2"- ... , messages = msg :| []- ... , format = Just JsonFormat- ... , options = object ["option" .= ("other value" :: String)]- ... }- >>> print (message res2)- Just (Message {role = Assistant, content = "{ \"Name\": \"Ollama\", \"Age\": 22, \"Occupation\": \"World Savior\", \"Goals\": [\"Save humanity from alien invasion\", \"Unite warring nations\", \"Protect the environment\"] }", images = Nothing})+>>> import Control.Monad.IO.Class+>>> let ops = defaultChatOps { chatModelName = "gemma3", messages = userMessage "Hello!" :| [] }+>>> runReaderT (chatM ops Nothing) someContext+Either OllamaError ChatResponse -}+chatM :: MonadIO m => ChatOps -> Maybe OllamaConfig -> m (Either OllamaError ChatResponse)+chatM ops mbCfg = liftIO $ chat ops mbCfg
+ src/Data/Ollama/Common/Config.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}++{- |+Module : Data.Ollama.Common.Config+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : A unified configuration type for controlling Ollama client behavior.++== Overview++This module defines the core configuration record used throughout the Ollama Haskell client.++Use 'defaultOllamaConfig' as a starting point and customize it with helper functions+like 'withOnModelStart', 'withOnModelFinish', or 'withOnModelError'.++Includes settings for base URL, timeout, retry logic, and custom HTTP managers.+-}+module Data.Ollama.Common.Config+ ( -- * Configuration Type+ OllamaConfig (..)++ -- * Default Config+ , defaultOllamaConfig++ -- * Hook Helpers+ , withOnModelStart+ , withOnModelFinish+ , withOnModelError+ ) where++import Data.Text (Text)+import GHC.Generics+import Network.HTTP.Client++{- | Configuration for the Ollama client.+Used across all requests to customize behavior such as timeouts, retries,+custom HTTP manager, and lifecycle hooks.+--+-- @since 0.2.0.0+-}+data OllamaConfig = OllamaConfig+ { hostUrl :: Text+ -- ^ Base URL for the Ollama server (default: @http://127.0.0.1:11434@)+ , timeout :: Int+ -- ^ Timeout in seconds for API requests (ignored if 'commonManager' is set)+ , onModelStart :: Maybe (IO ())+ -- ^ Callback executed when a model starts+ , onModelError :: Maybe (IO ())+ -- ^ Callback executed if a model encounters an error+ , onModelFinish :: Maybe (IO ())+ -- ^ Callback executed when a model finishes (not called on error)+ , retryCount :: Maybe Int+ -- ^ Number of retries on failure (default: @0@ if 'Nothing')+ , retryDelay :: Maybe Int+ -- ^ Delay between retries in seconds (if applicable)+ , commonManager :: Maybe Manager+ -- ^ Shared HTTP manager; disables timeout and retry settings+ }+ deriving (Generic)++{- | A default configuration pointing to @localhost:11434@ with 90s timeout+and no hooks or retry logic.+-}+defaultOllamaConfig :: OllamaConfig+defaultOllamaConfig =+ OllamaConfig+ { hostUrl = "http://127.0.0.1:11434"+ , timeout = 90+ , onModelStart = Nothing+ , onModelError = Nothing+ , onModelFinish = Nothing+ , retryCount = Nothing+ , retryDelay = Nothing+ , commonManager = Nothing+ }++-- | Add a callback to be executed when a model starts.+withOnModelStart :: IO () -> OllamaConfig -> OllamaConfig+withOnModelStart f cfg = cfg {onModelStart = Just f}++-- | Add a callback to be executed when a model errors.+withOnModelError :: IO () -> OllamaConfig -> OllamaConfig+withOnModelError f cfg = cfg {onModelError = Just f}++-- | Add a callback to be executed when a model finishes successfully.+withOnModelFinish :: IO () -> OllamaConfig -> OllamaConfig+withOnModelFinish f cfg = cfg {onModelFinish = Just f}
+ src/Data/Ollama/Common/Error.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE DeriveGeneric #-}++{- |+Module : Data.Ollama.Common.Error+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : Unified error type for handling failures across the Ollama client.++== Overview++Defines the core 'OllamaError' type that wraps all potential errors+encountered while interacting with the Ollama API, including HTTP errors,+JSON decoding failures, API-specific errors, file I/O errors, and timeouts.+-}+module Data.Ollama.Common.Error+ ( -- * Error Types+ OllamaError (..)++ -- * Decoding Utilities+ , DecodingErrorMessage+ , DecodingFailedValue+ ) where++import Control.Exception (Exception, IOException)+import Data.Text (Text)+import GHC.Generics+import Network.HTTP.Client (HttpException)++-- | Type alias for a decoding error message string.+type DecodingErrorMessage = String++-- | Type alias for the value that failed to decode.+type DecodingFailedValue = String++-- | Represents all possible errors that may occur when using the Ollama client.+--+-- @since 0.2.0.0+data OllamaError+ = -- | Low-level HTTP exception (connection failure, etc.)+ HttpError HttpException+ | -- | Failure to decode a JSON response, includes message and raw value+ DecodeError DecodingErrorMessage DecodingFailedValue+ | -- | Error returned from Ollama's HTTP API+ ApiError Text+ | -- | Error during file operations (e.g., loading an image)+ FileError IOException+ | -- | Mismatch in expected JSON schema or structure+ JsonSchemaError String+ | -- | Request timed out+ TimeoutError String+ | -- | Request is malformed or violates input constraints+ InvalidRequest String+ deriving (Show, Generic)++instance Eq OllamaError where+ (HttpError _) == (HttpError _) = True+ x == y = eqOllamaError x y+ where+ eqOllamaError :: OllamaError -> OllamaError -> Bool+ eqOllamaError = (==)++instance Exception OllamaError
+ src/Data/Ollama/Common/SchemaBuilder.hs view
@@ -0,0 +1,191 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}++{- |+Module : Data.Ollama.Common.SchemaBuilder+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : DSL for constructing structured JSON Schemas for Ollama's structured output API.++== Overview++This module defines a simple schema builder DSL for programmatically constructing+JSON Schemas compatible with the structured output features in the Ollama API.++It supports nested objects, arrays, required fields, and custom types, and+provides infix operators for a fluent and expressive syntax.++== Example++@+import Data.Ollama.Common.SchemaBuilder++let schema =+ emptyObject+ |+ ("name", JString)+ |+ ("age", JInteger)+ |++ ("address", buildSchema $+ emptyObject+ |+ ("city", JString)+ |+ ("zip", JInteger)+ |! "city"+ )+ |!! ["name", "age"]+ & buildSchema++printSchema schema+@+-}+module Data.Ollama.Common.SchemaBuilder+ ( -- * Core Types+ JsonType (..)+ , Property (..)+ , Schema (..)++ -- * Schema Construction+ , emptyObject+ , addProperty+ , addObjectProperty+ , requireField+ , requireFields+ , buildSchema++ -- * Schema Utilities+ , objectOf+ , arrayOf+ , toOllamaFormat+ , printSchema++ -- * Infix Schema DSL+ , (|+)+ , (|++)+ , (|!)+ , (|!!)+ ) where++import Data.Aeson+import Data.Map.Strict qualified as HM+import Data.Text (Text)+import Data.Text qualified as T+import Data.Text.Lazy qualified as TL+import Data.Text.Lazy.Encoding qualified as T+import GHC.Generics++-- | Supported JSON types for schema generation.+data JsonType+ = JString+ | JNumber+ | JInteger+ | JBoolean+ | JNull+ | -- | Array of a specific type+ JArray JsonType+ | -- | Nested object schema+ JObject Schema+ deriving (Show, Eq, Generic)++instance ToJSON JsonType where+ toJSON JString = "string"+ toJSON JNumber = "number"+ toJSON JInteger = "integer"+ toJSON JBoolean = "boolean"+ toJSON JNull = "null"+ toJSON (JArray _) = "array"+ toJSON (JObject _) = "object"++-- | A named property with a given type (supports nested values).+newtype Property = Property JsonType+ deriving (Show, Eq, Generic)++instance ToJSON Property where+ toJSON (Property (JArray itemType)) =+ object ["type" .= ("array" :: Text), "items" .= Property itemType]+ toJSON (Property (JObject schema)) = toJSON schema+ toJSON (Property typ) = object ["type" .= typ]++-- | Complete schema representation.+--+-- @since 0.2.0.0+data Schema = Schema+ { schemaProperties :: HM.Map Text Property+ , schemaRequired :: [Text]+ }+ deriving (Show, Eq, Generic)++instance ToJSON Schema where+ toJSON (Schema props req) =+ object+ [ "type" .= ("object" :: Text)+ , "properties" .= props+ , "required" .= req+ ]++-- | Internal builder for schema DSL.+newtype SchemaBuilder = SchemaBuilder Schema+ deriving (Show, Eq)++-- | Create an empty schema object.+emptyObject :: SchemaBuilder+emptyObject = SchemaBuilder $ Schema HM.empty []++-- | Add a simple field with a given name and type.+addProperty :: Text -> JsonType -> SchemaBuilder -> SchemaBuilder+addProperty name typ (SchemaBuilder s) =+ SchemaBuilder $ s {schemaProperties = HM.insert name (Property typ) (schemaProperties s)}++-- | Add a nested object field with its own schema.+addObjectProperty :: Text -> Schema -> SchemaBuilder -> SchemaBuilder+addObjectProperty name nestedSchema (SchemaBuilder s) =+ SchemaBuilder $+ s {schemaProperties = HM.insert name (Property (JObject nestedSchema)) (schemaProperties s)}++-- | Mark a field as required.+requireField :: Text -> SchemaBuilder -> SchemaBuilder+requireField name (SchemaBuilder s) =+ SchemaBuilder $ s {schemaRequired = name : schemaRequired s}++-- | Mark multiple fields as required.+requireFields :: [Text] -> SchemaBuilder -> SchemaBuilder+requireFields names builder = foldr requireField builder names++-- | Finalize the schema from a builder.+buildSchema :: SchemaBuilder -> Schema+buildSchema (SchemaBuilder s) = s++-- | Wrap a 'SchemaBuilder' as a nested object type.+objectOf :: SchemaBuilder -> JsonType+objectOf builder = JObject (buildSchema builder)++-- | Create an array of a given JSON type.+arrayOf :: JsonType -> JsonType+arrayOf = JArray++-- | Convert schema into a JSON 'Value' suitable for API submission.+toOllamaFormat :: Schema -> Value+toOllamaFormat = toJSON++-- | Pretty print a schema as formatted JSON.+printSchema :: Schema -> IO ()+printSchema = putStrLn . T.unpack . TL.toStrict . T.decodeUtf8 . encode++-- | Infix alias for 'addProperty'.+(|+) :: SchemaBuilder -> (Text, JsonType) -> SchemaBuilder+builder |+ (name, typ) = addProperty name typ builder++-- | Infix alias for 'addObjectProperty'.+(|++) :: SchemaBuilder -> (Text, Schema) -> SchemaBuilder+builder |++ (name, schema) = addObjectProperty name schema builder++-- | Infix alias for 'requireField'.+(|!) :: SchemaBuilder -> Text -> SchemaBuilder+builder |! name = requireField name builder++-- | Infix alias for 'requireFields'.+(|!!) :: SchemaBuilder -> [Text] -> SchemaBuilder+builder |!! names = requireFields names builder++infixl 7 |+, |+++infixl 6 |!, |!!
src/Data/Ollama/Common/Types.hs view
@@ -1,22 +1,82 @@+{-# 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 (..)- , OllamaClient (..) , 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- , format :: Text- , familiy :: Text- , families :: [Text]- , parameterSize :: Text+ { 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) @@ -30,35 +90,358 @@ <*> v .: "parameter_size" <*> v .: "quantization_level" -newtype OllamaClient = OllamaClient- { host :: Text+{- | 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" -{-|-E.g SchemaFormat-{- "type": "object",- "properties": {- "age": {- "type": "integer"- },- "available": {- "type": "boolean"- }- },- "required": [- "age",- "available"- ]+-- | 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 }-|-}--- | Format specification for the chat output--- | Since 0.1.3.0-data Format = JsonFormat | SchemaFormat Value+ 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 Format where- toJSON JsonFormat = String "json"- toJSON (SchemaFormat schema) = schema+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"
src/Data/Ollama/Common/Utils.hs view
@@ -1,26 +1,71 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-} -module Data.Ollama.Common.Utils (defaultOllamaUrl, OllamaClient (..), encodeImage) where+{- |+Module : Data.Ollama.Common.Utils+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : Utility functions for interacting with the Ollama API, including image encoding, HTTP request handling, and retry logic. +This module provides helper functions for common tasks in the Ollama client, such as encoding images to Base64,+sending HTTP requests to the Ollama API, handling streaming and non-streaming responses, and managing retries for failed requests.+It also includes a default model options configuration and a function to retrieve the Ollama server version.++The functions in this module are used internally by other modules like 'Data.Ollama.Chat' and 'Data.Ollama.Generate' but can also be used directly for custom API interactions.+-}+module Data.Ollama.Common.Utils+ ( -- * Image Encoding+ encodeImage++ -- * HTTP Request Handling+ , withOllamaRequest+ , commonNonStreamingHandler+ , commonStreamHandler+ , nonJsonHandler++ -- * Model Options+ , defaultModelOptions++ -- * Retry Logic+ , withRetry++ -- * Version Retrieval+ , getVersion+ ) where++import Control.Concurrent (threadDelay) import Control.Exception (IOException, try)+import Data.Aeson import Data.ByteString qualified as BS import Data.ByteString.Base64 qualified as Base64+import Data.ByteString.Lazy qualified as BSL import Data.Char (toLower)+import Data.Maybe (fromMaybe)+import Data.Ollama.Common.Config+import Data.Ollama.Common.Error+import Data.Ollama.Common.Error qualified as Error import Data.Ollama.Common.Types import Data.Text (Text)+import Data.Text qualified as T import Data.Text.Encoding qualified as TE+import Network.HTTP.Client+import Network.HTTP.Client.TLS+import Network.HTTP.Types (Status (statusCode)) import System.Directory import System.FilePath -defaultOllamaUrl :: Text-defaultOllamaUrl = "http://127.0.0.1:11434"-+-- | List of supported image file extensions for 'encodeImage'. supportedExtensions :: [String] supportedExtensions = [".jpg", ".jpeg", ".png"] +-- | Safely read a file, returning an 'Either' with an 'IOException' on failure. safeReadFile :: FilePath -> IO (Either IOException BS.ByteString) safeReadFile = try . BS.readFile +-- | Read a file if it exists, returning 'Nothing' if it does not. asPath :: FilePath -> IO (Maybe BS.ByteString) asPath filePath = do exists <- doesFileExist filePath@@ -28,13 +73,15 @@ then either (const Nothing) Just <$> safeReadFile filePath else return Nothing +-- | Check if a file has a supported image extension. isSupportedExtension :: FilePath -> Bool-isSupportedExtension path = map toLower (takeExtension path) `elem` supportedExtensions+isSupportedExtension p = map toLower (takeExtension p) `elem` supportedExtensions -{- |- encodeImage is a utility function that takes an image file path (jpg, jpeg, png) and- returns the image data in Base64 encoded format. Since GenerateOps' images field- expects image data in base64. It is helper function that we are providing out of the box.+{- | Encodes an image file to Base64 format.++Takes a file path to an image (jpg, jpeg, or png) and returns its data encoded as a Base64 'Text'.+Returns 'Nothing' if the file extension is unsupported or the file cannot be read.+This is useful for including images in API requests that expect Base64-encoded data, such as 'GenerateOps' images field. -} encodeImage :: FilePath -> IO (Maybe Text) encodeImage filePath = do@@ -43,3 +90,217 @@ else do maybeContent <- asPath filePath return $ fmap (TE.decodeUtf8 . Base64.encode) maybeContent++{- | Executes an action with retry logic for recoverable errors.++Retries the given action up to the specified number of times with a delay (in seconds) between attempts.+Only retries on recoverable errors such as HTTP errors, timeouts, JSON schema errors, or decoding errors.+-}+withRetry ::+ -- | Number of retries+ Int ->+ -- | Delay between retries in seconds+ Int ->+ -- | Action to execute, returning 'Either' 'OllamaError' or a result+ IO (Either OllamaError a) ->+ IO (Either OllamaError a)+withRetry 0 _ action = action+withRetry retries delaySeconds action = do+ result <- action+ case result of+ Left err | isRetryableError err -> do+ threadDelay (delaySeconds * 1000000) -- Convert to microseconds+ withRetry (retries - 1) delaySeconds action+ _ -> return result+ where+ isRetryableError (HttpError _) = True+ isRetryableError (TimeoutError _) = True+ isRetryableError (JsonSchemaError _) = True+ isRetryableError (DecodeError _ _) = True+ isRetryableError _ = False++{- | Sends an HTTP request to the Ollama API.++A unified function for making API requests to the Ollama server. Supports both GET and POST methods,+customizable payloads, and optional configuration. The response is processed by the provided handler.+-}+withOllamaRequest ::+ forall payload response.+ (ToJSON payload) =>+ -- | API endpoint+ Text ->+ -- | HTTP method ("GET" or "POST")+ BS.ByteString ->+ -- | Optional request payload (must implement 'ToJSON')+ Maybe payload ->+ -- | Optional 'OllamaConfig' (defaults to 'defaultOllamaConfig')+ Maybe OllamaConfig ->+ -- | Response handler to process the HTTP response+ (Response BodyReader -> IO (Either OllamaError response)) ->+ IO (Either OllamaError response)+withOllamaRequest endpoint reqMethod mbPayload mbOllamaConfig handler = do+ let OllamaConfig {..} = fromMaybe defaultOllamaConfig mbOllamaConfig+ fullUrl = T.unpack $ hostUrl <> endpoint+ timeoutMicros = timeout * 1000000+ manager <- case commonManager of+ Nothing ->+ newTlsManagerWith+ tlsManagerSettings {managerResponseTimeout = responseTimeoutMicro timeoutMicros}+ Just m -> pure m+ eRequest <- try $ parseRequest fullUrl+ case eRequest of+ Left ex -> return $ Left $ Error.HttpError ex+ Right req -> do+ let request =+ req+ { method = reqMethod+ , requestBody =+ maybe mempty (\x -> RequestBodyLBS $ encode x) mbPayload+ }+ retryCnt = fromMaybe 0 retryCount+ retryDelay_ = fromMaybe 1 retryDelay+ withRetry retryCnt retryDelay_ $ do+ fromMaybe (pure ()) onModelStart+ eResponse <- try $ withResponse request manager handler+ case eResponse of+ Left ex -> do+ fromMaybe (pure ()) onModelError+ case ex of+ (HttpExceptionRequest _ ResponseTimeout) ->+ return $ Left $ Error.TimeoutError "No response from LLM yet"+ _ -> return $ Left $ Error.HttpError ex+ Right result -> do+ fromMaybe (pure ()) onModelFinish+ return result++{- | Handles non-streaming API responses.++Processes an HTTP response, accumulating all chunks until EOF and decoding the result as JSON.+Returns an 'Either' with an 'OllamaError' on failure or the decoded response on success.+Suitable for APIs that return a single JSON response.+-}+commonNonStreamingHandler ::+ FromJSON a =>+ Response BodyReader ->+ IO (Either OllamaError a)+commonNonStreamingHandler resp = do+ let bodyReader = responseBody resp+ respStatus = statusCode $ responseStatus resp+ if respStatus >= 200 && respStatus < 300+ then do+ finalBs <- readFullBuff BS.empty bodyReader+ case eitherDecode (BSL.fromStrict finalBs) of+ Left err -> pure . Left $ Error.DecodeError err (show finalBs)+ Right decoded -> pure . Right $ decoded+ else Left . ApiError . TE.decodeUtf8 <$> brRead bodyReader++{- | Accumulates response chunks into a single ByteString.++Internal helper function to read all chunks from a 'BodyReader' until EOF.+-}+readFullBuff :: BS.ByteString -> BodyReader -> IO BS.ByteString+readFullBuff acc reader = do+ chunk <- brRead reader+ if BS.null chunk+ then pure acc+ else readFullBuff (acc `BS.append` chunk) reader++{- | Handles streaming API responses.++Processes a streaming HTTP response, decoding each chunk as JSON and passing it to the provided+'sendChunk' function. The 'flush' function is called after each chunk. Stops when the response+indicates completion (via 'HasDone'). Returns the final decoded response or an error.+-}+commonStreamHandler ::+ (HasDone a, FromJSON a) =>+ -- | Function to handle each decoded chunk+ (a -> IO ()) ->+ -- | Function to flush after each chunk+ IO () ->+ Response BodyReader ->+ IO (Either OllamaError a)+commonStreamHandler sendChunk flush resp = go mempty+ where+ go acc = do+ bs <- brRead $ responseBody resp+ if BS.null bs+ then do+ case eitherDecode (BSL.fromStrict acc) of+ Left err -> pure $ Left $ Error.DecodeError err (show acc)+ Right decoded -> pure $ Right decoded+ else do+ let chunk = BSL.fromStrict bs+ case eitherDecode chunk of+ Left err -> return $ Left $ Error.DecodeError err (show acc)+ Right res -> do+ sendChunk res+ flush+ if getDone res then return (Right res) else go (acc <> bs)++{- | Handles non-JSON API responses.++Processes an HTTP response, accumulating all chunks into a 'ByteString'. Returns the accumulated+data on success (HTTP status 2xx) or an 'ApiError' on failure.+-}+nonJsonHandler :: Response BodyReader -> IO (Either OllamaError BS.ByteString)+nonJsonHandler resp = do+ let bodyReader = responseBody resp+ respStatus = statusCode $ responseStatus resp+ if respStatus >= 200 && respStatus < 300+ then Right <$> readFullBuff BS.empty bodyReader+ else Left . ApiError . TE.decodeUtf8 <$> brRead bodyReader++{- | Default model options for API requests.++Provides a default 'ModelOptions' configuration with all fields set to 'Nothing',+suitable as a starting point for customizing model parameters like temperature or token limits.++Example:++>>> let opts = defaultModelOptions { temperature = Just 0.7 }+-}+defaultModelOptions :: ModelOptions+defaultModelOptions =+ ModelOptions+ { numKeep = Nothing+ , seed = Nothing+ , numPredict = Nothing+ , topK = Nothing+ , topP = Nothing+ , minP = Nothing+ , typicalP = Nothing+ , repeatLastN = Nothing+ , temperature = Nothing+ , repeatPenalty = Nothing+ , presencePenalty = Nothing+ , frequencyPenalty = Nothing+ , penalizeNewline = Nothing+ , stop = Nothing+ , numa = Nothing+ , numCtx = Nothing+ , numBatch = Nothing+ , numGpu = Nothing+ , mainGpu = Nothing+ , useMmap = Nothing+ , numThread = Nothing+ }++{- | Retrieves the Ollama server version.++Sends a GET request to the "/api//version" endpoint and returns the server version+as a 'Version' wrapped in an 'Either' 'OllamaError'.++Example:++>>> getVersion+--+-- @since 0.2.0.0+-}+getVersion :: IO (Either OllamaError Version)+getVersion = do+ withOllamaRequest+ "/api//version"+ "GET"+ (Nothing :: Maybe Value)+ Nothing+ commonNonStreamingHandler
+ src/Data/Ollama/Conversation.hs view
@@ -0,0 +1,186 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}++{- |+Module : Data.Ollama.Conversation+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : Conversation management for the Ollama client, including storage and retrieval of chat sessions.++This module provides types and functions for managing conversations in the Ollama client. It defines+a 'Conversation' type to represent a chat session, a 'ConversationStore' typeclass for storage operations,+and an in-memory implementation using 'InMemoryStore' and 'ConvoM'. The module supports saving, loading,+listing, and deleting conversations, with thread-safe operations using STM (Software Transactional Memory).++The 'Conversation' type includes metadata such as a unique ID, messages, model name, and timestamps.+The 'ConversationStore' typeclass defines a generic interface for conversation storage, while 'InMemoryStore'+provides a concrete in-memory implementation. The 'ConvoM' monad integrates with 'InMemoryStore' for+monadic operations.++Example:++>>> store <- initInMemoryStore+>>> let conv = Conversation "conv1" [userMessage "Hello!"] "gemma3" <$> getCurrentTime <*> getCurrentTime+>>> runInMemoryConvo store $ saveConversation conv+>>> runInMemoryConvo store $ loadConversation "conv1"+Just (Conversation ...)+-}+module Data.Ollama.Conversation+ ( -- * Conversation Types+ Conversation (..)+ , ConversationStore (..)++ -- * In-Memory Store+ , InMemoryStore (..)+ , ConvoM (..)+ , initInMemoryStore+ , runInMemoryConvo++ -- * Validation+ , validateConversation+ ) where++import Control.Concurrent.STM+import Control.Monad.Reader+import Data.Aeson (FromJSON, ToJSON)+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Ollama.Common.Types+import Data.Text (Text)+import Data.Text qualified as T+import Data.Time (UTCTime, getCurrentTime)+import GHC.Generics (Generic)++{- | Represents a chat session with metadata and messages.++Stores a conversation's unique identifier, list of messages, model name, creation time, and last updated time.+-}+data Conversation = Conversation+ { conversationId :: !Text+ -- ^ Unique identifier for the conversation.+ , messages :: ![Message]+ -- ^ List of messages in the conversation.+ , model :: !Text+ -- ^ Name of the model used in the conversation (e.g., "gemma3").+ , createdAt :: !UTCTime+ -- ^ Timestamp when the conversation was created.+ , lastUpdated :: !UTCTime+ -- ^ Timestamp when the conversation was last updated.+ }+ deriving (Show, Eq, Generic)++instance ToJSON Conversation+instance FromJSON Conversation++{- | Typeclass defining operations for storing and managing conversations.++Provides methods for saving, loading, listing, and deleting conversations in a monadic context.+--+-- @since 0.2.0.0+-}+class Monad m => ConversationStore m where+ -- | Saves a conversation to the store.+ --+ -- Validates the conversation and updates its 'lastUpdated' timestamp before saving.+ saveConversation :: Conversation -> m ()++ -- | Loads a conversation by its ID.+ --+ -- Returns 'Just' the conversation if found, or 'Nothing' if not.+ loadConversation :: Text -> m (Maybe Conversation)++ -- | Lists all conversations in the store.+ listConversations :: m [Conversation]++ -- | Deletes a conversation by its ID.+ --+ -- Returns 'True' if the conversation was found and deleted, 'False' otherwise.+ deleteConversation :: Text -> m Bool++{- | In-memory conversation store using a 'TVar' for thread-safe operations.++Stores conversations in a 'Map' keyed by conversation IDs, wrapped in a 'TVar' for concurrent access.+-}+newtype InMemoryStore = InMemoryStore (TVar (Map Text Conversation))++{- | Monad for operations with 'InMemoryStore'.++A wrapper around 'ReaderT' that provides access to an 'InMemoryStore' in a monadic context.+-}+newtype ConvoM a = ConvoM {runConvoM :: ReaderT InMemoryStore IO a}+ deriving (Functor, Applicative, Monad, MonadIO, MonadReader InMemoryStore)++{- | Runs a 'ConvoM' action with the given 'InMemoryStore'.++Executes a monadic computation in the context of an in-memory store.++Example:++>>> store <- initInMemoryStore+>>> runInMemoryConvo store $ saveConversation conv+-}+runInMemoryConvo :: InMemoryStore -> ConvoM a -> IO a+runInMemoryConvo store = flip runReaderT store . runConvoM++instance ConversationStore ConvoM where+ saveConversation conv = do+ case validateConversation conv of+ Left err -> liftIO $ putStrLn ("Validation error: " <> T.unpack err)+ Right validConv -> do+ now <- liftIO getCurrentTime+ let updatedConv = validConv {lastUpdated = now}+ InMemoryStore ref <- ask+ liftIO . atomically $ modifyTVar' ref (Map.insert (conversationId updatedConv) updatedConv)++ loadConversation cid = do+ InMemoryStore ref <- ask+ convs <- liftIO $ readTVarIO ref+ return $ Map.lookup cid convs++ listConversations = do+ InMemoryStore ref <- ask+ convs <- liftIO $ readTVarIO ref+ return $ Map.elems convs++ deleteConversation cid = do+ InMemoryStore ref <- ask+ liftIO . atomically $ do+ convs <- readTVar ref+ if Map.member cid convs+ then do+ writeTVar ref (Map.delete cid convs)+ return True+ else return False++{- | Validates a 'Conversation' to ensure required fields are non-empty.++Checks that the 'conversationId' is not empty and that the 'messages' list contains at least one message.+Returns 'Right' with the validated conversation or 'Left' with an error message.++Example:++>>> let conv = Conversation "" [] "gemma3" time time+>>> validateConversation conv+Left "Conversation ID cannot be empty"+-}+validateConversation :: Conversation -> Either Text Conversation+validateConversation conv+ | T.null (conversationId conv) = Left "Conversation ID cannot be empty"+ | null (messages conv) = Left "Conversation must have at least one message"+ | otherwise = Right conv++{- | Creates a new empty in-memory conversation store.++Initializes an 'InMemoryStore' with an empty 'Map' wrapped in a 'TVar' for thread-safe operations.++Example:++>>> store <- initInMemoryStore+>>> runInMemoryConvo store $ listConversations+[]+-}+initInMemoryStore :: IO InMemoryStore+initInMemoryStore = InMemoryStore <$> newTVarIO Map.empty
src/Data/Ollama/Copy.hs view
@@ -3,53 +3,84 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE OverloadedStrings #-} +{- |+Module : Data.Ollama.Copy+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : Functionality for copying models in the Ollama client.++This module provides functions to copy a model from a source name to a destination name using the Ollama API.+It includes both an IO-based function ('copyModel') and a monadic version ('copyModelM') for use in+'MonadIO' contexts. The copy operation is performed via a POST request to the "/api//copy" endpoint.++Example:++>>> copyModel "gemma3" "gemma3-copy" Nothing+Right ()+-} module Data.Ollama.Copy ( -- * Copy Model API copyModel+ , copyModelM ) where -import Control.Monad (when)+import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Aeson-import Data.Ollama.Common.Utils qualified as CU+import Data.Ollama.Common.Config (OllamaConfig (..))+import Data.Ollama.Common.Error (OllamaError)+import Data.Ollama.Common.Utils (nonJsonHandler, withOllamaRequest) import Data.Text (Text)-import Data.Text qualified as T import GHC.Generics-import Network.HTTP.Client-import Network.HTTP.Types.Status (status404) --- TODO: Add Options parameter--- TODO: Add Context parameter+-- | Configuration for copying a model. data CopyModelOps = CopyModelOps- { source :: Text- , destination :: Text+ { source :: !Text+ -- ^ The name of the source model to copy.+ , destination :: !Text+ -- ^ The name of the destination model. } deriving (Show, Eq, Generic, ToJSON) --- | Copy model from source to destination+{- | Copies a model from a source name to a destination name.++Sends a POST request to the "/api//copy" endpoint with the source and destination model names.+Returns 'Right ()' on success or 'Left' with an 'OllamaError' on failure.+Example:++>>> copyModel "gemma3" "gemma3-copy" Nothing+Right ()+-} copyModel ::- -- | Source model+ -- | Source model name Text ->- -- | Destination model+ -- | Destination model name Text ->- IO ()+ -- | Optional 'OllamaConfig' (defaults to 'defaultOllamaConfig' if 'Nothing')+ Maybe OllamaConfig ->+ IO (Either OllamaError ()) copyModel source_- destination_ =- do- let url = CU.defaultOllamaUrl- manager <- newManager defaultManagerSettings- initialRequest <- parseRequest $ T.unpack (url <> "/api/copy")- let reqBody =- CopyModelOps- { source = source_- , destination = destination_- }- request =- initialRequest- { method = "POST"- , requestBody = RequestBodyLBS $ encode reqBody- }- response <- httpLbs request manager- when- (responseStatus response == status404)- (putStrLn "Source Model does not exist")+ destination_+ mbConfig = do+ let reqBody = CopyModelOps {source = source_, destination = destination_}+ withOllamaRequest+ "/api//copy"+ "POST"+ (Just reqBody)+ mbConfig+ (fmap ((const ()) <$>) . nonJsonHandler)++{- | MonadIO version of 'copyModel' for use in monadic contexts.++Lifts the 'copyModel' function into a 'MonadIO' context, allowing it to be used in monadic computations.++Example:++>>> import Control.Monad.IO.Class+>>> runReaderT (copyModelM "gemma3" "gemma3-copy" Nothing) someContext+Right ()+-}+copyModelM :: MonadIO m => Text -> Text -> Maybe OllamaConfig -> m (Either OllamaError ())+copyModelM s d mbCfg = liftIO $ copyModel s d mbCfg
src/Data/Ollama/Create.hs view
@@ -1,35 +1,64 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} +{- |+Module : Data.Ollama.Create+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : Functionality for creating new models in the Ollama client.++This module provides functions to create a new model in the Ollama API using either a model file+content or a file path. It includes both an IO-based function ('createModel') and a monadic version+('createModelM') for use in 'MonadIO' contexts. The create operation is performed via a POST request+to the "/api//pull" endpoint, with streaming support for progress updates.++Note: If both 'modelFile' and 'path' are provided, 'modelFile' takes precedence.++Example:++>>> createModel "myModel" (Just "FROM llama3\nPARAMETER temperature 0.8") (Just True) Nothing Nothing+Creating model...+Completed+-} module Data.Ollama.Create ( -- * Create Model API createModel- , createModelOps+ , createModelM ) where -import Control.Monad (unless)+import Control.Monad (void)+import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Aeson-import Data.ByteString.Lazy.Char8 qualified as BSL+import Data.Ollama.Common.Config (OllamaConfig)+import Data.Ollama.Common.Types (HasDone (getDone)) import Data.Ollama.Common.Utils as CU import Data.Text (Text)-import Data.Text qualified as T-import Data.Text.IO qualified as T-import Network.HTTP.Client --- TODO: Add Options parameter--- TODO: Add Context parameter+-- | Configuration for creating a new model. data CreateModelOps = CreateModelOps- { name :: Text- , modelFile :: Maybe Text- , stream :: Maybe Bool- , path :: Maybe FilePath+ { name :: !Text+ -- ^ The name of the model to create.+ , modelFile :: !(Maybe Text)+ -- ^ Optional model file content (e.g., Modelfile text). Takes precedence over 'path'.+ , stream :: !(Maybe Bool)+ -- ^ Optional flag to enable streaming progress updates.+ , path :: !(Maybe FilePath)+ -- ^ Optional file path to a Modelfile. } deriving (Show, Eq) --- TODO: Add Context Param-newtype CreateModelResp = CreateModelResp {status :: Text}+-- | Response type for model creation.+newtype CreateModelResp+ = -- | The status of the create operation (e.g., "success").+ CreateModelResp {status :: Text} deriving (Show, Eq) +instance HasDone CreateModelResp where+ getDone CreateModelResp {..} = status /= "success"+ instance ToJSON CreateModelOps where toJSON ( CreateModelOps@@ -50,71 +79,63 @@ CreateModelResp <$> v .: "status" -{- | Create a new model either from ModelFile or Path-Please note, if you specify both ModelFile and Path, ModelFile will be used.+{- | Creates a new model using either model file content or a file path.++Sends a POST request to the "/api//pull" endpoint to create a model with the specified name.+The model can be defined either by 'modelFile' (Modelfile content as text) or 'path' (file path to a Modelfile).+If both are provided, 'modelFile' is used. Supports streaming progress updates if 'stream' is 'Just True'.+Prints progress messages to the console during creation. -}-createModelOps ::- -- | Model Name+createModel ::+ -- | Model name Text ->- -- | Model File+ -- | Optional model file content Maybe Text ->- -- | Stream+ -- | Optional streaming flag Maybe Bool ->- -- | Path+ -- | Optional file path to a Modelfile Maybe FilePath ->+ -- | Optional 'OllamaConfig' (defaults to 'defaultOllamaConfig' if 'Nothing')+ Maybe OllamaConfig -> IO ()-createModelOps+createModel modelName modelFile_ stream_- path_ =- do- let url = defaultOllamaUrl- manager <- newManager defaultManagerSettings- initialRequest <- parseRequest $ T.unpack (url <> "/api/create")- let reqBody =+ path_+ mbConfig =+ void $+ withOllamaRequest+ "/api//pull"+ "POST"+ ( Just $ CreateModelOps { name = modelName , modelFile = modelFile_ , stream = stream_ , path = path_ }- request =- initialRequest- { method = "POST"- , requestBody = RequestBodyLBS $ encode reqBody- }- withResponse request manager $ \response -> do- let go = do- bs <- brRead $ responseBody response- let eRes =- eitherDecode (BSL.fromStrict bs) ::- Either String CreateModelResp- case eRes of- Left err -> do- putStrLn $ "Error: " <> err- Right res -> do- unless- (status res /= "success")- ( do- T.putStr $ status res- go- )- go+ )+ mbConfig+ (commonStreamHandler onToken onComplete)+ where+ onToken :: CreateModelResp -> IO ()+ onToken _ = do+ putStrLn "Creating model..." -{- | Create a new model-| Please note, if you specify both ModelFile and Path, ModelFile will be used.+ onComplete :: IO ()+ onComplete = putStrLn "Completed"++{- | MonadIO version of 'createModel' for use in monadic contexts.++Lifts the 'createModel' function into a 'MonadIO' context, allowing it to be used in monadic computations. -}-createModel ::- -- | Model Name+createModelM ::+ MonadIO m => Text ->- -- | Model File Maybe Text ->- -- | Path+ Maybe Bool -> Maybe FilePath ->- IO ()-createModel modelName modelFile_ =- createModelOps- modelName- modelFile_- Nothing+ Maybe OllamaConfig ->+ m ()+createModelM m mf s p mbCfg = liftIO $ createModel m mf s p mbCfg
src/Data/Ollama/Delete.hs view
@@ -3,42 +3,69 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} +{- |+Module : Data.Ollama.Delete+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : Functionality for deleting models in the Ollama client.++This module provides functions to delete a model from the Ollama server using its name. It includes+both an IO-based function ('deleteModel') and a monadic version ('deleteModelM') for use in+'MonadIO' contexts. The delete operation is performed via a DELETE request to the "/api//delete" endpoint.++Example:++>>> deleteModel "gemma3" Nothing+Right ()+-} module Data.Ollama.Delete- ( -- * Delete downloaded Models+ ( -- * Delete Model API deleteModel+ , deleteModelM ) where -import Control.Monad (when)+import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Aeson-import Data.Ollama.Common.Utils qualified as CU+import Data.Ollama.Common.Config (OllamaConfig (..))+import Data.Ollama.Common.Error (OllamaError)+import Data.Ollama.Common.Utils (nonJsonHandler, withOllamaRequest) import Data.Text (Text)-import Data.Text qualified as T-import Network.HTTP.Client-import Network.HTTP.Types.Status (status404) --- TODO: Add Options parameter--- TODO: Add Context parameter-newtype DeleteModelReq = DeleteModelReq {name :: Text}- deriving newtype (Show, Eq, ToJSON)+-- | Request payload for deleting a model.+newtype DeleteModelReq+ = -- | The name of the model to delete.+ DeleteModelReq {name :: Text}+ deriving newtype (Show, Eq) --- | Delete a model+instance ToJSON DeleteModelReq where+ toJSON (DeleteModelReq name_) = object ["name" .= name_]++{- | Deletes a model from the Ollama server.++Sends a DELETE request to the "/api//delete" endpoint with the specified model name.+Returns 'Right ()' on success or 'Left' with an 'OllamaError' on failure.+-} deleteModel ::- -- | Model name+ -- | Model name to delete Text ->- IO ()-deleteModel modelName =- do- let url = CU.defaultOllamaUrl- manager <- newManager defaultManagerSettings- initialRequest <- parseRequest $ T.unpack (url <> "/api/delete")- let reqBody =- DeleteModelReq {name = modelName}- request =- initialRequest- { method = "DELETE"- , requestBody = RequestBodyLBS $ encode reqBody- }- response <- httpLbs request manager- when- (responseStatus response == status404)- (putStrLn "Model does not exist")+ -- | Optional 'OllamaConfig' (defaults to 'defaultOllamaConfig' if 'Nothing')+ Maybe OllamaConfig ->+ IO (Either OllamaError ())+deleteModel modelName mbConfig = do+ let reqBody = DeleteModelReq {name = modelName}+ withOllamaRequest+ "/api//delete"+ "DELETE"+ (Just reqBody)+ mbConfig+ (fmap ((const ()) <$>) . nonJsonHandler)++{- | MonadIO version of 'deleteModel' for use in monadic contexts.++Lifts the 'deleteModel' function into a context,+allowing it to be used in monadic computations.+-}+deleteModelM :: MonadIO m => Text -> Maybe OllamaConfig -> m (Either OllamaError ())+deleteModelM t mbCfg = liftIO $ deleteModel t mbCfg
src/Data/Ollama/Embeddings.hs view
@@ -1,102 +1,180 @@-{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE OverloadedStrings #-} +{- |+Module : Data.Ollama.Embeddings+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : Functionality for generating text embeddings using the Ollama API.++This module provides functions to generate text embeddings from an Ollama model. It includes both+high-level ('embedding', 'embeddingM') and low-level ('embeddingOps', 'embeddingOpsM') APIs for+generating embeddings, with support for customizing model options, truncation, and keep-alive settings.+The embeddings are returned as a list of float vectors, suitable for tasks like semantic search or+text similarity analysis.++The 'EmbeddingOps' type configures the embedding request, and 'EmbeddingResp' represents the response+containing the model name and the generated embeddings. The 'defaultEmbeddingOps' provides a default+configuration for convenience.++Example:++>>> embedding "llama3.2" ["Hello, world!"]+Right (EmbeddingResp "llama3.2" [[0.1, 0.2, ...]])+-} module Data.Ollama.Embeddings ( -- * Embedding API embedding , embeddingOps+ , embeddingM+ , embeddingOpsM++ -- * Configuration and Response Types+ , defaultEmbeddingOps , EmbeddingOps (..) , EmbeddingResp (..)++ -- * Model Options+ , ModelOptions (..)+ , defaultModelOptions ) where +import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Aeson+import Data.Ollama.Common.Config (OllamaConfig)+import Data.Ollama.Common.Error (OllamaError)+import Data.Ollama.Common.Types (ModelOptions (..)) import Data.Ollama.Common.Utils as CU import Data.Text (Text)-import Data.Text qualified as T-import Network.HTTP.Client-import Control.Exception (try)-import Data.ByteString.Lazy.Char8 (ByteString) --- TODO: Add Options parameter+{- | Default configuration for embedding requests.++Provides a default 'EmbeddingOps' with the "llama3.2" model, an empty input list, and no additional options.+Can be customized by modifying fields as needed.+-}+defaultEmbeddingOps :: EmbeddingOps+defaultEmbeddingOps =+ EmbeddingOps+ { model = "llama3.2"+ , input = []+ , truncateInput = Nothing+ , keepAliveEmbed = Nothing+ , modelOptions = Nothing+ }++-- | Configuration for an embedding request. data EmbeddingOps = EmbeddingOps- { model :: Text- , input :: Text- , truncate :: Maybe Bool- , keepAlive :: Maybe Text+ { model :: !Text+ -- ^ The name of the model to use for generating embeddings (e.g., "llama3.2").+ , input :: ![Text]+ -- ^ List of input texts to generate embeddings for.+ , truncateInput :: !(Maybe Bool)+ -- ^ Optional flag to truncate input if it exceeds model limits.+ , keepAliveEmbed :: !(Maybe Int)+ -- ^ Optional override for the keep-alive timeout in minutes.+ , modelOptions :: !(Maybe ModelOptions)+ -- ^ Optional model parameters (e.g., temperature) as specified in the Modelfile.+ --+ -- @since 0.2.0.0 } deriving (Show, Eq) +-- | Response type for an embedding request. data EmbeddingResp = EmbeddingResp- { model :: Text- , embedding_ :: [[Float]]+ { respondedModel :: !Text+ -- ^ The name of the model that generated the embeddings.+ , respondedEmbeddings :: ![[Float]]+ -- ^ List of embedding vectors, one for each input text. } deriving (Show, Eq) instance FromJSON EmbeddingResp where- parseJSON = withObject "EmbeddingResp" $ \v -> EmbeddingResp- <$> v .: "model"- <*> v .: "embeddings"+ parseJSON = withObject "EmbeddingResp" $ \v ->+ EmbeddingResp+ <$> v .: "model"+ <*> v .: "embeddings" instance ToJSON EmbeddingOps where- toJSON (EmbeddingOps model_ input_ truncate' keepAlive_) =+ toJSON (EmbeddingOps model_ input_ truncate' keepAlive_ ops) = object [ "model" .= model_ , "input" .= input_ , "truncate" .= truncate' , "keep_alive" .= keepAlive_+ , "options" .= ops ] --- TODO: Add Options parameter+{- | Generates embeddings for a list of input texts with full configuration. --- | Embedding API+Sends a POST request to the "/api//embed" endpoint to generate embeddings for the provided inputs.+Allows customization of truncation, keep-alive settings, model options, and Ollama configuration.+Returns 'Right' with an 'EmbeddingResp' on success or 'Left' with an 'OllamaError' on failure.+-} embeddingOps ::- -- | Model- Text ->- -- | Input+ -- | Model name Text ->- -- | Truncate+ -- | List of input texts+ [Text] ->+ -- | Optional truncation flag Maybe Bool ->- -- | Keep Alive- Maybe Text ->- IO (Either String EmbeddingResp)-embeddingOps modelName input_ mTruncate mKeepAlive = do- let url = defaultOllamaUrl- manager <- newManager defaultManagerSettings- --einitialRequest <- parseRequest $ T.unpack (url <> "/api/embed")- eInitialRequest <-- try $ parseRequest $ T.unpack (url <> "/api/embed") :: IO (Either HttpException Request)- case eInitialRequest of- Left e -> do- return $ Left $ show e- Right initialRequest -> do- let reqBody =- EmbeddingOps- { model = modelName- , input = input_- , truncate = mTruncate- , keepAlive = mKeepAlive- }- request =- initialRequest- { method = "POST"- , requestBody = RequestBodyLBS $ encode reqBody- }- eResp <- try $ httpLbs request manager :: IO (Either HttpException (Response ByteString))- case eResp of- Left err -> return $ Left (show err)- Right resp -> - case decode (responseBody resp) of- Nothing -> return $ Left $ "Couldn't decode response: " <> show (responseBody resp)- Just r -> return $ Right r+ -- | Optional keep-alive timeout in minutes+ Maybe Int ->+ -- | Optional model options+ Maybe ModelOptions ->+ -- | Optional 'OllamaConfig' (defaults to 'defaultOllamaConfig' if 'Nothing')+ Maybe OllamaConfig ->+ IO (Either OllamaError EmbeddingResp)+embeddingOps modelName input_ mTruncate mKeepAlive mbOptions mbConfig = do+ withOllamaRequest+ "/api//embed"+ "POST"+ ( Just $+ EmbeddingOps+ { model = modelName+ , input = input_+ , truncateInput = mTruncate+ , keepAliveEmbed = mKeepAlive+ , modelOptions = mbOptions+ }+ )+ mbConfig+ commonNonStreamingHandler --- Higher level binding that only takes important params+{- | Simplified API for generating embeddings. --- | Embedding API+A higher-level function that generates embeddings using default settings for truncation, keep-alive,+model options, and Ollama configuration. Suitable for basic use cases.+-} embedding ::- -- | Model- Text ->- -- | Input+ -- | Model name Text ->- IO (Either String EmbeddingResp)+ -- | List of input texts+ [Text] ->+ IO (Either OllamaError EmbeddingResp) embedding modelName input_ =- embeddingOps modelName input_ Nothing Nothing+ embeddingOps modelName input_ Nothing Nothing Nothing Nothing++{- | MonadIO version of 'embedding' for use in monadic contexts.++Lifts the 'embedding' function into a 'MonadIO' context, allowing it to be used in monadic computations.+-}+embeddingM :: MonadIO m => Text -> [Text] -> m (Either OllamaError EmbeddingResp)+embeddingM m ip = liftIO $ embedding m ip++{- | MonadIO version of 'embeddingOps' for use in monadic contexts.++Lifts the 'embeddingOps' function into a 'MonadIO' context, allowing it to be used in monadic computations+with full configuration options.+-}+embeddingOpsM ::+ MonadIO m =>+ Text ->+ [Text] ->+ Maybe Bool ->+ Maybe Int ->+ Maybe ModelOptions ->+ Maybe OllamaConfig ->+ m (Either OllamaError EmbeddingResp)+embeddingOpsM m ip mbTruncate mbKeepAlive mbOptions mbCfg =+ liftIO $ embeddingOps m ip mbTruncate mbKeepAlive mbOptions mbCfg
src/Data/Ollama/Generate.hs view
@@ -2,80 +2,111 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} +{- |+Module : Data.Ollama.Generate+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : Text generation functionality for the Ollama client.++This module provides functions and types for generating text using an Ollama model. It includes APIs+for sending generation requests, both in IO ('generate') and monadic ('generateM') contexts, with+support for streaming and non-streaming responses. The 'GenerateOps' type configures the generation+request, allowing customization of the model, prompt, images, format, and other parameters. The+'defaultGenerateOps' provides a convenient starting point for configuration.++The module supports advanced features like Base64-encoded images, custom templates, and model-specific+options (e.g., temperature). It also includes validation to ensure required fields are non-empty.++Example:++>>> let ops = defaultGenerateOps { modelName = "gemma3", prompt = "Write a poem." }+>>> generate ops Nothing+Right (GenerateResponse ...)+-} module Data.Ollama.Generate ( -- * Generate Texts generate+ , generateM++ -- * Configuration , defaultGenerateOps- , generateJson , GenerateOps (..)+ , validateGenerateOps++ -- * Response and Configuration Types , GenerateResponse (..)+ , Format (..)+ , OllamaConfig (..)+ , defaultOllamaConfig+ , ModelOptions (..)+ , defaultModelOptions++ -- * Error Types+ , OllamaError (..) ) where -import Control.Exception (try)+import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Aeson-import Data.ByteString.Char8 qualified as BS-import Data.ByteString.Lazy.Char8 qualified as BSL import Data.Maybe+import Data.Ollama.Common.Config (OllamaConfig (..), defaultOllamaConfig)+import Data.Ollama.Common.Error (OllamaError (..))+import Data.Ollama.Common.Types (Format (..), GenerateResponse (..), ModelOptions (..)) import Data.Ollama.Common.Utils as CU-import Data.Ollama.Common.Types (Format(..)) import Data.Text (Text) import Data.Text qualified as T-import Data.Text.Encoding qualified as T-import Data.Time (UTCTime)-import GHC.Int (Int64)-import Network.HTTP.Client --- TODO: Add Options parameter--- TODO: Add Context parameter+{- | Validates 'GenerateOps' to ensure required fields are non-empty. -{- |- Input type for generate functions. This data type represents all possible configurations- that you can pass to the Ollama generate API.+Checks that the 'modelName' and 'prompt' fields are not empty. Returns 'Right' with the validated+'GenerateOps' or 'Left' with an 'OllamaError' if validation fails. - Example:+Example: - > let ops = GenerateOps- > { modelName = "llama3.2"- > , prompt = "What is the meaning of life?"- > , suffix = Nothing- > , images = Nothing- > , format = Just "text"- > , system = Nothing- > , template = Nothing- > , stream = Nothing- > , raw = Just False- > , keepAlive = Just "yes"- > }+>>> validateGenerateOps defaultGenerateOps+Left (InvalidRequest "Prompt cannot be empty")+--+-- @since 0.2.0.0 -}+validateGenerateOps :: GenerateOps -> Either OllamaError GenerateOps+validateGenerateOps ops+ | T.null (modelName ops) = Left $ InvalidRequest "Model name cannot be empty"+ | T.null (prompt ops) = Left $ InvalidRequest "Prompt cannot be empty"+ | otherwise = Right ops++-- | Configuration for a text generation request. data GenerateOps = GenerateOps- { modelName :: Text- -- ^ The name of the model to be used for generation.- , prompt :: Text- -- ^ The prompt text that will be provided to the model for generating a response.+ { modelName :: !Text+ -- ^ The name of the model to use for generation (e.g., "gemma3").+ , prompt :: !Text+ -- ^ The prompt text to provide to the model for generating a response. , suffix :: Maybe Text- -- ^ An optional suffix to append to the generated text.- , images :: Maybe [Text]- -- ^ Optional list of base64 encoded images to include with the request.- , format :: Maybe Format- -- ^ An optional format specifier for the response.- -- ^ Since 0.1.3.0- , system :: Maybe Text- -- ^ Optional system text that can be included in the generation context.- , template :: Maybe Text- -- ^ An optional template to format the response.- , stream :: Maybe (GenerateResponse -> IO (), IO ())- -- ^ An optional streaming function where the first function handles each chunk of response, and the second flushes the stream.- , raw :: Maybe Bool- -- ^ An optional flag to return the raw response.- , keepAlive :: Maybe Text- -- ^ Optional text to specify keep-alive behavior.- , hostUrl :: Maybe Text- -- ^ Override default Ollama host url. Default url = "http://127.0.0.1:11434"- , responseTimeOut :: Maybe Int- -- ^ Override default response timeout in minutes. Default = 15 minutes- , options :: Maybe Value- -- ^ additional model parameters listed in the documentation for the Modelfile such as temperature- -- ^ Since 0.1.3.0+ -- ^ Optional suffix to append to the generated text (not supported by all models).+ , images :: !(Maybe [Text])+ -- ^ Optional list of Base64-encoded images to include with the request.+ , format :: !(Maybe Format)+ -- ^ Optional format specifier for the response (e.g., JSON).+ --+ -- @since 0.1.3.0+ , system :: !(Maybe Text)+ -- ^ Optional system text to include in the generation context.+ , template :: !(Maybe Text)+ -- ^ Optional template to format the response.+ , stream :: !(Maybe (GenerateResponse -> IO (), IO ()))+ -- ^ Optional streaming functions: the first handles each response chunk, the second flushes the stream.+ , raw :: !(Maybe Bool)+ -- ^ Optional flag to return the raw response.+ , keepAlive :: !(Maybe Int)+ -- ^ Optional override for how long (in minutes) the model stays loaded in memory (default: 5 minutes).+ , options :: !(Maybe ModelOptions)+ -- ^ Optional model parameters (e.g., temperature) as specified in the Modelfile.+ --+ -- @since 0.1.3.0+ , think :: !(Maybe Bool)+ -- ^ Optional flag to enable thinking mode.+ --+ -- @since 0.2.0.0 } instance Show GenerateOps where@@ -103,6 +134,8 @@ <> show keepAlive <> ", options : " <> show options+ <> ", think: "+ <> show think instance Eq GenerateOps where (==) a b =@@ -116,35 +149,7 @@ && raw a == raw b && keepAlive a == keepAlive b && options a == options b---- TODO: Add Context Param--{- |-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.- , response_ :: 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.- }- deriving (Show, Eq)+ && think a == think b instance ToJSON GenerateOps where toJSON@@ -159,9 +164,8 @@ stream raw keepAlive- _ -- Host url- _ -- Response timeout- options + options+ think ) = object [ "model" .= model@@ -175,37 +179,24 @@ , "raw" .= raw , "keep_alive" .= keepAlive , "options" .= options+ , "think" .= think ] -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"+{- | Default configuration for text generation. -{- |-A function to create a default 'GenerateOps' type with preset values.+Provides a default 'GenerateOps' with the "gemma3" model and an empty prompt. Other fields are set+to 'Nothing' or default values. Can be customized by modifying fields as needed. Example: -> let ops = defaultGenerateOps-> generate ops--This will generate a response using the default configuration.+>>> let ops = defaultGenerateOps { modelName = "customModel", prompt = "Hello!" }+>>> generate ops Nothing -} defaultGenerateOps :: GenerateOps defaultGenerateOps = GenerateOps- { modelName = "llama3.2"- , prompt = "what is 2+2"+ { modelName = "gemma3"+ , prompt = "" , suffix = Nothing , images = Nothing , format = Nothing@@ -214,197 +205,44 @@ , stream = Nothing , raw = Nothing , keepAlive = Nothing- , hostUrl = Nothing- , responseTimeOut = Nothing , options = Nothing+ , think = Nothing } -{- |-Generate function that returns either a 'GenerateResponse' type or an error message.-It takes a 'GenerateOps' configuration and performs a request to the Ollama generate API.--Examples:--Basic usage without streaming:--> let ops = GenerateOps-> { modelName = "llama3.2"-> , prompt = "Tell me a joke."-> , suffix = Nothing-> , images = Nothing-> , format = Nothing-> , system = Nothing-> , template = Nothing-> , stream = Nothing-> , raw = Nothing-> , keepAlive = Nothing-> }-> result <- generate ops-> case result of-> Left errorMsg -> putStrLn ("Error: " ++ errorMsg)-> Right response -> print response--Usage with streaming to print responses to the console:--> void $-> generate-> defaultGenerateOps-> { modelName = "llama3.2"-> , prompt = "what is functional programming?"-> , stream = Just (T.putStr . response_, pure ())-> }--In this example, the first function in the 'stream' tuple processes each chunk of response by printing it,-and the second function is a simple no-op flush.generate :: GenerateOps -> IO (Either String GenerateResponse)--}-generate :: GenerateOps -> IO (Either String GenerateResponse)-generate genOps = do- let url = fromMaybe defaultOllamaUrl (hostUrl genOps)- responseTimeout = fromMaybe 15 (responseTimeOut genOps)- manager <-- newManager -- Setting response timeout to 5 minutes, since llm takes time- defaultManagerSettings- { managerResponseTimeout = responseTimeoutMicro (responseTimeout * 60 * 1000000)- }- eInitialRequest <-- try $ parseRequest $ T.unpack (url <> "/api/generate") :: IO (Either HttpException Request)- case eInitialRequest of- Left e -> do- return $ Left $ show e- Right initialRequest -> do- let reqBody = genOps- request =- initialRequest- { method = "POST"- , requestBody = RequestBodyLBS $ encode reqBody- }- eRes <-- try (withResponse request manager $ handleRequest genOps) ::- IO (Either HttpException (Either String GenerateResponse))- case eRes of- Left e -> do- return $ Left $ "HTTP error occured: " <> show e- Right r -> return r--handleRequest :: GenerateOps -> Response BodyReader -> IO (Either String GenerateResponse)-handleRequest genOps response = do- let streamResponse sendChunk flush = do- bs <- brRead $ responseBody response- if BS.null bs- then putStrLn "" >> pure (Left "")- else do- let eRes = eitherDecode (BSL.fromStrict bs) :: Either String GenerateResponse- case eRes of- Left e -> pure (Left e)- Right r -> do- _ <- sendChunk r- _ <- flush- if done r then pure (Right r) else streamResponse sendChunk flush- let genResponse op = do- bs <- brRead $ responseBody response- if bs == ""- then do- let eRes0 = eitherDecode (BSL.fromStrict op) :: Either String GenerateResponse- case eRes0 of- Left e -> pure (Left e)- Right r -> pure (Right r)- else genResponse (op <> bs)- case stream genOps of- Nothing -> genResponse ""- Just (sendChunk, flush) -> streamResponse sendChunk flush--{- |- generateJson is a higher level function that takes generateOps (similar to generate) and also takes- a Haskell type (that has To and From JSON instance) and returns the response in provided type.-- This function simply calls generate with extra prompt appended to it, telling LLM to return the- response in certain JSON format and serializes the response. This function will be helpful when you- want to use the LLM to do something programmatic.-- For Example:- > let expectedJsonStrucutre = Example {- > sortedList = ["sorted List here"]- > , wasListAlreadSorted = False- > }- > eRes2 <- generateJson- > defaultGenerateOps- > { modelName = "llama3.2"- > , prompt = "Sort given list: [4, 2 , 3, 67]. Also tell whether list was already sorted or not."- > }- > expectedJsonStrucutre- > Nothing- > case eRes2 of- > Left e -> putStrLn e- > Right r -> print ("JSON response: " :: String, r)+{- | Generates text using the specified model and configuration. -Output:- > ("JSON response: ",Example {sortedList = ["1","2","3","4"], wasListAlreadSorted = False})+Validates the 'GenerateOps' configuration and sends a POST request to the "/api//generate" endpoint.+Supports both streaming and non-streaming responses based on the 'stream' field in 'GenerateOps'.+Returns 'Right' with a 'GenerateResponse' on success or 'Left' with an 'OllamaError' on failure. -Note: While Passing the type, construct the type that will help LLM understand the field better.- For example, in the above example, the sortedList's value is written as "Sorted List here". This- will help LLM understand context better.+Example: - You can also provide number of retries in case the LLM field to return the response in correct JSON- in first attempt.+>>> let ops = defaultGenerateOps { modelName = "gemma3", prompt = "Write a short poem." }+>>> generate ops Nothing+Right (GenerateResponse ...) -}-generateJson ::- (ToJSON jsonResult, FromJSON jsonResult) =>- GenerateOps ->- -- | Haskell type that you want your result in- jsonResult ->- -- | Max retries- Maybe Int ->- IO (Either String jsonResult)-generateJson genOps@GenerateOps {..} jsonStructure mMaxRetries = do- let jsonHelperPrompt =- "You are an AI that returns only JSON object. \n"- <> "* Your output should be a JSON object that matches the following schema: \n"- <> T.decodeUtf8 (BSL.toStrict $ encode jsonStructure)- <> prompt- <> "\n"- <> "# How to treat the task:\n"- <> "* Stricly follow the schema for the output.\n"- <> "* Never return anything other than a JSON object.\n"- <> "* Do not talk to the user.\n"- generatedResponse <- generate genOps {prompt = jsonHelperPrompt}- case generatedResponse of- Left err -> return $ Left err- Right r -> do- case decode (BSL.fromStrict . T.encodeUtf8 $ response_ r) of- Nothing -> do- case mMaxRetries of- Nothing -> return $ Left "Decoding Failed :("- Just n ->- if n < 1- then return $ Left "Decoding failed :("- else generateJson genOps jsonStructure (Just (n - 1))- Just resultInType -> return $ Right resultInType+generate :: GenerateOps -> Maybe OllamaConfig -> IO (Either OllamaError GenerateResponse)+generate ops mbConfig =+ case validateGenerateOps ops of+ Left err -> pure $ Left err+ Right _ -> withOllamaRequest "/api//generate" "POST" (Just ops) mbConfig handler+ where+ handler = case stream ops of+ Nothing -> commonNonStreamingHandler+ Just (sc, fl) -> commonStreamHandler sc fl +{- | MonadIO version of 'generate' for use in monadic contexts. -{- |- Example usage of 'Ollama.generate' with a JSON schema format and options field.+Lifts the 'generate' function into a 'MonadIO' context, allowing it to be used in monadic computations. - In this example we pass a JSON schema that expects an object with an integer field @age@.- The options field is supplied as a JSON value.+Example: - >>> import Data.Aeson (Value, object, (.=))- >>> import Ollama (GenerateOps, defaultGenerateOps, SchemaFormat)- >>> let x :: Value- ... x = object [ "type" .= ("object" :: String)- ... , "properties" .= object [ "age" .= object ["type" .= ("integer" :: String)] ]- ... ]- >>> let opts :: Value- ... opts = object ["option" .= ("some value" :: String)]- >>> generate defaultGenerateOps- ... { modelName = "llama3.2"- ... , prompt = "Ollama is 22 years old and is busy saving the world. Respond using JSON"- ... , format = Just (SchemaFormat x)- ... , options = opts- ... }- Right (GenerateResponse {model = "llama3.2", createdAt = 2025-03-25 09:34:15.853417157 UTC,- response_ = "{\n \"age\": 22\n}", done = True,- totalDuration = Just 6625631744, loadDuration = Just 2578791966,- promptEvalCount = Just 43, promptEvalDuration = Just 2983000000,- evalCount = Just 10, evalDuration = Just 1061000000})+>>> import Control.Monad.IO.Class+>>> let ops = defaultGenerateOps { modelName = "gemma3", prompt = "Hello!" }+>>> runReaderT (generateM ops Nothing) someContext+Right (GenerateResponse ...) -}-+generateM ::+ MonadIO m =>+ GenerateOps -> Maybe OllamaConfig -> m (Either OllamaError GenerateResponse)+generateM ops mbCfg = liftIO $ generate ops mbCfg
src/Data/Ollama/List.hs view
@@ -1,39 +1,69 @@+-- |+-- Module : Data.Ollama.List+-- Copyright : (c) 2025 Tushar Adhatrao+-- License : MIT+-- Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+-- Stability : experimental+-- Description : Functionality for listing available models in the Ollama client.+--+-- This module provides functions to retrieve a list of models available on the Ollama server.+-- It includes both an IO-based function ('list') and a monadic version ('listM') for use in+-- 'MonadIO' contexts. The list operation is performed via a GET request to the "/api//tags" endpoint,+-- returning a 'Models' type containing a list of 'ModelInfo' records with details about each model.+--+-- Example:+--+-- >>> list Nothing+-- Right (Models [ModelInfo ...])+-- {-# LANGUAGE OverloadedStrings #-} module Data.Ollama.List ( -- * List Models API- list- , Models (..)- , ModelInfo (..)- )-where+ list,+ listM, + -- * Model Types+ Models (..),+ ModelInfo (..)+ ) where++import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Aeson+import Data.Ollama.Common.Config (OllamaConfig)+import Data.Ollama.Common.Error (OllamaError) import Data.Ollama.Common.Types as CT import Data.Ollama.Common.Utils as CU import Data.Text (Text)-import Data.Text qualified as T import Data.Time import GHC.Int (Int64)-import Network.HTTP.Client-import Network.HTTP.Types.Status (statusCode) +-- | A wrapper type containing a list of available models.+-- newtype Models = Models [ModelInfo]+ -- ^ List of 'ModelInfo' records describing available models. deriving (Eq, Show) +-- | Details about a specific model.+-- data ModelInfo = ModelInfo- { name :: Text- , modifiedAt :: UTCTime- , size :: Int64- , digest :: Text- , details :: ModelDetails- }- deriving (Eq, Show)+ { name :: !Text+ -- ^ The name of the model.+ , modifiedAt :: !UTCTime+ -- ^ The timestamp when the model was last modified.+ , size :: !Int64+ -- ^ The size of the model in bytes.+ , digest :: !Text+ -- ^ The digest (hash) of the model.+ , details :: !ModelDetails+ -- ^ Additional details about the model (e.g., format, family, parameters).+ } deriving (Eq, Show) --- Instances+-- | JSON parsing instance for 'Models'. instance FromJSON Models where parseJSON = withObject "Models" $ \v -> Models <$> v .: "models" +-- | JSON parsing instance for 'ModelInfo'. instance FromJSON ModelInfo where parseJSON = withObject "ModelInfo" $ \v -> ModelInfo@@ -43,17 +73,31 @@ <*> v .: "digest" <*> v .: "details" --- | List all models from local-list :: IO (Maybe Models)-list = do- let url = defaultOllamaUrl- manager <- newManager defaultManagerSettings- request <- parseRequest $ T.unpack (url <> "/api/tags")- response <- httpLbs request manager- if statusCode (responseStatus response) /= 200- then pure Nothing- else do- let res = decode (responseBody response) :: Maybe Models- case res of- Nothing -> pure Nothing- Just l -> pure $ Just l+-- | Retrieves a list of available models from the Ollama server.+--+-- Sends a GET request to the "/api//tags" endpoint to fetch the list of models.+-- Returns 'Right' with a 'Models' containing the list of 'ModelInfo' on success,+-- or 'Left' with an 'OllamaError' on failure.+list ::+ Maybe OllamaConfig -> -- ^ Optional 'OllamaConfig' (defaults to 'defaultOllamaConfig' if 'Nothing')+ IO (Either OllamaError Models)+list mbConfig = do+ withOllamaRequest+ "/api//tags"+ "GET"+ (Nothing :: Maybe Value)+ mbConfig+ commonNonStreamingHandler++-- | MonadIO version of 'list' for use in monadic contexts.+--+-- Lifts the 'list' function into a 'MonadIO' context, allowing it to be used in monadic computations.+--+-- Example:+--+-- >>> import Control.Monad.IO.Class+-- >>> runReaderT (listM Nothing) someContext+-- Right (Models [ModelInfo ...])+--+listM :: MonadIO m => Maybe OllamaConfig -> m (Either OllamaError Models)+listM mbCfg = liftIO $ list mbCfg
+ src/Data/Ollama/Load.hs view
@@ -0,0 +1,103 @@+{-# LANGUAGE OverloadedStrings #-}++{- |+Module : Data.Ollama.Load+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : High-level functions for loading and unloading models in the Ollama client.++This module provides functions to load and unload generative models in the Ollama server.+It includes both IO-based functions ('loadGenModel', 'unloadGenModel') and monadic versions+('loadGenModelM', 'unloadGenModelM') for use in 'MonadIO' contexts. The operations are+performed via POST requests to the "/api//generate" endpoint, leveraging the 'GenerateOps'+configuration from the 'Data.Ollama.Generate' module.++Loading a model keeps it in memory for faster subsequent requests, while unloading frees+up memory by setting the keep-alive duration to zero.++Example:++>>> loadGenModel "gemma3"+Right ()+>>> unloadGenModel "gemma3"+Right ()+-}+module Data.Ollama.Load+ ( -- * Load and Unload Model APIs+ loadGenModel+ , unloadGenModel+ , loadGenModelM+ , unloadGenModelM+ ) where++import Control.Monad.IO.Class (MonadIO (liftIO))+import Data.Ollama.Common.Error+import Data.Ollama.Common.Utils (commonNonStreamingHandler, withOllamaRequest)+import Data.Ollama.Generate qualified as Gen+import Data.Text (Text)++{- | Loads a generative model into memory.++Sends a POST request to the "/api//generate" endpoint to load the specified model into+memory, ensuring faster response times for subsequent requests. Returns 'Right ()' on+success or 'Left' with an 'OllamaError' on failure.+--+-- @since 0.2.0.0+-}+loadGenModel ::+ -- | Model name (e.g., "gemma3")+ Text ->+ IO (Either OllamaError ())+loadGenModel m = do+ let ops = Gen.defaultGenerateOps {Gen.modelName = m}+ withOllamaRequest "/api//generate" "POST" (Just ops) Nothing commonNonStreamingHandler++{- | Unloads a generative model from memory.++Sends a POST request to the "/api//generate" endpoint with a keep-alive duration of zero+to unload the specified model from memory, freeing up resources. Returns 'Right ()' on+success or 'Left' with an 'OllamaError' on failure.+--+-- @since 0.2.0.0+-}+unloadGenModel ::+ -- | Model name (e.g., "gemma3")+ Text ->+ IO (Either OllamaError ())+unloadGenModel m = do+ let ops = Gen.defaultGenerateOps {Gen.modelName = m, Gen.keepAlive = Just 0}+ withOllamaRequest "/api//generate" "POST" (Just ops) Nothing commonNonStreamingHandler++{- | MonadIO version of 'loadGenModel' for use in monadic contexts.++Lifts the 'loadGenModel' function into a 'MonadIO' context, allowing it to be used in+monadic computations.++Example:++>>> import Control.Monad.IO.Class+>>> runReaderT (loadGenModelM "gemma3") someContext+Right ()+--+-- @since 0.2.0.0+-}+loadGenModelM :: MonadIO m => Text -> m (Either OllamaError ())+loadGenModelM t = liftIO $ loadGenModel t++{- | MonadIO version of 'unloadGenModel' for use in monadic contexts.++Lifts the 'unloadGenModel' function into a 'MonadIO' context, allowing it to be used in+monadic computations.++Example:++>>> import Control.Monad.IO.Class+>>> runReaderT (unloadGenModelM "gemma3") someContext+Right ()+--+-- @since 0.2.0.0+-}+unloadGenModelM :: MonadIO m => Text -> m (Either OllamaError ())+unloadGenModelM t = liftIO $ unloadGenModel t
src/Data/Ollama/Ps.hs view
@@ -1,39 +1,73 @@ {-# LANGUAGE OverloadedStrings #-} +{- |+Module : Data.Ollama.Ps+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : Functionality for listing running models in the Ollama client.++This module provides functions to retrieve a list of models currently running on the Ollama server.+It includes both an IO-based function ('ps') and a monadic version ('psM') for use in 'MonadIO'+contexts. The operation is performed via a GET request to the "/api//ps" endpoint, returning a+'RunningModels' type containing a list of 'RunningModel' records with details about each running model.++Example:++>>> ps Nothing+Right (RunningModels [RunningModel ...])+-} module Data.Ollama.Ps- ( ps+ ( -- * List Running Models API+ ps+ , psM++ -- * Model Types , RunningModels (..) , RunningModel (..) ) where +import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Aeson+import Data.Ollama.Common.Config (OllamaConfig)+import Data.Ollama.Common.Error (OllamaError) import Data.Ollama.Common.Types as CT import Data.Ollama.Common.Utils as CU import Data.Text (Text)-import Data.Text qualified as T import Data.Time import GHC.Int (Int64)-import Network.HTTP.Client-import Network.HTTP.Types.Status (statusCode) --- Types for Ps API-newtype RunningModels = RunningModels [RunningModel]+-- | A wrapper type containing a list of running models.+newtype RunningModels+ = -- | List of 'RunningModel' records describing currently running models.+ RunningModels [RunningModel] deriving (Eq, Show) +-- | Details about a specific running model. data RunningModel = RunningModel- { name_ :: Text- , modelName :: Text- , size_ :: Int64- , modelDigest :: Text- , modelDetails :: ModelDetails- , expiresAt :: UTCTime- , sizeVRam :: Int64+ { name_ :: !Text+ -- ^ The name of the running model instance.+ , modelName :: !Text+ -- ^ The base model name (e.g., "gemma3").+ , size_ :: !Int64+ -- ^ The size of the model in bytes.+ , modelDigest :: !Text+ -- ^ The digest (hash) of the model.+ , modelDetails :: !ModelDetails+ -- ^ Additional details about the model (e.g., format, family, parameters).+ , expiresAt :: !UTCTime+ -- ^ The timestamp when the model's memory allocation expires.+ , sizeVRam :: !Int64+ -- ^ The size of the model's VRAM usage in bytes. } deriving (Eq, Show) +-- | JSON parsing instance for 'RunningModels'. instance FromJSON RunningModels where parseJSON = withObject "Models" $ \v -> RunningModels <$> v .: "models" +-- | JSON parsing instance for 'RunningModel'. instance FromJSON RunningModel where parseJSON = withObject "RunningModel" $ \v -> RunningModel@@ -45,17 +79,31 @@ <*> v .: "expires_at" <*> v .: "size_vram" --- | List running models-ps :: IO (Maybe RunningModels)-ps = do- let url = defaultOllamaUrl- manager <- newManager defaultManagerSettings- request <- parseRequest $ T.unpack (url <> "/api/ps")- response <- httpLbs request manager- if statusCode (responseStatus response) /= 200- then pure Nothing- else do- let res = decode (responseBody response) :: Maybe RunningModels- case res of- Nothing -> pure Nothing- Just l -> pure $ Just l+{- | Retrieves a list of currently running models from the Ollama server.++Sends a GET request to the "/api//ps" endpoint to fetch the list of running models.+Returns 'Right' with a 'RunningModels' containing the list of 'RunningModel' on success,+or 'Left' with an 'OllamaError' on failure.+Example:++>>> ps Nothing+Right (RunningModels [RunningModel {name_ = "gemma3:instance1", modelName = "gemma3", ...}])+-}+ps ::+ -- | Optional 'OllamaConfig' (defaults to 'defaultOllamaConfig' if 'Nothing')+ Maybe OllamaConfig ->+ IO (Either OllamaError RunningModels)+ps mbConfig = do+ withOllamaRequest+ "/api//ps"+ "GET"+ (Nothing :: Maybe Value)+ mbConfig+ commonNonStreamingHandler++{- | MonadIO version of 'ps' for use in monadic contexts.++Lifts the 'ps' function into a 'MonadIO' context, allowing it to be used in monadic computations.+-}+psM :: MonadIO m => Maybe OllamaConfig -> m (Either OllamaError RunningModels)+psM mbCfg = liftIO $ ps mbCfg
src/Data/Ollama/Pull.hs view
@@ -2,113 +2,155 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} +{- |+Module : Data.Ollama.Pull+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : Functionality for pulling models in the Ollama client.++This module provides functions to pull (download) models from the Ollama server. It includes both+high-level ('pull', 'pullM') and low-level ('pullOps', 'pullOpsM') APIs for pulling models, with+support for streaming progress updates and insecure connections. The 'PullOps' type configures the+pull request, and 'PullResp' represents the response containing the status and progress details.++The pull operation is performed via a POST request to the "/api//pull" endpoint. Streaming mode,+when enabled, provides real-time progress updates by printing the remaining bytes to the console.++Example:++>>> pull "gemma3"+Remaining bytes: 123456789+...+Completed+Right (PullResp {status = "success", ...})+-} module Data.Ollama.Pull- ( -- * Downloaded Models+ ( -- * Pull Model API pull , pullOps+ , pullM+ , pullOpsM ) where +import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Aeson-import Data.ByteString.Lazy.Char8 qualified as BSL import Data.Maybe (fromMaybe)+import Data.Ollama.Common.Config (OllamaConfig)+import Data.Ollama.Common.Error (OllamaError)+import Data.Ollama.Common.Types (HasDone (..)) import Data.Ollama.Common.Utils as CU import Data.Text (Text)-import Data.Text qualified as T import GHC.Generics import GHC.Int (Int64)-import Network.HTTP.Client --- TODO: Add Options parameter- -- | Configuration options for pulling a model. data PullOps = PullOps- { name :: Text- -- ^ The name of the model to pull.- , insecure :: Maybe Bool- -- ^ Option to allow insecure connections.- -- If set to 'Just True', the pull operation will allow insecure connections.- , stream :: Maybe Bool- -- ^ Option to enable streaming of the download.- -- If set to 'Just True', the download will be streamed.+ { name :: !Text+ -- ^ The name of the model to pull (e.g., "gemma3").+ , insecure :: !(Maybe Bool)+ -- ^ Optional flag to allow insecure connections. If 'Just True', insecure connections are permitted.+ , stream :: !(Maybe Bool)+ -- ^ Optional flag to enable streaming of the download. If 'Just True', progress updates are streamed. } deriving (Show, Eq, Generic, ToJSON) -- | Response data from a pull operation. data PullResp = PullResp- { status :: Text- -- ^ The status of the pull operation, e.g., "success" or "failure".- , digest :: Maybe Text- -- ^ The digest of the model, if available.- , total :: Maybe Int64+ { status :: !Text+ -- ^ The status of the pull operation (e.g., "success" or "failure").+ , digest :: !(Maybe Text)+ -- ^ The digest (hash) of the model, if available.+ , total :: !(Maybe Int64) -- ^ The total size of the model in bytes, if available.- , completed :: Maybe Int64- -- ^ The number of bytes completed, if available.+ , completed :: !(Maybe Int64)+ -- ^ The number of bytes downloaded, if available. } deriving (Show, Eq, Generic, FromJSON) -{- |-Pull a model with additional options for insecure connections and streaming.-This function interacts directly with the Ollama API to download the specified model.--Example:+instance HasDone PullResp where+ getDone PullResp {..} = status /= "success" -> pullOps "myModel" (Just True) (Just True)+{- | Pulls a model with full configuration. -This will attempt to pull "myModel" with insecure connections allowed and enable streaming.+Sends a POST request to the "/api//pull" endpoint to download the specified model. Supports+streaming progress updates (if 'stream' is 'Just True') and insecure connections (if 'insecure'+is 'Just True'). Prints remaining bytes during streaming and "Completed" when finished.+Returns 'Right' with a 'PullResp' on success or 'Left' with an 'OllamaError' on failure. -} pullOps ::- -- | Model Name+ -- | Model name Text ->- -- | Insecure+ -- | Optional insecure connection flag Maybe Bool ->- -- | Stream+ -- | Optional streaming flag Maybe Bool ->- IO ()-pullOps modelName mInsecure mStream = do- let url = defaultOllamaUrl- manager <- newManager defaultManagerSettings- initialRequest <- parseRequest $ T.unpack (url <> "/api/pull")- let reqBody =- PullOps- { name = modelName- , insecure = mInsecure- , stream = mStream- }- request =- initialRequest- { method = "POST"- , requestBody = RequestBodyLBS $ encode reqBody- }- withResponse request manager $ \response -> do- let go = do- bs <- brRead $ responseBody response- let eRes = decode (BSL.fromStrict bs) :: Maybe PullResp- case eRes of- Nothing -> putStrLn "Something went wrong"- Just res -> do- if status res /= "success"- then do- let completed' = fromMaybe 0 (completed res)- let total' = fromMaybe 0 (total res)- putStrLn $ "Remaining bytes: " <> show (total' - completed')- go- else do- putStrLn "Completed"- go+ -- | Optional 'OllamaConfig' (defaults to 'defaultOllamaConfig' if 'Nothing')+ Maybe OllamaConfig ->+ IO (Either OllamaError PullResp)+pullOps modelName mInsecure mStream mbConfig = do+ withOllamaRequest+ "/api//pull"+ "POST"+ (Just $ PullOps {name = modelName, insecure = mInsecure, stream = mStream})+ mbConfig+ (commonStreamHandler onToken onComplete)+ where+ onToken :: PullResp -> IO ()+ onToken res = do+ let completed' = fromMaybe 0 (completed res)+ let total' = fromMaybe 0 (total res)+ putStrLn $ "Remaining bytes: " <> show (total' - completed') -{- |-Pull a model using default options. This simplifies the pull operation by-not requiring additional options.+ onComplete :: IO ()+ onComplete = putStrLn "Completed" +{- | Simplified API for pulling a model.++A higher-level function that pulls a model using default settings for insecure connections,+streaming, and Ollama configuration. Suitable for basic use cases.+-}+pull ::+ -- | Model name+ Text ->+ IO (Either OllamaError PullResp)+pull modelName = pullOps modelName Nothing Nothing Nothing++{- | MonadIO version of 'pull' for use in monadic contexts.++Lifts the 'pull' function into a 'MonadIO' context, allowing it to be used in monadic computations.+ Example: -> pull "myModel"+>>> import Control.Monad.IO.Class+>>> runReaderT (pullM "gemma3") someContext+Right (PullResp {status = "success", ...})+-}+pullM :: MonadIO m => Text -> m (Either OllamaError PullResp)+pullM t = liftIO $ pull t -This will pull "myModel" using default settings (no insecure connections and no streaming).+{- | MonadIO version of 'pullOps' for use in monadic contexts.++Lifts the 'pullOps' function into a 'MonadIO' context, allowing it to be used in monadic computations+with full configuration options.++Example:++>>> import Control.Monad.IO.Class+>>> runReaderT (pullOpsM "gemma3" Nothing (Just True) Nothing) someContext+Remaining bytes: 123456789+...+Completed+Right (PullResp {status = "success", ...}) -}-pull ::- -- | Model Name+pullOpsM ::+ MonadIO m => Text ->- IO ()-pull modelName = pullOps modelName Nothing Nothing+ Maybe Bool ->+ Maybe Bool ->+ Maybe OllamaConfig ->+ m (Either OllamaError PullResp)+pullOpsM t mbInsecure mbStream mbCfg = liftIO $ pullOps t mbInsecure mbStream mbCfg
src/Data/Ollama/Push.hs view
@@ -2,84 +2,117 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} +{- |+Module : Data.Ollama.Push+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : Functionality for pushing models to the Ollama server.++This module provides functions to push (upload) a model to the Ollama server. It includes+both an IO-based function ('push') and a monadic version ('pushM') for use in 'MonadIO'+contexts. The push operation is performed via a POST request to the "/api//pull" endpoint,+with support for streaming progress updates and insecure connections.++The 'PushOps' type configures the push request, and 'PushResp' represents the response+containing the status and progress details. Streaming mode, when enabled, provides+real-time progress updates by printing to the console.++Example:++>>> push "gemma3" Nothing (Just True) Nothing+Pushing...+Completed+-} module Data.Ollama.Push- ( -- * Push API+ ( -- * Push Model API push- , pushOps+ , pushM ) where +import Control.Monad (void)+import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Aeson-import Data.ByteString.Lazy.Char8 qualified as BSL-import Data.Maybe (fromMaybe)+import Data.Ollama.Common.Config (OllamaConfig)+import Data.Ollama.Common.Types (HasDone (getDone)) import Data.Ollama.Common.Utils as CU import Data.Text (Text)-import Data.Text qualified as T import GHC.Generics import GHC.Int (Int64)-import Network.HTTP.Client --- TODO: Add Options parameter+-- | Configuration options for pushing a model. data PushOps = PushOps- { name :: Text- , insecure :: Maybe Bool- , stream :: Maybe Bool+ { name :: !Text+ -- ^ The name of the model to push (e.g., "gemma3").+ , insecure :: !(Maybe Bool)+ -- ^ Optional flag to allow insecure connections.+ -- If 'Just True', insecure connections are permitted.+ , stream :: !(Maybe Bool)+ -- ^ Optional flag to enable streaming of the upload.+ -- If 'Just True', progress updates are streamed. } deriving (Show, Eq, Generic, ToJSON) +-- | Response data from a push operation. data PushResp = PushResp- { status :: Text- , digest :: Maybe Text- , total :: Maybe Int64+ { status :: !Text+ -- ^ The status of the push operation (e.g., "success" or "failure").+ , digest :: !(Maybe Text)+ -- ^ The digest (hash) of the model, if available.+ , total :: !(Maybe Int64)+ -- ^ The total size of the model in bytes, if available. } deriving (Show, Eq, Generic, FromJSON) --- | Push a model with options-pushOps ::+instance HasDone PushResp where+ getDone PushResp {..} = status /= "success"++{- | Pushes a model to the Ollama server with specified options.++Sends a POST request to the "/api//pull" endpoint to upload the specified model. Supports+streaming progress updates (if 'stream' is 'Just True') and insecure connections (if+'insecure' is 'Just True'). Prints "Pushing..." during streaming and "Completed" when+finished. Returns '()' on completion.+-}+push :: -- | Model name Text ->- -- | Insecure+ -- | Optional insecure connection flag Maybe Bool ->- -- | Stream+ -- | Optional streaming flag Maybe Bool ->+ -- | Optional 'OllamaConfig' (defaults to 'defaultOllamaConfig' if 'Nothing')+ Maybe OllamaConfig -> IO ()-pushOps modelName mInsecure mStream = do- let url = defaultOllamaUrl- manager <- newManager defaultManagerSettings- initialRequest <- parseRequest $ T.unpack (url <> "/api/push")- let reqBody =- PushOps- { name = modelName- , insecure = mInsecure- , stream = mStream- }- request =- initialRequest- { method = "POST"- , requestBody = RequestBodyLBS $ encode reqBody- }- withResponse request manager $ \response -> do- let go = do- bs <- brRead $ responseBody response- let eRes = decode (BSL.fromStrict bs) :: Maybe PushResp- case eRes of- Nothing -> putStrLn "Something went wrong"- Just res -> do- if status res /= "success"- then do- let total' = fromMaybe 0 (total res)- putStrLn $ "Remaining bytes: " <> show total'- go- else do- putStrLn "Completed"- go+push modelName mInsecure mStream mbConfig = do+ void $+ withOllamaRequest+ "/api//push"+ "POST"+ (Just $ PushOps {name = modelName, insecure = mInsecure, stream = mStream})+ mbConfig+ (commonStreamHandler onToken onComplete)+ where+ onToken :: PushResp -> IO ()+ onToken _ = putStrLn "Pushing... " --- Higher level API for Pull--- This API is untested. Will test soon!+ onComplete :: IO ()+ onComplete = putStrLn "Completed" --- | Push a model-push ::- -- | Model name- Text ->- IO ()-push modelName = pushOps modelName Nothing Nothing+{- | MonadIO version of 'push' for use in monadic contexts.++Lifts the 'push' function into a 'MonadIO' context, allowing it to be used in monadic+computations.++Example:++>>> import Control.Monad.IO.Class+>>> runReaderT (pushM "gemma3" Nothing (Just True) Nothing) someContext+Pushing...+Completed+-}+pushM :: MonadIO m => Text -> Maybe Bool -> Maybe Bool -> Maybe OllamaConfig -> m ()+pushM t insec s mbCfg = liftIO $ push t insec s mbCfg
src/Data/Ollama/Show.hs view
@@ -3,108 +3,149 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE OverloadedStrings #-} +{- |+Module : Data.Ollama.Show+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Description : Functionality for retrieving detailed information about models in the Ollama client.++This module provides functions to fetch detailed information about a specific model on the Ollama server.+It includes both high-level ('showModel', 'showModelM') and low-level ('showModelOps', 'showModelOpsM') APIs+for retrieving model details, with support for verbose output. The operation is performed via a POST request+to the "/api//show" endpoint, returning a 'ShowModelResponse' containing comprehensive model metadata.++The 'ShowModelOps' type configures the request, and 'ShowModelResponse' and 'ShowModelInfo' represent the+response structure. The module also re-exports 'CT.ModelDetails' for completeness.++Note: Verbose mode parsing is currently not fully supported.++Example:++>>> showModel "gemma3"+Right (ShowModelResponse {modelFile = "...", ...})++@since 1.0.0.0+-} module Data.Ollama.Show ( -- * Show Model Info API showModel+ , showModelM , showModelOps+ , showModelOpsM++ -- * Response Types , ShowModelResponse (..)+ , ShowModelInfo (..)+ , CT.ModelDetails (..) ) where +import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Aeson-import Data.Ollama.Common.Utils qualified as CU+import Data.Ollama.Common.Config (OllamaConfig)+import Data.Ollama.Common.Error (OllamaError)+import Data.Ollama.Common.Types qualified as CT+import Data.Ollama.Common.Utils (commonNonStreamingHandler, withOllamaRequest) import Data.Text (Text)-import Data.Text qualified as T import GHC.Generics import GHC.Int (Int64)-import Network.HTTP.Client --- TODO: Add Options parameter--- TODO: Add Context parameter--{- |- #ShowModelOps#- Input parameters for show model information.--}+-- | Configuration options for requesting model information. data ShowModelOps = ShowModelOps- { name :: Text- , verbose :: Maybe Bool+ { name :: !Text+ -- ^ The name of the model to query (e.g., "gemma3").+ , verbose :: !(Maybe Bool)+ -- ^ Optional flag to request verbose output. Note: Verbose mode parsing is currently incomplete. } deriving (Show, Eq, Generic, ToJSON) -{- |- #ShowModelResponse#-- Ouput structure for show model information.--}+-- | Response structure for model information. data ShowModelResponse = ShowModelResponse- { modelFile :: Text- , parameters :: Text- , template :: Text- , details :: ModelDetails- , modelInfo :: ModelInfo- }- deriving (Show, Eq)--data ModelDetails = ModelDetails- { parentModel :: Text- , format :: Text- , familiy :: Text- , families :: [Text]- , parameterSize :: Text- , quantizationLevel :: Text+ { modelFile :: !Text+ -- ^ The content of the model's Modelfile.+ , parameters :: !(Maybe Text)+ -- ^ Optional model parameters (e.g., temperature settings).+ , template :: !(Maybe Text)+ -- ^ Optional template used for the model.+ , details :: !CT.ModelDetails+ -- ^ General details about the model (e.g., format, family).+ , modelInfo :: !ShowModelInfo+ -- ^ Detailed technical information about the model.+ , license :: !(Maybe Text)+ -- ^ Optional license information for the model.+ --+ -- @since 0.2.0.0+ , capabilities :: Maybe [Text]+ -- ^ Optional list of model capabilities.+ --+ -- @since 0.2.0.0 } deriving (Show, Eq) -data ModelInfo = ModelInfo- { generalArchitecture :: Maybe Text- , generalFileType :: Maybe Int- , generalParameterCount :: Maybe Int64- , generalQuantizationVersion :: Maybe Int- , llamaAttentionHeadCount :: Maybe Int- , llamaAttentionHeadCountKV :: Maybe Int- , llamaAttentionLayerNormRMSEpsilon :: Maybe Float- , llamaBlockCount :: Maybe Int- , llamaContextLength :: Maybe Int- , llamaEmbeddingLength :: Maybe Int- , llamaFeedForwardLength :: Maybe Int- , llamaRopeDimensionCount :: Maybe Int- , llamaRopeFreqBase :: Maybe Int64- , llamaVocabSize :: Maybe Int64- , tokenizerGgmlBosToken_id :: Maybe Int- , tokenizerGgmlEosToken_id :: Maybe Int- , tokenizerGgmlMerges :: Maybe [Text]- , tokenizerGgmlMode :: Maybe Text- , tokenizerGgmlPre :: Maybe Text- , tokenizerGgmlTokenType :: Maybe [Text]- , tokenizerGgmlTokens :: Maybe [Text]+-- | Detailed technical information about a model.+data ShowModelInfo = ShowModelInfo+ { generalArchitecture :: !(Maybe Text)+ -- ^ The architecture of the model (e.g., "llama").+ , generalFileType :: !(Maybe Int)+ -- ^ The file type identifier for the model.+ , generalParameterCount :: !(Maybe Int64)+ -- ^ The number of parameters in the model.+ , generalQuantizationVersion :: !(Maybe Int)+ -- ^ The quantization version used by the model.+ , llamaAttentionHeadCount :: !(Maybe Int)+ -- ^ Number of attention heads in the LLaMA model.+ , llamaAttentionHeadCountKV :: !(Maybe Int)+ -- ^ Number of key-value attention heads in the LLaMA model.+ , llamaAttentionLayerNormRMSEpsilon :: !(Maybe Float)+ -- ^ RMS epsilon for layer normalization in the LLaMA model.+ , llamaBlockCount :: !(Maybe Int)+ -- ^ Number of blocks in the LLaMA model.+ , llamaContextLength :: !(Maybe Int)+ -- ^ Context length supported by the LLaMA model.+ , llamaEmbeddingLength :: !(Maybe Int)+ -- ^ Embedding length used by the LLaMA model.+ , llamaFeedForwardLength :: !(Maybe Int)+ -- ^ Feed-forward layer length in the LLaMA model.+ , llamaRopeDimensionCount :: !(Maybe Int)+ -- ^ RoPE dimension count in the LLaMA model.+ , llamaRopeFreqBase :: !(Maybe Int64)+ -- ^ Base frequency for RoPE in the LLaMA model.+ , llamaVocabSize :: !(Maybe Int64)+ -- ^ Vocabulary size of the LLaMA model.+ , tokenizerGgmlBosToken_id :: !(Maybe Int)+ -- ^ BOS (beginning of sequence) token ID for the GGML tokenizer.+ , tokenizerGgmlEosToken_id :: !(Maybe Int)+ -- ^ EOS (end of sequence) token ID for the GGML tokenizer.+ , tokenizerGgmlMerges :: !(Maybe [Text])+ -- ^ List of merges for the GGML tokenizer.+ , tokenizerGgmlMode :: !(Maybe Text)+ -- ^ Mode of the GGML tokenizer.+ , tokenizerGgmlPre :: !(Maybe Text)+ -- ^ Pre-tokenization configuration for the GGML tokenizer.+ , tokenizerGgmlTokenType :: !(Maybe [Text])+ -- ^ Token type information for the GGML tokenizer.+ , tokenizerGgmlTokens :: !(Maybe [Text])+ -- ^ List of tokens for the GGML tokenizer. } deriving (Show, Eq) --- FromJSON instances---- | The instance for show model response+-- | JSON parsing instance for 'ShowModelResponse'. instance FromJSON ShowModelResponse where parseJSON = withObject "ShowModelResponse" $ \v -> ShowModelResponse <$> v .: "modelfile"- <*> v .: "parameters"- <*> v .: "template"+ <*> v .:? "parameters"+ <*> v .:? "template" <*> v .: "details" <*> v .: "model_info"--instance FromJSON ModelDetails where- parseJSON = withObject "ModelDetails" $ \v ->- ModelDetails- <$> v .: "parent_model"- <*> v .: "format"- <*> v .: "family"- <*> v .: "families"- <*> v .: "parameter_size"- <*> v .: "quantization_level"+ <*> v .:? "license"+ <*> v .:? "capabilities" -instance FromJSON ModelInfo where+-- | JSON parsing instance for 'ShowModelInfo'.+instance FromJSON ShowModelInfo where parseJSON = withObject "ModelInfo" $ \v ->- ModelInfo+ ShowModelInfo <$> v .:? "general.architecture" <*> v .:? "general.file_type" <*> v .:? "general.parameter_count"@@ -127,49 +168,63 @@ <*> v .:? "tokenizer.ggml.token_type" <*> v .:? "tokenizer.ggml.tokens" -{- | Show given model's information with options.+{- | Retrieves model information with configuration options. -@since 1.0.0.0+Sends a POST request to the "/api//show" endpoint to fetch detailed information about+the specified model. Supports verbose output if 'verbose' is 'Just True' (though verbose+mode parsing is currently incomplete). Returns 'Right' with a 'ShowModelResponse' on+success or 'Left' with an 'OllamaError' on failure. -} showModelOps ::- -- | model name+ -- | Model name Text ->- -- | verbose+ -- | Optional verbose flag Maybe Bool ->- IO (Maybe ShowModelResponse)-showModelOps- modelName- verbose_ =- do- let url = CU.defaultOllamaUrl- manager <- newManager defaultManagerSettings- initialRequest <- parseRequest $ T.unpack (url <> "/api/show")- let reqBody =- ShowModelOps- { name = modelName- , verbose = verbose_- }- request =- initialRequest- { method = "POST"- , requestBody = RequestBodyLBS $ encode reqBody- }- response <- httpLbs request manager- let eRes =- eitherDecode (responseBody response) ::- Either String ShowModelResponse- case eRes of- Left _ -> pure Nothing- Right r -> pure $ Just r+ -- | Optional 'OllamaConfig' (defaults to 'defaultOllamaConfig' if 'Nothing')+ Maybe OllamaConfig ->+ IO (Either OllamaError ShowModelResponse)+showModelOps modelName verbose_ mbConfig = do+ withOllamaRequest+ "/api//show"+ "POST"+ ( Just $+ ShowModelOps+ { name = modelName+ , verbose = verbose_+ }+ )+ mbConfig+ commonNonStreamingHandler -{- | Show given model's information.+{- | Simplified API for retrieving model information. -Higher level API for show.-@since 1.0.0.0+A higher-level function that fetches model information using default settings for+verbose output and Ollama configuration. Suitable for basic use cases. -} showModel ::- -- | model name+ -- | Model name Text ->- IO (Maybe ShowModelResponse)+ IO (Either OllamaError ShowModelResponse) showModel modelName =- showModelOps modelName Nothing+ showModelOps modelName Nothing Nothing++{- | MonadIO version of 'showModel' for use in monadic contexts.++Lifts the 'showModel' function into a 'MonadIO' context, allowing it to be used in+monadic computations.+-}+showModelM :: MonadIO m => Text -> m (Either OllamaError ShowModelResponse)+showModelM t = liftIO $ showModel t++{- | MonadIO version of 'showModelOps' for use in monadic contexts.++Lifts the 'showModelOps' function into a 'MonadIO' context, allowing it to be used in+monadic computations with full configuration options.+-}+showModelOpsM ::+ MonadIO m =>+ Text ->+ Maybe Bool ->+ Maybe OllamaConfig ->+ m (Either OllamaError ShowModelResponse)+showModelOpsM t v mbCfg = liftIO $ showModelOps t v mbCfg
src/Ollama.hs view
@@ -1,94 +1,194 @@ {-# LANGUAGE DuplicateRecordFields #-}+ {- |- #Ollama-Haskell#- This library lets you run LlMs from within Haskell projects. Inspired by `ollama-python`.+Module : Data.Ollama+Copyright : (c) 2025 Tushar Adhatrao+License : MIT+Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>+Stability : experimental+Portability : portable++== Ollama Haskell++This module provides a high-level Haskell interface to the [Ollama](https://ollama.com) API + for interacting with local LLMs. It includes support for:++- Text generation (sync/streaming)+- Conversational chat (with tools and images)+- Embeddings+- Model management (pull, push, delete, list, show)+- Structured outputs+- Custom configuration and model options++Inspired by @ollama-python@, this library is built to offer idiomatic Haskell bindings +over Ollama’s HTTP API.++== 🔧 Usage++Import this module as a top-level interface:++@+import Ollama+@++All functions return @Either OllamaError a@ or can be used in a Monad stack using +their @\*M@ variants.++== 🔑 Main APIs++=== ✍️ Generate Text++- 'generate', 'generateM' – Generate text from a model+- 'defaultGenerateOps' – Default generation parameters+- 'GenerateOps', 'GenerateResponse' – Request and response types++=== 💬 Chat with LLMs++- 'chat', 'chatM' – Send chat messages to a model+- 'ChatOps', 'ChatResponse', 'Role', 'Message' – Chat input/output types+- Supports tools via 'InputTool', 'FunctionDef', 'OutputFunction', etc.++=== 🧠 Embeddings++- 'embedding', 'embeddingM' – Generate vector embeddings+- 'EmbeddingOps', 'EmbeddingResp' – Request/response types++=== 📦 Model Management++- 'copyModel', 'createModel', 'deleteModel'+- 'list' – List all installed models+- 'ps', 'psM' – Show running models+- 'showModel', 'showModelM' – Show model info+- 'pull', 'push' – Pull/push models (with progress support)++=== ⚙️ Configuration++- 'defaultOllamaConfig' – Modify host, retries, streaming, etc.+- 'withOnModelStart', 'withOnModelFinish', 'withOnModelError' – Hook support++=== 🧰 Utilities++- 'defaultModelOptions', 'encodeImage', 'withOllamaRequest'+- 'loadGenModel', 'unloadGenModel' – Load/unload generation models+- 'getVersion' – Ollama server version++== 🧾 Types++All request/response payloads and enums are exposed, including:++- 'ModelOptions', 'OllamaConfig', 'OllamaError', 'Format'+- 'Models', 'ModelInfo', 'ModelDetails', 'ShowModelResponse'+- 'RunningModels', 'RunningModel', 'Version' -} module Ollama ( -- * Main APIs -- ** Generate Texts generate- , generateJson+ , generateM , defaultGenerateOps , GenerateOps (..) , GenerateResponse (..) -- ** Chat with LLMs , chat- , chatJson+ , chatM , Role (..) , defaultChatOps , ChatResponse (..) , ChatOps (..)+ , InputTool (..)+ , FunctionDef (..)+ , FunctionParameters (..)+ , ToolCall (..)+ , OutputFunction (..) -- ** Embeddings , embedding , embeddingOps+ , embeddingM+ , embeddingOpsM , EmbeddingOps (..) , EmbeddingResp (..) - -- * Ollama operations- -- ** Copy Models , copyModel+ , copyModelM -- ** Create Models , createModel- , createModelOps+ , createModelM -- ** Delete Models , deleteModel+ , deleteModelM -- ** List Models , list -- ** List currently running models , ps+ , psM -- ** Push and Pull , push- , pushOps+ , pushM , pull+ , pullM , pullOps+ , pullOpsM -- ** Show Model Info , showModel , showModelOps+ , showModelM+ , showModelOpsM + -- * Ollama config+ , defaultOllamaConfig+ , withOnModelStart+ , withOnModelFinish+ , withOnModelError++ -- * Utils+ , defaultModelOptions+ , ModelOptions (..)+ , encodeImage+ , withOllamaRequest+ , getVersion+ , loadGenModel+ , unloadGenModel+ , loadGenModelM+ , unloadGenModelM+ -- * Types , ShowModelResponse (..) , Models (..) , ModelInfo (..)+ , ModelDetails (..)+ , ShowModelInfo (..) , RunningModels (..) , RunningModel (..) , Message (..)- , Format(..)+ , Format (..)+ , OllamaError (..)+ , OllamaConfig (..)+ , Version (..) ) where import Data.Ollama.Chat- ( ChatOps (..)- , ChatResponse (..)- , Message (..)- , Role (..)- , chat- , chatJson- , defaultChatOps- )-import Data.Ollama.Copy (copyModel)-import Data.Ollama.Create (createModel, createModelOps)-import Data.Ollama.Delete (deleteModel)-import Data.Ollama.Embeddings (embedding, embeddingOps, EmbeddingOps (..), EmbeddingResp (..))+import Data.Ollama.Common.Config+import Data.Ollama.Common.Types+import Data.Ollama.Common.Utils+import Data.Ollama.Copy+import Data.Ollama.Create+import Data.Ollama.Delete+import Data.Ollama.Embeddings import Data.Ollama.Generate- ( GenerateOps (..)- , GenerateResponse (..)- , defaultGenerateOps- , generate- , generateJson- )-import Data.Ollama.List (ModelInfo (..), Models (..), list)-import Data.Ollama.Ps (RunningModel (..), RunningModels (..), ps)-import Data.Ollama.Pull (pull, pullOps)-import Data.Ollama.Push (push, pushOps)-import Data.Ollama.Show (ShowModelResponse (..), showModel, showModelOps)-import Data.Ollama.Common.Types (Format (..))+import Data.Ollama.List+import Data.Ollama.Load+import Data.Ollama.Ps+import Data.Ollama.Pull+import Data.Ollama.Push+import Data.Ollama.Show
test/Main.hs view
@@ -2,211 +2,29 @@ module Main (main) where -import Data.Either-import Data.List.NonEmpty hiding (length)-import Data.Maybe-import Data.Ollama.Chat qualified as Chat-import Data.Text.IO qualified as T-import Ollama (GenerateOps (..), Role (..), defaultChatOps, defaultGenerateOps)-import Ollama qualified-import System.IO.Silently (capture)+import Data.Text (unpack)+import Ollama (Version (..), getVersion)+import Test.Ollama.Chat qualified as Chat+import Test.Ollama.Embedding qualified as Embeddings+import Test.Ollama.Generate qualified as Generate+import Test.Ollama.Show qualified as Show import Test.Tasty-import Test.Tasty.HUnit-import qualified Data.Text as T -import Data.Aeson tests :: TestTree tests = testGroup "Tests"- [ generateTest- , chatTest- , psTest- , showTest- , embeddingTest- , generateFormatTest- , chatFormatTest- ]--generateTest :: TestTree-generateTest =- testGroup- "Generate tests"- [ testCase "generate stream" $ do- output <-- capture $- Ollama.generate- defaultGenerateOps- { modelName = "llama3.2"- , prompt = "what is 4 + 2?"- , stream = Just (T.putStr . Ollama.response_, pure ())- }- assertBool "Checking if generate function is printing anything" (length output > 0)- , testCase "Generate non-stream" $ do- eRes <-- Ollama.generate- defaultGenerateOps- { modelName = "llama3.2"- , prompt = "what is 4 + 2?"- }- assertBool "Checking if generate function returns a valid value" (isRight eRes)- , testCase "Generate with invalid host" $ do- eRes <- Ollama.generate defaultGenerateOps {- modelName = "llama3.2"- , prompt = "what is 23 + 9?"- , hostUrl = pure "http://some-site"- , responseTimeOut = pure 2- }- print ("got response" :: String ,eRes)- assertBool "Expecting Left" (isLeft eRes)- , testCase "Generate with invalid model" $ do- eRes <- Ollama.generate defaultGenerateOps {modelName = "invalid-model"}- assertBool "Expecting generation to fail with invalid model" (isLeft eRes)- ]--generateFormatTest :: TestTree-generateFormatTest =- testGroup "Generate tests with format and options"- [ testCase "Generate with SchemaFormat and options" $ do- let schema = object- [ "type" .= ("object" :: String)- , "properties" .= object [ "age" .= object ["type" .= ("integer" :: String)] ]- ]- eRes <-- Ollama.generate- defaultGenerateOps- { modelName = "llama3.2"- , prompt = "Ollama is 22 years old and is busy saving the world. Respond using JSON"- , format = Just (Ollama.SchemaFormat schema)- }- case eRes of- Right res ->- assertBool "Response should contain JSON with key \"age\"" ( "age" `T.isInfixOf` Ollama.response_ res )- Left err ->- assertFailure $ "Generation failed with error: " ++ show err- , testCase "Generate with JsonFormat and options" $ do- eRes <-- Ollama.generate- defaultGenerateOps- { modelName = "llama3.2"- , prompt = "Provide a simple JSON response describing Ollama."- , format = Just Ollama.JsonFormat- }- case eRes of- Right res ->- assertBool "Response should start with '{'" ( "{" `T.isPrefixOf` Ollama.response_ res )- Left err ->- assertFailure $ "Generation failed with error: " ++ show err- ]--chatTest :: TestTree-chatTest =- testGroup- "Chat tests"- [ testCase "chat stream" $ do- let msg = Ollama.Message User "What is 29 + 3?" Nothing Nothing- defaultMsg = Ollama.Message User "" Nothing Nothing- output <-- capture $- Ollama.chat- defaultChatOps- { Chat.chatModelName = "llama3.2"- , Chat.messages = msg :| []- , Chat.stream = Just (T.putStr . Chat.content . fromMaybe defaultMsg . Chat.message, pure ())- }- assertBool "Checking if chat function is printing anything" (length output > 0)- , testCase "Chat non-stream" $ do- let msg = Ollama.Message User "What is 29 + 3?" Nothing Nothing- eRes <-- Ollama.chat- defaultChatOps- { Chat.chatModelName = "llama3.2"- , Chat.messages = msg :| []- }- assertBool "Checking if chat function returns a valid value" (isRight eRes)- , testCase "Chat invalid host url" $ do- let msg = Ollama.Message User "What is 29 + 3?" Nothing Nothing- eRes <-- Ollama.chat- defaultChatOps- { Chat.chatModelName = "llama3.2"- , Chat.messages = msg :| []- , Chat.hostUrl = pure "some random value"- , Chat.responseTimeOut = pure 2- }- assertBool "It should return Left" (isLeft eRes)- ]--chatFormatTest :: TestTree-chatFormatTest =- testGroup "Chat tests with format and options"- [ testCase "Chat with SchemaFormat and options" $ do- let schema = object- [ "type" .= ("object" :: String)- , "properties" .= object [ "age" .= object ["type" .= ("integer" :: String)] ]- ]- msg = Ollama.Message User "Ollama is 22 years old and is busy saving the world. Respond using JSON" Nothing Nothing- eRes <-- Ollama.chat- defaultChatOps- { Chat.chatModelName = "llama3.2"- , Chat.messages = msg :| []- , Chat.format = Just (Ollama.SchemaFormat schema)- , Chat.options = Just $ object ["penalize_newline" .= Bool True]- }- case eRes of- Right res ->- assertBool "Chat response should contain key \"age\"" $- maybe False (\m -> "age" `T.isInfixOf` Chat.content m) (Chat.message res)- Left err ->- assertFailure $ "Chat failed with error: " ++ show err- , testCase "Chat with JsonFormat and options" $ do- let msg = Ollama.Message User "Tell me about Ollama in JSON format." Nothing Nothing- eRes <-- Ollama.chat- defaultChatOps- { Chat.chatModelName = "llama3.2"- , Chat.messages = msg :| []- , Chat.format = Just Ollama.JsonFormat- }- case eRes of- Right res ->- assertBool "Chat response should start with '{'" $- maybe False (\m -> "{" `T.isPrefixOf` Chat.content m) (Chat.message res)- Left err ->- assertFailure $ "Chat failed with error: " ++ show err- ]--psTest :: TestTree-psTest =- testGroup- "PS test"- [ testCase "check ps" $ do- mRes <- Ollama.ps- assertBool "Check if ps returns anything" (isJust mRes)- ]--showTest :: TestTree-showTest =- testGroup- "Show test"- [ testCase "check show" $ do- mRes <- Ollama.showModel "llama3.2"- assertBool "Check if model exists or not" (isJust mRes)- ]--embeddingTest :: TestTree-embeddingTest =- testGroup- "Embedding test"- [ testCase "check embedding" $ do- eRes <- Ollama.embedding "llama3.2" "Why is sky blue?"- assertBool "Check if embedding returns anything" (isRight eRes)+ [ Generate.tests+ , Chat.tests+ , Show.tests+ , Embeddings.tests ] main :: IO () main = do- mRes <- Ollama.list- case mRes of- Nothing -> pure () -- Ollama is likely not running. Not running tests.- Just _ -> defaultMain tests+ eRes <- getVersion+ case eRes of+ Left err -> putStrLn $ show err+ Right (Version r) -> do+ putStrLn $ "Ollama client version: " <> unpack r+ defaultMain tests
+ test/Test/Ollama/Chat.hs view
@@ -0,0 +1,278 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}++module Test.Ollama.Chat (tests) where++import Control.Monad (void)+import Data.Aeson qualified as Aeson+import Data.ByteString.Lazy.Char8 qualified as BSL+import Data.IORef (modifyIORef, newIORef, readIORef, writeIORef)+import Data.List.NonEmpty (fromList)+import Data.List.NonEmpty qualified as NE+import Data.Map qualified as HM+import Data.Maybe (isJust)+import Data.Ollama.Chat+import Data.Scientific+import Data.Text qualified as T+import Data.Time (diffUTCTime, getCurrentTime)+import Network.HTTP.Client+import Network.HTTP.Client.TLS+import Test.Tasty+import Test.Tasty.HUnit++-- | Basic chat test with default options+basicChatTest :: TestTree+basicChatTest = testCase "Basic chat should contain 4 for 2+2" $ do+ let ops = defaultChatOps+ eRes <- chat ops Nothing+ case eRes of+ Left err -> assertFailure $ "Expected success, got error: " ++ show err+ Right r -> case message r of+ Nothing -> assertFailure "Expected a message in response"+ Just msg -> assertBool "Should contain '4'" (T.isInfixOf "4" (content msg))++-- | Test timeout configuration+timeoutTest :: TestTree+timeoutTest = testCase "Setting timeout" $ do+ let config = Just $ defaultOllamaConfig {timeout = 1}+ eRes <- chat defaultChatOps config+ case eRes of+ Left (TimeoutError _) -> pure ()+ _ -> assertFailure "Expected timeout error"++-- | Test model lifecycle hooks on failure+hooksFailTest :: TestTree+hooksFailTest = testCase "Model lifecycle hooks should trigger on failure" $ do+ refStart <- newIORef False+ refError <- newIORef False+ refFinish <- newIORef True+ let config =+ defaultOllamaConfig+ { hostUrl = "http://localhost:12345" -- Guaranteed to fail+ , onModelStart = Just $ writeIORef refStart True+ , onModelError = Just $ writeIORef refError True+ , onModelFinish = Just $ writeIORef refFinish False+ }+ void $ chat defaultChatOps (Just config)+ wasStarted <- readIORef refStart+ wasErrored <- readIORef refError+ wasFinished <- readIORef refFinish+ assertBool "onModelStart should be called" wasStarted+ assertBool "onModelError should be called" wasErrored+ assertBool "onModelFinish should be called" wasFinished++-- | Test model lifecycle hooks on success+hooksSuccessTest :: TestTree+hooksSuccessTest = testCase "Model lifecycle hooks should trigger on success" $ do+ refStart <- newIORef False+ refError <- newIORef True+ refFinish <- newIORef False+ let config =+ defaultOllamaConfig+ { onModelStart = Just $ writeIORef refStart True+ , onModelError = Just $ writeIORef refError False+ , onModelFinish = Just $ writeIORef refFinish True+ }+ void $ chat defaultChatOps (Just config)+ wasStarted <- readIORef refStart+ wasErrored <- readIORef refError+ wasFinished <- readIORef refFinish+ assertBool "onModelStart should be called" wasStarted+ assertBool "onModelError should not be called" wasErrored+ assertBool "onModelFinish should be called" wasFinished++-- | Test retry count+retryCountTest :: TestTree+retryCountTest = testCase "Should retry chat call retryCount times" $ do+ counter <- newIORef (0 :: Int)+ let config =+ defaultOllamaConfig+ { hostUrl = "http://localhost:12345" -- Fails+ , retryCount = Just 2+ , retryDelay = Just 1+ , onModelStart = Just $ modifyIORef counter (+ 1)+ , onModelError = Just $ pure ()+ , onModelFinish = Just $ pure ()+ }+ _ <- chat defaultChatOps (Just config)+ calls <- readIORef counter+ assertEqual "Expected 3 attempts (1 initial + 2 retries)" 3 calls++-- | Test retry delay+retryDelayTest :: TestTree+retryDelayTest = testCase "Should delay between retries" $ do+ counter <- newIORef (0 :: Int)+ let delaySecs = 2+ start <- getCurrentTime+ let config =+ defaultOllamaConfig+ { hostUrl = "http://localhost:12345" -- Fails+ , retryCount = Just 1+ , retryDelay = Just delaySecs+ , onModelStart = Just $ modifyIORef counter (+ 1)+ , onModelError = Just $ pure ()+ , onModelFinish = Just $ pure ()+ }+ _ <- chat defaultChatOps (Just config)+ end <- getCurrentTime+ let elapsed = realToFrac (diffUTCTime end start) :: Double+ expectedMin = fromIntegral delaySecs+ assertBool+ ("Elapsed time should be at least " ++ show expectedMin ++ "s, but was " ++ show elapsed)+ (elapsed >= expectedMin)++-- | Test common manager usage+commonManagerTest :: TestTree+commonManagerTest = testCase "Should reuse provided commonManager" $ do+ refStart <- newIORef (0 :: Int)+ mgr <-+ newTlsManagerWith+ tlsManagerSettings {managerResponseTimeout = responseTimeoutMicro 1000000}+ let config =+ defaultOllamaConfig+ { hostUrl = "http://localhost:12345" -- Will fail fast+ , commonManager = Just mgr+ , timeout = 999 -- Shouldn’t matter, manager timeout takes precedence+ , onModelStart = Just $ modifyIORef refStart (+ 1)+ , onModelError = Just $ pure ()+ , onModelFinish = Just $ pure ()+ }+ _ <- chat defaultChatOps (Just config)+ _ <- chat defaultChatOps (Just config)+ startCount <- readIORef refStart+ assertEqual "Both requests should start (reuse manager)" 2 startCount++-- | Test JSON format response+jsonFormatTest :: TestTree+jsonFormatTest = testCase "Should return response in JSON format" $ do+ let ops =+ defaultChatOps+ { messages =+ fromList+ [userMessage "Return a JSON with keys 'name' and 'age' for John, 25 years old."]+ , format = Just JsonFormat+ }+ eRes <- chat ops Nothing+ case eRes of+ Left err -> assertFailure $ "Expected success, got error: " ++ show err+ Right r -> case message r of+ Nothing -> assertFailure "Expected a message in response"+ Just msg -> do+ let responseText = content msg+ let decoded = Aeson.decode (BSL.pack $ T.unpack responseText) :: Maybe Aeson.Value+ assertBool "Expected valid JSON object in response" (decoded /= Nothing)++-- | Test streaming response+streamingTest :: TestTree+streamingTest = testCase "Should handle streaming response" $ do+ chunksRef <- newIORef []+ let streamHandler chunk = modifyIORef chunksRef (++ [message chunk])+ flushHandler = pure ()+ ops = defaultChatOps {stream = Just (streamHandler, flushHandler)}+ eRes <- chat ops Nothing+ chunks <- readIORef chunksRef+ let fullOutput = T.concat (map (maybe "" content) chunks)+ case eRes of+ Left err -> assertFailure $ "Expected streaming success, got error: " ++ show err+ Right _ -> assertBool "Expected some streamed content" (not $ T.null fullOutput)++-- | Test custom model options+modelOptionsTest :: TestTree+modelOptionsTest = testCase "Should use custom model options" $ do+ let opts =+ Just $+ defaultModelOptions+ { temperature = Just 0.9+ , topP = Just 0.8+ , topK = Nothing+ , numPredict = Just 20+ }+ ops = defaultChatOps {options = opts}+ eRes <- chat ops Nothing+ case eRes of+ Left err -> assertFailure $ "Expected success, got error: " ++ show err+ Right r -> assertBool "Expected a response message" (isJust (message r))++testToolCall_addTwoNumbers :: TestTree+testToolCall_addTwoNumbers = testCase "Tool call: addTwoNumbers(23, 46)" $ do+ let messageList = NE.singleton $ userMessage "What is 23 + 46? (Use tool)"+ paramProps =+ HM.fromList+ [ ("a", FunctionParameters "number" Nothing Nothing Nothing)+ , ("b", FunctionParameters "number" Nothing Nothing Nothing)+ ]+ functionParams =+ FunctionParameters+ { parameterType = "object"+ , requiredParams = Just ["a", "b"]+ , parameterProperties = Just paramProps+ , additionalProperties = Just False+ }+ functionDef =+ FunctionDef+ { functionName = "addTwoNumbers"+ , functionDescription = Just "Add two numbers"+ , functionParameters = Just functionParams+ , functionStrict = Nothing+ }+ tool =+ InputTool+ { toolType = "function"+ , function = functionDef+ }+ ops =+ defaultChatOps+ { chatModelName = "qwen3:0.6b"+ , messages = messageList+ , tools = Just [tool]+ }++ res <- chat ops Nothing+ case res of+ Left err -> assertFailure $ "Chat failed: " ++ show err+ Right ChatResponse {message = Nothing} -> assertFailure "No message in response"+ Right ChatResponse {message = Just msg} ->+ case tool_calls msg of+ Nothing -> assertFailure "No tool calls received"+ Just [toolCall] -> do+ result <- captureAddToolCall toolCall+ assertEqual "Expected result of 23 + 46" 69 result+ Just other -> assertFailure $ "Unexpected number of tool calls: " ++ show other++-- Helper to evaluate the tool call+captureAddToolCall :: ToolCall -> IO Int+captureAddToolCall (ToolCall func)+ | outputFunctionName func == "addTwoNumbers" =+ case ( HM.lookup "a" (arguments func) >>= convertToNumber+ , HM.lookup "b" (arguments func) >>= convertToNumber+ ) of+ (Just a, Just b) -> return $ addTwoNumbers a b+ _ -> assertFailure "Missing parameters a or b" >> return 0+ | otherwise = assertFailure "Unexpected function name" >> return 0++addTwoNumbers :: Int -> Int -> Int+addTwoNumbers = (+)++-- Convert Aeson value to Int+convertToNumber :: Aeson.Value -> Maybe Int+convertToNumber (Aeson.Number n) = toBoundedInteger n+convertToNumber _ = Nothing++-- | Group all tests+tests :: TestTree+tests =+ sequentialTestGroup+ "Chat tests"+ AllFinish+ [ basicChatTest+ , timeoutTest+ , hooksFailTest+ , hooksSuccessTest+ , retryCountTest+ , retryDelayTest+ , commonManagerTest+ , jsonFormatTest+ , streamingTest+ , modelOptionsTest+ , testToolCall_addTwoNumbers+ ]
+ test/Test/Ollama/Embedding.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Test.Ollama.Embedding (tests) where++import Data.Maybe (listToMaybe)+import Data.Ollama.Embeddings+import Test.Tasty+import Test.Tasty.HUnit++testEmbeddingBasic :: TestTree+testEmbeddingBasic = testCase "Basic embedding with qwen3" $ do+ res <- embedding "qwen3:0.6b" ["The sky is blue.", "Cats are independent."]+ case res of+ Left err -> assertFailure $ "Expected success, got error: " ++ show err+ Right EmbeddingResp {..} -> do+ assertEqual "Should return two embeddings" 2 (length respondedEmbeddings)+ assertBool "Embeddings should not be empty" (all (not . null) respondedEmbeddings)++testEmbeddingWithOptions :: TestTree+testEmbeddingWithOptions = testCase "Embedding with truncate and keepAlive" $ do+ let opts = defaultModelOptions {numKeep = Just 5, seed = Just 42}+ res <- embeddingOps "qwen3:0.6b" ["Hello world"] (Just True) (Just 30) (Just opts) Nothing+ case res of+ Left err -> assertFailure $ "Unexpected error: " ++ show err+ Right EmbeddingResp {..} -> do+ assertEqual "Should return one embedding" 1 (length respondedEmbeddings)+ assertBool+ "Embedding vector should not be empty"+ (not . null $ listToMaybe respondedEmbeddings)++testEmbeddingInvalidModel :: TestTree+testEmbeddingInvalidModel = testCase "Embedding with invalid model name" $ do+ res <- embedding "nonexistent-model" ["This should fail."]+ case res of+ Left _ -> return () -- Expected failure+ Right _ -> assertFailure "Expected failure with invalid model name"++tests :: TestTree+tests =+ testGroup+ "Embeddings tests"+ [ testEmbeddingBasic+ , testEmbeddingWithOptions+ , testEmbeddingInvalidModel+ ]
+ test/Test/Ollama/Generate.hs view
@@ -0,0 +1,319 @@+{-# LANGUAGE OverloadedStrings #-}++{-+ Tests related to Generate module+-}+module Test.Ollama.Generate (tests) where++import Control.Monad (void)+import Data.Aeson qualified as Aeson+import Data.ByteString.Lazy.Char8 qualified as BSL+import Data.IORef (modifyIORef, newIORef, readIORef, writeIORef)+import Data.Ollama.Common.SchemaBuilder+import Data.Ollama.Common.Utils (encodeImage)+import Data.Ollama.Generate+import Data.Text qualified as T+import Data.Time (diffUTCTime, getCurrentTime)+import Network.HTTP.Client+import Network.HTTP.Client.TLS+import Test.Tasty+import Test.Tasty.HUnit++generateTests :: TestTree+generateTests =+ testGroup+ "Generation with various options"+ [ testCase "Should contain 4 in 2+2" $ do+ eRes <-+ generate+ defaultGenerateOps {modelName = "gemma3", prompt = "What is 2+2?"}+ Nothing+ case eRes of+ Left err -> assertFailure $ "Expected success, got error: " ++ show err+ Right r -> assertBool "Should contain 4" (T.isInfixOf "4" (genResponse r))+ , testCase "Setting timeout" $ do+ eRes <-+ generate+ defaultGenerateOps {modelName = "gemma3", prompt = "Write a poem about French revolution"}+ (Just $ defaultOllamaConfig {timeout = 1})+ case eRes of+ Left (TimeoutError _) -> pure ()+ _ -> assertFailure "Expected timeout error"+ ]++testOnModelHooksFail :: TestTree+testOnModelHooksFail = testCase "Model lifecycle hooks should be triggered" $ do+ refStart <- newIORef False+ refError <- newIORef False+ refFinish <- newIORef True+ let config =+ defaultOllamaConfig+ { hostUrl = "http://localhost:12345" -- guaranteed to fail+ , onModelStart = Just $ writeIORef refStart True+ , onModelError = Just $ writeIORef refError True+ , onModelFinish = Just $ writeIORef refFinish True+ }++ void $+ generate+ defaultGenerateOps {modelName = "gemma3", prompt = "what is 23+41?"}+ (Just config)++ wasStarted <- readIORef refStart+ wasErrored <- readIORef refError+ wasFinished <- readIORef refFinish++ assertBool "onModelStart should be called" wasStarted+ assertBool "onModelError should be called" wasErrored+ assertBool "onModelFinish should be called" wasFinished++testOnModelHooksSucc :: TestTree+testOnModelHooksSucc = testCase "Model lifecycle hooks should be triggered 2" $ do+ refStart <- newIORef False+ refError <- newIORef True+ refFinish <- newIORef False+ let config =+ defaultOllamaConfig+ { onModelStart = Just $ writeIORef refStart True+ , onModelError = Just $ writeIORef refError False+ , onModelFinish = Just $ writeIORef refFinish True+ }++ void $+ generate+ defaultGenerateOps {modelName = "gemma3", prompt = "what is 23+41?"}+ (Just config)++ wasStarted <- readIORef refStart+ wasErrored <- readIORef refError+ wasFinished <- readIORef refFinish++ assertBool "onModelStart should be called" wasStarted+ assertBool "onModelError should be called" wasErrored+ assertBool "onModelFinish should be called" wasFinished++testRetryCount :: TestTree+testRetryCount = testCase "Should retry generate call retryCount times" $ do+ counter <- newIORef (0 :: Int)+ let config =+ defaultOllamaConfig+ { hostUrl = "http://localhost:12345" -- fails+ , retryCount = Just 2+ , retryDelay = Just 1+ , onModelStart = Just $ modifyIORef counter (+ 1)+ , onModelError = Just $ pure ()+ , onModelFinish = Just $ pure ()+ }++ _ <- generate defaultGenerateOps {prompt = "Retry test"} (Just config)+ calls <- readIORef counter+ -- Should be retryCount + 1 (initial + retries)+ assertEqual "Expected 3 attempts (1 initial + 2 retries)" 3 calls++testRetryDelay :: TestTree+testRetryDelay = testCase "Should delay between retries" $ do+ counter <- newIORef (0 :: Int)+ let delaySecs = 2+ start <- getCurrentTime++ let config =+ defaultOllamaConfig+ { hostUrl = "http://localhost:12345" -- fails+ , retryCount = Just 1+ , retryDelay = Just delaySecs+ , onModelStart = Just $ modifyIORef counter (+ 1)+ , onModelError = Just $ pure ()+ , onModelFinish = Just $ pure ()+ }++ _ <- generate defaultGenerateOps {prompt = "Retry delay test"} (Just config)+ end <- getCurrentTime+ let elapsed = realToFrac (diffUTCTime end start) :: Double+ let expectedMin = fromIntegral delaySecs++ assertBool+ ("Elapsed time should be at least " ++ show expectedMin ++ "s, but was " ++ show elapsed)+ (elapsed >= expectedMin)++testCommonManagerUsage :: TestTree+testCommonManagerUsage = testCase "Should reuse provided commonManager" $ do+ refStart <- newIORef (0 :: Int)+ mgr <-+ newTlsManagerWith tlsManagerSettings {managerResponseTimeout = responseTimeoutMicro 1000000}+ let config =+ defaultOllamaConfig+ { hostUrl = "http://localhost:12345" -- will fail fast+ , commonManager = Just mgr+ , timeout = 999 -- shouldn't matter, manager timeout will be used+ , onModelStart = Just $ modifyIORef refStart (+ 1)+ , onModelError = Just $ pure ()+ , onModelFinish = Just $ pure ()+ }++ _ <- generate defaultGenerateOps {prompt = "1"} (Just config)+ _ <- generate defaultGenerateOps {prompt = "2"} (Just config)+ startCount <- readIORef refStart+ assertEqual "Both requests should start (reuse manager)" 2 startCount++{-+ Suffix is not supported for few gemma3 and qwen3.++testSuffixOption :: TestTree+testSuffixOption = testCase "Should respect suffix in generation" $ do+ let ops = defaultGenerateOps+ { modelName = "qwen3:0.6b"+ , prompt = "Complete this sentence: The Eiffel Tower is in"+ , suffix = Just " [End]"+ }+ eRes <- generate ops Nothing+ case eRes of+ Left err -> assertFailure $ "Expected success, got error: " ++ show err+ Right r -> assertBool "Expected suffix in response" $+ T.isSuffixOf "[End]" (genResponse r)+ -}++testThinkOption :: TestTree+testThinkOption = testCase "Should activate thinking mode when think=True" $ do+ let ops =+ defaultGenerateOps+ { modelName = "qwen3:0.6b"+ , prompt = "What is 2+2?"+ , think = Just True+ }+ eRes <- generate ops Nothing+ case eRes of+ Left err -> assertFailure $ "Expected success, got error: " ++ show err+ Right _ -> pure () -- TODO: Need to find a way to know if model is thinking++testFormatJsonFormat :: TestTree+testFormatJsonFormat = testCase "Should return response in JsonFormat" $ do+ let ops =+ defaultGenerateOps+ { modelName = "gemma3"+ , prompt =+ "John was 23 year old in 2023, this is year 2025."+ <> "How old is John assuming he celebrated this year's birthday; "+ <> "Return an object with keys 'name' and 'age'."+ , format = Just JsonFormat+ }+ eRes <- generate ops Nothing+ case eRes of+ Left err -> assertFailure $ "Expected success, got error: " ++ show err+ Right r -> do+ let responseText = genResponse r+ let decoded = Aeson.decode (BSL.pack $ T.unpack responseText) :: Maybe Aeson.Value+ assertBool "Expected valid JSON object in response" (decoded /= Nothing)++testFormatSchemaFormat :: TestTree+testFormatSchemaFormat = testCase "Should include SchemaFormat in the request" $ do+ let schema =+ buildSchema $+ emptyObject+ |+ ("fruit", JString)+ |+ ("quantity", JNumber)+ |! "fruit"+ |! "quantity"++ ops =+ defaultGenerateOps+ { modelName = "gemma3"+ , prompt = "I had 3 apples, 1 gave one away. How many left?"+ , format = Just (SchemaFormat schema)+ }++ eRes <- generate ops Nothing+ case eRes of+ Left err -> assertFailure $ "Expected success, got error: " ++ show err+ Right r -> do+ let response = T.toLower (genResponse r)+ assertBool "Expected fruit information in response" $+ "apple" `T.isInfixOf` response || "fruit" `T.isInfixOf` response++testImageInput :: TestTree+testImageInput = testCase "Should accept and process base64 image input" $ do+ maybeImg <- encodeImage "./examples/sample.png"+ case maybeImg of+ Nothing -> assertFailure "Image encoding failed (unsupported format or missing file)"+ Just imgData -> do+ let ops =+ defaultGenerateOps+ { modelName = "gemma3"+ , prompt = "Describe this image."+ , images = Just [imgData]+ }+ cfg = Just defaultOllamaConfig {timeout = 300}++ eRes <- generate ops cfg+ case eRes of+ Left err -> assertFailure $ "Expected success, got error: " ++ show err+ Right r -> do+ let response = T.toLower (genResponse r)+ assertBool "Expected image-related description in response" $+ T.isInfixOf "i love haskell" response++testStreamingHandler :: TestTree+testStreamingHandler = testCase "Should handle streaming response" $ do+ -- IORef to collect streamed chunks+ chunksRef <- newIORef []+ -- Define the stream handler: accumulate responses+ let streamHandler chunk = modifyIORef chunksRef (++ [genResponse chunk])+ flushHandler = pure ()+ ops =+ defaultGenerateOps+ { modelName = "gemma3"+ , prompt = "Write few words about Haskell."+ , stream = Just (streamHandler, flushHandler)+ }+ eRes <- generate ops Nothing+ -- Collect streamed chunks from IORef+ chunks <- readIORef chunksRef+ let fullOutput = T.concat chunks+ case eRes of+ Left err -> assertFailure $ "Expected streaming success, got error: " ++ show err+ Right _ -> do+ assertBool "Expected streamed text to include 'haskell'" $+ "haskell" `T.isInfixOf` T.toLower fullOutput+ assertBool "Expected some streamed content" $ not (T.null fullOutput)++testModelOptionsBasic :: TestTree+testModelOptionsBasic = testCase "ModelOptions: temperature and topP" $ do+ let opts =+ Just $+ defaultModelOptions+ { temperature = Just 0.9+ , topP = Just 0.8+ , topK = Nothing+ , numPredict = Just 20+ }++ eRes <-+ generate+ defaultGenerateOps+ { modelName = "gemma3"+ , prompt = "Generate a random list of 3 animals"+ , options = opts+ }+ Nothing++ case eRes of+ Left err -> assertFailure $ "Expected success, got: " ++ show err+ Right r -> assertBool "Response should not be empty" (not . T.null $ genResponse r)++tests :: TestTree+tests =+ sequentialTestGroup+ "Generate tests"+ AllFinish+ [ generateTests+ , testOnModelHooksFail+ , testOnModelHooksSucc+ , testRetryCount+ , testRetryDelay+ , testCommonManagerUsage+ , testThinkOption+ , testFormatJsonFormat+ , testFormatSchemaFormat+ , testImageInput+ , testStreamingHandler+ , testModelOptionsBasic+ ]
+ test/Test/Ollama/Show.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}++module Test.Ollama.Show (tests) where++import Test.Tasty+import Test.Tasty.HUnit+import Data.Ollama.Show+import Data.Ollama.Ps+import Data.Text qualified as T+import Data.Maybe (isJust)++testShowModelBasic :: TestTree+testShowModelBasic = testCase "Show model info: basic call" $ do+ res <- showModel "gemma3"+ case res of+ Left err -> assertFailure $ "Expected success, got error: " ++ show err+ Right ShowModelResponse {modelFile, modelInfo = ShowModelInfo {generalArchitecture}} -> do+ assertBool "modelFile should not be empty" (not $ T.null modelFile)+ assertBool "Architecture should be present" (isJust generalArchitecture)++testShowModelVerbose :: TestTree+testShowModelVerbose = testCase "Show model info: verbose enabled" $ do+ res <- showModelOps "qwen3:0.6b" (Just True) Nothing+ case res of+ Left _ -> pure () --assertFailure $ "Expected success, got error: " ++ show err+ Right ShowModelResponse {template, parameters} -> do+ -- Verbose should yield more details like parameters/template+ assertBool "Should have a template if verbose" (isJust template)+ assertBool "Should have parameters if verbose" (isJust parameters)++testPsBasic :: TestTree+testPsBasic = testCase "List running models: basic success" $ do+ res <- ps Nothing+ case res of+ Left err -> assertFailure $ "Expected success, got error: " ++ show err+ Right (RunningModels _) -> do+ assertBool "Should return a list (possibly empty)" True++testPsModelFields :: TestTree+testPsModelFields = testCase "Running model fields are populated" $ do+ res <- ps Nothing+ case res of+ Left _ -> return () -- Allow failure if no models are running+ Right (RunningModels (m:_)) -> do+ assertBool "name_ should not be empty" (not $ T.null $ name_ m)+ assertBool "modelName should not be empty" (not $ T.null $ modelName m)+ assertBool "modelDigest should not be empty" (not $ T.null $ modelDigest m)+ assertBool "size_ should be positive" (size_ m > 0)+ assertBool "sizeVRam should be non-negative" (sizeVRam m >= 0)+ Right _ -> return () -- If empty, that's acceptable++tests :: TestTree+tests = testGroup "show model tests" [+ testShowModelBasic + , testShowModelVerbose+ , testPsBasic+ , testPsModelFields+ ]