ollama-haskell

Hackage MIT License

Industry-grade, feature-complete, modern Haskell client library for the Ollama local LLM engine.

Features

  • Client-Centric Architecture: Thread-safe OllamaClient handle with connection pooling and resource management (newClient, defaultClient, clientFromEnv, withClient).
  • First-Class Streaming: conduit-based response streaming (chatStream, generateStream, pullStream, pushStream, createModelStream).
  • Complete API Surface: Text generation, chat completions, vector embeddings, model management (list, show, copy, delete, pull, push, create), and system endpoints.
  • Structured Outputs: Powerful SchemaBuilder DSL (|+, |++, |!, |!!) for type-safe JSON Schema structured responses.
  • Function / Tool Calling: Full support for tool definitions (Tool), tool calls (ToolCall), and execution results (toolResultMessage).
  • Thinking Models Support: Native support for reasoning models (qwen3.5, deepseek-r1) with Think / ThinkingLevel types.
  • Environment & Auth Integration: Robust URL normalization for OLLAMA_HOST and bearer token support for OLLAMA_API_KEY.
  • Configurable Resilience: Flexible retry policies (NoRetry, ConstantRetry, ExponentialRetry), lifecycle callbacks, and structured logging.
  • Conversation Store: Transactional STM-backed InMemoryStore and ConversationStore typeclass for managing multi-turn chat sessions.
  • SDK Comparison Matrix: Detailed feature comparison against Python, JS/TS, and Go SDKs in doc/COMPARISON.md.

Installation

Add ollama-haskell to your .cabal file:

build-depends:
    base >= 4.17 && < 5
  , ollama-haskell >= 0.3.0.0

Or using Stack in package.yaml:

dependencies:
  - ollama-haskell >= 0.3.0.0

Quick Start (5 Lines)

import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.Text.IO qualified as TIO
import Ollama

main :: IO ()
main = do
  client <- defaultClient
  res <- chat client $ chatRequest "qwen3.5:2b" (userMessage "Why is the sky blue?" :| [])
  case res of
    Left err   -> print err
    Right resp -> mapM_ (TIO.putStrLn . messageContent) (crMessage resp)

Streaming Responses with Conduit

Stream LLM responses token-by-token as they generate:

import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.Text.IO qualified as TIO
import Ollama

main :: IO ()
main = do
  client <- defaultClient
  let req = chatRequest "qwen3.5:2b" (userMessage "Count from 1 to 5." :| [])
  
  -- Stream chunks directly into stdout or collect them
  chunks <- collectStream (chatStream client req)
  mapM_ (TIO.putStr . maybe "" messageContent . crMessage) chunks
  putStrLn ""

Function & Tool Calling

Define function signatures and let the LLM execute structured tool calls:

import Data.List.NonEmpty (NonEmpty ((:|)))
import Ollama

calculatorTool :: Tool
calculatorTool = Tool "function" $ FunctionDef
  { fnName = "add"
  , fnDescription = Just "Add two numbers"
  , fnParameters = Just (FunctionParameters "object" Nothing (Just ["a", "b"]) Nothing Nothing Nothing)
  , fnStrict = Just True
  }

main :: IO ()
main = do
  client <- defaultClient
  let req = (chatRequest "qwen3.5:2b" (userMessage "What is 40 + 2?" :| []))
        { chatTools = Just [calculatorTool] }
  res <- chat client req
  case res of
    Left err   -> print err
    Right resp -> print (crMessage resp)

Structured Outputs (JSON Schema DSL)

Enforce structured JSON output formats using SchemaBuilder:

import Data.Text.IO qualified as TIO
import Ollama
import Ollama.Types.Format.SchemaBuilder

personSchema :: Schema
personSchema = buildSchema $ emptyObject
  |+ ("name", JString)
  |+ ("age", JInteger)
  |! "name"

