langchain-hs-0.0.2.0: src/Langchain/Runnable/ConversationChain.hs
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
{- |
Module : Langchain.Runnable.ConversationChain
Description : Stateful conversation handler for LLM interactions
Copyright : (c) 2025 Tushar Adhatrao
License : MIT
Maintainer : Tushar Adhatrao <tusharadhatrao@gmail.com>
Note: This module is not functional at this moment.
This module provides the 'ConversationChain' implementation, which manages stateful
conversations with language models. It combines:
1. A memory component for storing conversation history
2. An LLM for generating responses
3. A prompt template for formatting the conversation
'ConversationChain' handles the full conversation lifecycle, including:
- Adding user messages to memory
- Retrieving conversation history
- Formatting the conversation context for the LLM
- Getting responses from the LLM
- Storing AI responses back to memory
This creates a complete conversation loop that maintains context across multiple turns.
-}
module Langchain.Runnable.ConversationChain
( -- * Types
ConversationChain (..)
) where
import Data.Text (Text)
import Langchain.LLM.Core
import Langchain.Memory.Core
import Langchain.PromptTemplate
import Langchain.Runnable.Core
{- | Manages a stateful conversation between a user and a language model.
The 'ConversationChain' combines three key components:
1. @memory@: Stores and retrieves conversation history
2. @llm@: The language model that generates responses
3. @prompt@: Template for formatting the conversation for the LLM
When invoked with a user message, the 'ConversationChain':
- Adds the user message to memory
- Retrieves the updated conversation history
- Formats the conversation for the LLM using the prompt template
- Gets a response from the LLM
- Stores the AI response in memory
- Returns the AI response
Example:
@
import Data.Text (Text)
import qualified Data.Text as T
import Langchain.LLM.OpenAI (OpenAI(..))
import Langchain.Memory.ConversationBufferMemory (ConversationBufferMemory(..))
import Langchain.PromptTemplate (PromptTemplate(..), createPromptTemplate)
import Langchain.Runnable.ConversationChain (ConversationChain(..))
main :: IO ()
main = do
-- Create memory component
let memory = ConversationBufferMemory
{ messages = []
, returnMessages = True
}
-- Create LLM
let llm = OpenAI
{ model = "gpt-4"
, temperature = 0.7
}
-- Create prompt template
promptTemplate <- createPromptTemplate
"You are a helpful assistant. {history}\\nHuman: {input}\\nAI:"
["history", "input"]
-- Create conversation chain
let conversation = ConversationChain
{ memory = memory
, llm = llm
, prompt = promptTemplate
}
-- Start conversation
response1 <- invoke conversation "Hello, who are you?"
case response1 of
Left err -> putStrLn $ "Error: " ++ T.unpack err
Right answer -> do
putStrLn $ "AI: " ++ T.unpack answer
-- Continue conversation with context
response2 <- invoke conversation "What can you help me with?"
case response2 of
Left err -> putStrLn $ "Error: " ++ T.unpack err
Right answer2 -> putStrLn $ "AI: " ++ T.unpack answer2
@
You can customize the behavior by using different memory implementations:
* 'ConversationBufferMemory' - Stores the full conversation history
* 'ConversationBufferWindowMemory' - Keeps only the most recent N exchanges
* 'ConversationSummaryMemory' - Summarizes older conversations to save tokens
* 'ConversationEntityMemory' - Tracks entities mentioned in the conversation
The prompt template can be customized to give the LLM specific instructions,
persona characteristics, or to format the conversation history in different ways.
-}
data ConversationChain m l = ConversationChain
{ memory :: m
-- ^ Memory component that stores conversation history
, llm :: l
-- ^ Language model that generates responses
, prompt :: PromptTemplate
-- ^ Template for formatting the conversation
}
-- | Make ConversationChain an instance of Runnable to enable composition with other components
instance (BaseMemory m, LLM l) => Runnable (ConversationChain m l) where
type RunnableInput (ConversationChain m l) = Text
type RunnableOutput (ConversationChain m l) = Text
-- \| Process a user message and generate an AI response.
--
-- This method:
-- 1. Adds the user message to memory
-- 2. Retrieves the full conversation history
-- 3. Formats the history and input for the LLM
-- 4. Gets a response from the LLM
-- 5. Stores the AI response in memory
-- 6. Returns the AI response
--
-- Example:
--
-- @
-- let chatbot = ConversationChain { ... }
--
-- -- Single turn conversation
-- response <- invoke chatbot "Can you explain monads in Haskell?"
--
-- -- Multi-turn conversation with context
-- response1 <- invoke chatbot "Who was Alan Turing?"
-- response2 <- invoke chatbot "What was his most famous contribution?"
-- response3 <- invoke chatbot "Can you explain it in simpler terms?"
-- @
--
invoke ConversationChain {..} input = do
-- Add user message to memory
updatedMemResult <- addUserMessage memory input
case updatedMemResult of
Left err -> return $ Left err
Right updatedMem -> do
-- Get all messages
messagesResult <- messages updatedMem
case messagesResult of
Left err -> return $ Left err
Right allMessages -> do
-- Format messages for the LLM
let formattedMessages = allMessages
-- Get response from LLM
llmResponse <- chat llm formattedMessages Nothing
case llmResponse of
Left err -> return $ Left err
Right response -> do
-- Store AI response in memory
_ <- addAiMessage updatedMem response
return $ Right response