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 |
langchain-hs-0.0.6.0@sha256:81eb7858b12647c0b1520ff88c0b47a33cfdb5a8b48f2bf2059353d685121923,5442Module 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.
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
- Hackage Package: https://hackage.haskell.org/package/langchain-hs
- Documentation Website: https://tusharad.github.io/langchain-hs/
- Whitepaper: whitepaper.md (Effect polymorphism, categorical design, state graphs)
- Examples Directory:
examples/
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-exportinglangchain-hs-coreandlangchain-hs-graph.
StateGraph Productionization (langchain-hs-graph)
- Replaced prototype modules with robust execution combinators:
runGraph,runGraphWithConfig,runGraphInterleaved, andstreamGraphSteps. - Added
runnableNodeandchatModelNodecombinators to lift arbitraryRunnableandChatModelinstances 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, andresetEntitiesToInitial. - Umbrella Prelude Re-exports: Re-exported all memory implementations (
ConversationBufferMemory,SessionMemoryStore,EntityMemory,CombinedMemory), buffer reset helpers, andsplitDocumentsinLangchain.Prelude.
Output Parsers & Document Loaders
- Trimmed fragile character-scanning parsers (
NumberSeparatedList) and redundant wrapper types (JSONOutputStructure) in favor of type-safeStructuredOutputandTextOutputParser. - Hardened boolean output parser to exact normalized token recognition.
- Added standard numeric
OutputParserinstances forInt,Integer, andDoublewith whitespace stripping. - Eliminated lazy I/O file handle leaks in
FileLoaderandDirectoryLoaderwith strict text I/O. - Added
splitDocumentspreserving document metadata across chunk splits, and provided defaultloadAndSplitimplementation inBaseLoader.
Breaking Changes from 0.0.5.0
- Provider modules are no longer bundled in
langchain-hs:- For Ollama: Add
langchain-hs-ollamato dependencies andimport Langchain.Provider.Ollama. - For OpenAI: Add
langchain-hs-openaito dependencies andimport Langchain.Provider.OpenAI. - For Gemini: Add
langchain-hs-geminito dependencies andimport Langchain.Provider.Gemini. - For MCP: Add
langchain-hs-mcpto dependencies andimport Langchain.MCP.Client.
- For Ollama: Add
Langchain.Preludeno longer re-exports provider-specific constructors (newOllama,newOpenAI,newGemini,newStdioMcpClient). Import the corresponding provider package.JSONOutputStructureandNumberSeparatedListhave been removed fromLangchain.OutputParser.CoreandLangchain.Prelude. UseStructuredOutputorTextOutputParser.
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 withRunnableTree,ChatModel,ContentBlock,Tool,StreamEvent, andLangchainT.langchain-hs-graph(0.0.5.0): Graph-based state machine engine withStateGraph,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
Runnabletypeclass. - Compose pure GADT abstract syntax trees using
|>>(sequential composition),&>&(parallel fan-out), and>>>#(fallback failover) without side effects before interpretation.
- The core selling point: Every component implements the
- 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 persistentSQLiteCheckpointer. - First-class Human-in-the-Loop (
HITL) node interruption, inspect/edit state, and resumption viaresumeGraph. - Time-travel state replay and Graphviz DOT visualization export.
- Cyclic state machines with pure state reducers (
- Model Context Protocol (MCP):
- Native stdio and HTTP JSON-RPC 2.0 client implementation.
- Dynamic tool inspection and bidirectional schema mapping to Haskell
Tooldefinitions.
- 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, andMonadTransinstances. - OpenTelemetry distributed tracing spans (
withSpan) and structured JSON telemetry. - Three-state Circuit Breaker, exponential backoff retries with randomized jitter, and in-memory caching.
- Removed redundant global config structs in favor of parameterization over custom user environment
- Dependency & Performance Upgrades:
- Upgraded to
ollama-haskell0.4.1.0with JSON schema grammar constraints. - Migrated to
MercuryTechnologies/openaiclient. - Full PVP upper bounds across all packages for Hackage compliance.
- Upgraded to
Breaking Changes from 0.0.3.0
LangchainTis now parameterized overenv(LangchainT env m ainstead of the former implicitLangchainConfig).- Replace
runLangchainT config actionwithrunLangchainT env actionwhereenvis your custom environment type (use()if you have no shared config). - The
MonadReader env (LangchainT env m)instance gives youask/asksto read your environment from within the monad.
- Replace
ChatMessagerenamed toMessagethroughout — update all pattern matches and constructor calls.- Agent modules restructured —
Langchain.Agentis now split intoLangchain.Agent.ReActandLangchain.Agent.PlanAndExecutewith updated type signatures for tool-call support. - Removed
HtmlLoader,JsonLoader, andWebPageLoaderfromLangchain.DocumentLoader— Along with thetagsoupdependency. 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
OpenAILLM integration. - Added
DirectoryLoaderfor loading Documents from a directory. - Added
HuggingFaceLLM integration. - Added docusaurus documentation.
- Added
OpenAIembeddings integration. - Added GHC CI matrix build.
- Added
TokenBufferMemoryMemory integration. - Added
RetrievalQAchain. - Added
CalculatorTooltool.
Fixed
- Fixed
loadAndSplitfunction forPdfLoader. - Minor documentation fixes.
- Fixed
WebScraperto only scrape textual content. - Made langchain-hs buildable till stack-lts-19.33
- Fixed
Reactagent.
Changed
- Generalized LLMParams to accept different type per LLM.