main :: IO ()
main = do
  client <- defaultClient
  let req = (generateRequest "qwen3.5:2b" "Generate a person profile.")
        { genFormat = Just (SchemaFormat personSchema) }
  res <- generate client req
  case res of
    Left err   -> print err
    Right resp -> TIO.putStrLn (grResponse resp)

Environment Variables & Configuration

Construct a client using environment variables (OLLAMA_HOST, OLLAMA_API_KEY):

main :: IO ()
main = do
  client <- clientFromEnv
  -- Automatically connects to OLLAMA_HOST with optional Authorization: Bearer header
  ...

Or configure custom retry policies and loggers:

customConfig :: OllamaClientConfig
customConfig = defaultConfig
  { configBaseUrl = "http://my-ollama-server:11434"
  , configTimeout = 120
  , configRetry   = ExponentialRetry 3 1 -- 3 retries with exponential backoff
  , configLogger  = Just (\level msg -> putStrLn $ "[" <> show level <> "] " <> show msg)
  }

main :: IO ()
main = withClient customConfig $ \client -> do
  ...

Documentation & SDK Comparison


License

MIT © 2024–2026 Tushar Adhatrao

Changes

Changelog

All notable changes to ollama-haskell will be documented in this file. The format is based on Keep a Changelog, and this project adheres to PVP (Haskell Package Versioning Policy).


[0.3.0.0] - 2026-08-04

Added

  • OllamaClient Core: Thread-safe client handle with automatic connection manager lifecycle management (newClient, defaultClient, clientFromEnv, withClient).
  • First-Class Streaming Pipeline: conduit-based response streaming (chatStream, generateStream, pullStream, pushStream, createModelStream).
  • Stream Combinators: collectStream and foldStream in Ollama.Streaming.
  • Structured Output DSL: Type-safe SchemaBuilder DSL in Ollama.Types.Format.SchemaBuilder (|+, |++, |!, |!!) for constructing JSON Schemas.
  • Thinking / Reasoning Models Support: Think ADT (ThinkEnabled, ThinkDisabled, ThinkLevel) and ThinkingLevel (ThinkLow, ThinkMedium, ThinkHigh, ThinkMax) supporting models such as qwen3.5 and deepseek-r1.
  • Function / Tool Calling: Tool, FunctionDef, FunctionParameters, ToolCall, and toolResultMessage helper.
  • Environment Resolution: Automatic OLLAMA_HOST parsing and normalization in clientFromEnv supporting host:port, http://host:port, and bare host.
  • Authorization & Headers: Support for OLLAMA_API_KEY bearer tokens and custom configHeaders.
  • Configurable Resilience: RetryPolicy ADT (NoRetry, ConstantRetry, ExponentialRetry), lifecycle callbacks (configOnStart, configOnSuccess, configOnError), and structured logger configLogger.
  • Token Throughput Metrics: Metrics helpers chatEvalTokensPerSecond, chatPromptEvalTokensPerSecond, evalTokensPerSecond, promptEvalTokensPerSecond, tokensPerSecond.
  • Testing Infrastructure: Built-in mock testing module Ollama.Testing (newMockClient, withMockClient, mockGenerateResponse, mockChatResponse, mockEmbedResponse, mockListModelsResponse).
  • Conversation Store: Transactional STM-backed InMemoryStore and ConversationStore typeclass.
  • New API Endpoints: Ollama.API.Embed (/api/embed), Ollama.API.Blobs (/api/blobs), Ollama.API.Ps (/api/ps), Ollama.API.Version (/api/version).
  • Benchmark Suite: Criterion/tasty-bench suite in bench/Main.hs measuring serialization and throughput.

Changed

  • MonadIO / MonadUnliftIO Polymorphism: All API functions use MonadIO m => / MonadUnliftIO m => signatures instead of dual *M variants.
  • Typed Newtypes: ModelName, Digest, Base64Image, Duration, Version replace primitive string types.
  • Unified Error Type: OllamaError sum type with structured constructors and Exception instance.

Deprecated

  • embeddings endpoint (/api/embeddings) marked deprecated in favor of /api/embed.