langchain-hs

Pure functional LLM agent framework and multi-agent graph engine in Haskell

https://github.com/tusharad/langchain-hs#readme

Stackage Nightly 2026-10-26:0.0.6.0
Latest on Hackage:0.0.6.0

See all snapshots langchain-hs appears in

MIT licensed by Tushar Adhatrao
Maintained by [email protected]
This version can be pinned in stack with:langchain-hs-0.0.6.0@sha256:81eb7858b12647c0b1520ff88c0b47a33cfdb5a8b48f2bf2059353d685121923,5442

Module documentation for 0.0.6.0

There are no documented modules for this package.

LangChain Haskell (langchain-hs)

Type-Safe AI Agents & Multi-Agent Graphs in Haskell

A functional, effect-polymorphic Haskell ecosystem for building production AI applications, featuring pure AST pipelines (RunnableTree), cyclic state graphs (StateGraph), Model Context Protocol (MCP), and OpenTelemetry observability.

Hackage GHC License: MIT Documentation


Installation

Install langchain-hs and provider packages via Cabal:

cabal update
cabal install langchain-hs langchain-hs-ollama langchain-hs-openai

Or add to your stack.yaml:

extra-deps:
  - langchain-hs-0.0.6.0
  - langchain-hs-core-0.0.6.0
  - langchain-hs-graph-0.0.6.0
  - langchain-hs-ollama-0.0.6.0
  - langchain-hs-openai-0.0.6.0

Package reference on Hackage: https://hackage.haskell.org/package/langchain-hs


1. Simple Chat Calling

Send a prompt to an LLM (local Ollama, OpenAI, or Gemini) and retrieve a typed response:

{-# LANGUAGE OverloadedStrings #-}
module Main where

import Control.Monad.Except (runExceptT)
import qualified Data.Text.IO as T
import Langchain.Prelude
import Langchain.Provider.Ollama

main :: IO ()
main = do
  -- Connect to local Ollama instance (DeepSeek, Llama 3, Gemma, etc.)
  model <- newOllama "llama3.2" defaultConfig
  
  let messages = [userMessage "Explain in two sentences why Haskell makes AI agents more reliable."]
  result <- runExceptT $ invoke model messages Nothing
  
  case result of
    Left err   -> T.putStrLn $ errorMessage err
    Right resp -> T.putStrLn $ extractMessageText resp

Output

Haskell's static type system prevents common agent failures like missing dictionary keys or unexpected nulls before code ever reaches production. Furthermore, pure state transitions guarantee that autonomous agents cannot silently mutate variables behind your back.

2. JSON Structured Output

Guarantee that the LLM response conforms to a typed Haskell record using FromJSON and ToSchema:

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Main where

import Data.Aeson (FromJSON)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import GHC.Generics (Generic)
import Langchain.Prelude
import Langchain.Provider.Ollama

-- Define your target data schema
data Developer = Developer
  { name              :: T.Text
  , preferredLanguage :: T.Text
  , yearsOfExperience :: Int
  , favoriteLibrary   :: T.Text
  } deriving (Show, Generic, FromJSON, ToSchema)

main :: IO ()
main = do
  model <- newOllama "llama3.2" defaultConfig
  
  let prompt =
        [ userMessage "Extract profile: Alan has been writing Haskell for 8 years and loves langchain-hs." ]
      options = withStructuredOutput (Proxy @Developer) defaultOllamaConfig

  result <- runLangchainT () $ invoke model prompt (Just options)
  case result of
    Left err  -> T.putStrLn $ errorMessage err
    Right msg -> T.putStrLn $ extractMessageText msg

Output

{
  "name": "Alan",
  "preferredLanguage": "Haskell",
  "yearsOfExperience": 8,
  "favoriteLibrary": "langchain-hs"
}

3. Tool Calling

Equip models with effectful Haskell tools. The LLM automatically emits structured tool calls, which your application executes and feeds back for final synthesis:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Main where

import Control.Monad.IO.Class (liftIO)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Langchain.Prelude
import Langchain.Provider.Ollama

main :: IO ()
main = do
  model <- newOllama "qwen2.5:7b" defaultConfig
  
  -- Instantiate typed filesystem tool
  let fsTool   = findFilesTool @IO defaultSecurityPolicy
      toolCfg  = withTools [fsTool] defaultOllamaConfig
      prompt   = [userMessage "Search for Haskell source files using find_files with pattern '*.hs'."]

  res <- runLangchainT () $ do
    response <- invoke model prompt (Just toolCfg)
    case messageToolCalls response of
      Just (tCall:_) -> do
        liftIO $ T.putStrLn $ "Tool Invoked: " <> toolCallName tCall
        liftIO $ T.putStrLn $ "Arguments:    " <> T.pack (show (toolCallArguments tCall))
        
        -- Execute tool with LLM arguments
        execResult <- liftIO $ toolExecute fsTool (toolCallArguments tCall)
        let observation = case execResult of
              Left err  -> "Error: " <> errorMessage err
              Right out -> out
        liftIO $ T.putStrLn $ "Observation:  " <> observation
        
        -- Supply tool observation back to model for final response
        let toolMsg = (toolMessage observation) { messageName = Just (toolCallName tCall) }
            thread  = prompt ++ [response, toolMsg]
            
        finalMsg <- invoke model thread (Just toolCfg)
        liftIO $ T.putStrLn "\nFinal Assistant Answer:"
        liftIO $ T.putStrLn $ extractMessageText finalMsg
      _ -> do
        liftIO $ T.putStrLn $ extractMessageText response

  case res of
    Left err -> T.putStrLn $ "Error: " <> errorMessage err
    Right () -> pure ()

Output

Tool Invoked: find_files
Arguments:    fromList [("pattern",String "*.hs")]
Observation:  ["src/Main.hs","src/Agent.hs","src/Tools.hs"]

Final Assistant Answer:
I scanned the repository and identified 3 Haskell source files matching your pattern:
- src/Main.hs
- src/Agent.hs
- src/Tools.hs

4. ReAct Agent

Run an autonomous Reason + Act agent loop that decomposes user tasks, selects tools, evaluates observations, and iterates until completion:

{-# LANGUAGE OverloadedStrings #-}
module Main where

import Control.Monad.Except (runExceptT)
import qualified Data.Text.IO as T
import Langchain.Prelude
import Langchain.Provider.Ollama

main :: IO ()
main = do
  -- Setup sample file for demonstration
  writeFile "/tmp/server_status.txt" "Service: api-gateway, Status: Degraded, Latency: 420ms"
  
  model <- newOllama "qwen2.5:7b" defaultConfig
  
  -- Equip agent with file inspection tools
  let policy = defaultSecurityPolicy
      tools  = [readFileTool Nothing policy, findFilesTool policy]
      agent  = defaultReActAgent model tools
      prompt = [userMessage "Inspect /tmp/server_status.txt using read_file and summarize the service health."]

  res <- runExceptT $ runReActAgent agent prompt
  case res of
    Left err  -> T.putStrLn $ errorMessage err
    Right msg -> T.putStrLn $ extractMessageText msg

Output

[ReAct Iteration 1]
Thought: I should read the contents of /tmp/server_status.txt to check the current health status.
Action: read_file {"path": "/tmp/server_status.txt"}
Observation: Service: api-gateway, Status: Degraded, Latency: 420ms

[ReAct Iteration 2]
Thought: The file has been read. The api-gateway service is in a degraded state with elevated latency. I will now summarize this for the user.

Final Answer:
The api-gateway service is currently experiencing degraded health with high latency (420ms). Immediate investigation of the gateway service is recommended.

Documentation & Resources


Development & Testing

# Build project
stack build

# Run test suite
stack test

# Code quality checks
make lint
make format-check

License

Distributed under the MIT License. See LICENSE for details.

Changes

Changelog for langchain-hs

0.0.6.0 - 2026-10-15

Highlights

  • Complete Modular Provider Architecture: All concrete AI providers and protocol clients are now decoupled into standalone Hackage packages:
    • langchain-hs-core (0.0.6.0): Core abstractions, runnables, tools, agents, vector stores, memory, output parsers, and resilience.
    • langchain-hs-graph (0.0.6.0): Production StateGraph engine with state reducers, checkpointing, and time-travel replay.
    • langchain-hs-ollama (0.0.6.0): Ollama chat model and embeddings provider.
    • langchain-hs-openai (0.0.6.0): OpenAI, OpenRouter, and OpenAI-compatible provider with structured output and streaming.
    • langchain-hs-gemini (0.0.6.0): Google Gemini provider with function calling and SSE streaming.
    • langchain-hs-mcp (0.0.6.0): Model Context Protocol (MCP) client over stdio and HTTP JSON-RPC 2.0.
    • langchain-hs (0.0.6.0): Umbrella package re-exporting langchain-hs-core and langchain-hs-graph.

StateGraph Productionization (langchain-hs-graph)

  • Replaced prototype modules with robust execution combinators: runGraph, runGraphWithConfig, runGraphInterleaved, and streamGraphSteps.
  • Added runnableNode and chatModelNode combinators to lift arbitrary Runnable and ChatModel instances into graph nodes.
  • Full support for recursion limits, step-by-step transition logging, checkpointing, and human-in-the-loop interruption.

Memory Hardening (langchain-hs-core)

  • InVariableMemory: Thread-safe STM-backed transactional memory with snapshotting, resets, and clear semantics.
  • SessionMemory: Multi-session conversation manager supporting per-session state isolation, active session tracking, and TTL-based pruning.
  • FileMemory: Persistent file-backed conversation memory with atomic disk writes and metadata preservation across process restarts.
  • Reset Helpers: Added explicit seed-state reset combinators resetConversationBuffer, resetWindowBuffer, resetTokenBuffer, and resetEntitiesToInitial.
  • Umbrella Prelude Re-exports: Re-exported all memory implementations (ConversationBufferMemory, SessionMemoryStore, EntityMemory, CombinedMemory), buffer reset helpers, and splitDocuments in Langchain.Prelude.

Output Parsers & Document Loaders

  • Trimmed fragile character-scanning parsers (NumberSeparatedList) and redundant wrapper types (JSONOutputStructure) in favor of type-safe StructuredOutput and TextOutputParser.
  • Hardened boolean output parser to exact normalized token recognition.
  • Added standard numeric OutputParser instances for Int, Integer, and Double with whitespace stripping.
  • Eliminated lazy I/O file handle leaks in FileLoader and DirectoryLoader with strict text I/O.
  • Added splitDocuments preserving document metadata across chunk splits, and provided default loadAndSplit implementation in BaseLoader.

Breaking Changes from 0.0.5.0

  • Provider modules are no longer bundled in langchain-hs:
    • For Ollama: Add langchain-hs-ollama to dependencies and import Langchain.Provider.Ollama.
    • For OpenAI: Add langchain-hs-openai to dependencies and import Langchain.Provider.OpenAI.
    • For Gemini: Add langchain-hs-gemini to dependencies and import Langchain.Provider.Gemini.
    • For MCP: Add langchain-hs-mcp to dependencies and import Langchain.MCP.Client.
  • Langchain.Prelude no longer re-exports provider-specific constructors (newOllama, newOpenAI, newGemini, newStdioMcpClient). Import the corresponding provider package.
  • JSONOutputStructure and NumberSeparatedList have been removed from Langchain.OutputParser.Core and Langchain.Prelude. Use StructuredOutput or TextOutputParser.

0.0.5.0 - 2026-09-10

Major Architecture & Ecosystem Evolution

  • 3-Tier Monorepo Architecture:
    • langchain-hs-core (0.0.5.0): Zero-dependency pure core with RunnableTree, ChatModel, ContentBlock, Tool, StreamEvent, and LangchainT.
    • langchain-hs-graph (0.0.5.0): Graph-based state machine engine with StateGraph, StateReducer, checkpointers, HITL, and multi-agent coordination.
    • langchain-hs (0.0.5.0): Production integrations for Ollama, OpenAI, Gemini, Vector Stores, MCP, and Observability.
  • Pure AST Pipelines (RunnableTree):
    • The core selling point: Every component implements the Runnable typeclass.
    • Compose pure GADT abstract syntax trees using |>> (sequential composition), &>& (parallel fan-out), and >>># (fallback failover) without side effects before interpretation.
  • LangGraph in Haskell (StateGraph):
    • Cyclic state machines with pure state reducers (StateReducer s) satisfying monoid associativity laws.
    • Thread-safe STM in-memory checkpointer (MemoryCheckpointer) and persistent SQLiteCheckpointer.
    • First-class Human-in-the-Loop (HITL) node interruption, inspect/edit state, and resumption via resumeGraph.
    • Time-travel state replay and Graphviz DOT visualization export.
  • Model Context Protocol (MCP):
    • Native stdio and HTTP JSON-RPC 2.0 client implementation.
    • Dynamic tool inspection and bidirectional schema mapping to Haskell Tool definitions.
  • Decoupled Monad Transformer (LangchainT env m a):
    • Removed redundant global config structs in favor of parameterization over custom user environment env.
    • Complete MonadReader, MonadError, MonadIO, and MonadTrans instances.
    • OpenTelemetry distributed tracing spans (withSpan) and structured JSON telemetry.
    • Three-state Circuit Breaker, exponential backoff retries with randomized jitter, and in-memory caching.
  • Dependency & Performance Upgrades:
    • Upgraded to ollama-haskell 0.4.1.0 with JSON schema grammar constraints.
    • Migrated to MercuryTechnologies/openai client.
    • Full PVP upper bounds across all packages for Hackage compliance.

Breaking Changes from 0.0.3.0

  • LangchainT is now parameterized over env (LangchainT env m a instead of the former implicit LangchainConfig).
    • Replace runLangchainT config action with runLangchainT env action where env is your custom environment type (use () if you have no shared config).
    • The MonadReader env (LangchainT env m) instance gives you ask/asks to read your environment from within the monad.
  • ChatMessage renamed to Message throughout — update all pattern matches and constructor calls.
  • Agent modules restructured — Langchain.Agent is now split into Langchain.Agent.ReAct and Langchain.Agent.PlanAndExecute with updated type signatures for tool-call support.
  • Removed HtmlLoader, JsonLoader, and WebPageLoader from Langchain.DocumentLoader — Along with the tagsoup dependency. Users can write custom document loaders tailored to their schemas and formats.

0.0.3.0 - 2025-11-16

Added

  • Added Agent middleware support.
  • Added image input support for OpenAI and Gemini.

Fixed

  • Fixed Ollama stream’s onComplete callback.
  • Fixed system_fingerprint field to be nullable.

Changed

    • Revamped Agent module with native tool_call support and improved architecture.
    • Migrated to Mercury’s OpenAI client.
    • Changed Ollama params to use ChatOps type.
    • Renamed ChatMessage to ChatHistory.
    • Parameterized StreamHandler Token type.
    • Added Langchain error type for better error handling.
  • Updated ollama-haskell to 0.2.1.0.

0.0.2.0 - 2025-05-04

Added

  • Added OpenAI LLM integration.
  • Added DirectoryLoader for loading Documents from a directory.
  • Added HuggingFace LLM integration.
  • Added docusaurus documentation.
  • Added OpenAI embeddings integration.
  • Added GHC CI matrix build.
  • Added TokenBufferMemory Memory integration.
  • Added RetrievalQA chain.
  • Added CalculatorTool tool.

Fixed

  • Fixed loadAndSplit function for PdfLoader.
  • Minor documentation fixes.
  • Fixed WebScraper to only scrape textual content.
  • Made langchain-hs buildable till stack-lts-19.33
  • Fixed React agent.

Changed

  • Generalized LLMParams to accept different type per LLM.