These days there is a lot of discussion about deep-learning chatbots such as ChatGPT. There are packages like LangChain which make consuming these models easier. This tutorial will guide you through a project combining LangChain and DelphiFMX‘s innovative capabilities to craft a unique and personalized custom chat app. So, let’s get started!
Table of Contents
What is Langchain?
LangChain is a framework for creating language model-driven applications. Language models are artificial intelligence programs that can produce text in natural language in response to inputs like questions, prompts, or documents. With LangChain, you can link a language model to additional data sources like files, databases, or web pages and communicate with them using natural language. Using different components and APIs offered by LangChain, you may also alter how the language model and application behave. With LangChain, you may create various apps, such as chatbots, code generators, document summarization tools, and more.
How to build a custom chat app with LangChain and DelphiFMX?
In this article, we intend to go over the basics of LangChain in Python and use Openai’s LLM model to create a chat app that considers the user’s age and gives answers according to this information. So the GUI will involve two screens. Some user data is taken on the first screen, while the second screen is the chat screen with widgets like a memo, button, and textbox.
What are the prerequisites of the custom chat app project?
To create the custom chat application, you should install the Delphi IDE, the Delphi4PythonExporter tool, and the DelphiFMX library on your device. Additionally, you will need a text editor or IDE that supports Python, such as PyScripter. If you don’t have these tools installed, we recommend checking out the article “Powerful Python GUI Project Setup” to help you get started.
Next, install Langchain and OpenAI by using the following command:
1 2 |
pip install langchain openai |
Lastly, you can get the complete code for this project from our GitHub repository:
https://github.com/Embarcadero/PythonBlogExamples/tree/main/Custom_Chat_App
How to use a prompt template in LangChain?
You might be asking yourself, what are Prompt Templates? Why should we use them in Chatbots? In simple words, Prompt Templates are pre-defined recipes that help generate prompts for language models. These templates may include certain instructions, some examples to help the chatbot, or even specific contexts and/or questions to help the chatbot with a certain task.
Take, for example, ChatGPT. If you ask ChatGPT, “What is the definition of a Machine Learning model?” It might give you an in-depth and detailed answer. A prompt will help give ChatGPT instructions on how the user might want the question answered. For example, A prompt may append text to a query to modify the model’s output like the query above may change into “What is the definition of a Machine Learning model? Explain it to me like a 5-year-old.” This will change the answer, and the model may reduce complex words to make it easy for the user to understand.
LangChain, as a framework, provides many tools to create and work with prompt templates. Let’s start by creating a simple prompt template that helps our OpenAI chatbot. Start by creating a new file called LLMChat.py
in your project directory and start importing some modules:
1 2 3 4 5 |
from langchain.llms import OpenAI from langchain.prompts import PromptTemplate from langchain.chains import LLMChain from langchain.memory import ConversationBufferMemory |
Next, create an LLM
class with an __init__
and a getReply
method:
1 2 3 4 5 6 7 |
class LLM(): def __init__(self, name, age) -> None: pass def getReply(self, qs): pass |
Start by initializing your name
and age
variables in the __init__
function and create an LLM model, a template for queries, and a ConversationBufferMemory to help store your previous conversations. You need to create your OpenAI api key and replace your key in the openai_api_key
variable’s value:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class LLM(): def __init__(self, name, age) -> None: self.age = age self.name = name llm = OpenAI(openai_api_key="**PUT YOUR API KEY HERE**", temperature=0) # Create instance of openai llm temp = "You are a nice chatbot having a conversation with a human of age {}.".format(age) template = temp + """ So respond appropriately and preferably in around 2 sentences. Previous conversation: {chat_history} New human question: {question} Response:""" prompt = PromptTemplate.from_template(template) # Create template for prompt to openai memory = ConversationBufferMemory(memory_key="chat_history") # Create buffer memory for multiturn conversation |
Finally, we chain all the components using an LLMChain
, completing our __init__
function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
def __init__(self, name, age) -> None: self.age = age self.name = name llm = OpenAI(openai_api_key="**PUT YOUR API KEY HERE**", temperature=0) # Create instance of openai llm temp = "You are a nice chatbot having a conversation with a human of age {}.".format(age) template = temp + """ So respond appropriately and preferably in around 2 sentences. Previous conversation: {chat_history} New human question: {question} Response:""" prompt = PromptTemplate.from_template(template) # Create template for prompt to openai memory = ConversationBufferMemory(memory_key="chat_history") # Create buffer memory for multiturn conversation self.conversation = LLMChain( # Chain all components llm=llm, prompt=prompt, memory=memory ) |
Next, navigate to the getReply
method and simply use the model to generate a response to a question and then return the response. Here is what our complete LLMChat.py
looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
from langchain.llms import OpenAI from langchain.prompts import PromptTemplate from langchain.chains import LLMChain from langchain.memory import ConversationBufferMemory class LLM(): def __init__(self, name, age) -> None: self.age = age self.name = name llm = OpenAI(openai_api_key="your_openai_api_key", temperature=0) # Create instance of openai llm temp = "You are a nice chatbot having a conversation with a human of age {}.".format(age) template = temp + """ So respond appropriately and preferably in around 2 sentences. Previous conversation: {chat_history} New human question: {question} Response:""" prompt = PromptTemplate.from_template(template) # Create template for prompt to openai memory = ConversationBufferMemory(memory_key="chat_history") # Create buffer memory for multiturn conversation self.conversation = LLMChain( # Chain all components llm=llm, prompt=prompt, memory=memory ) def getReply(self, qs): response = self.conversation({"question": qs}) # Provide conversation to LLM to get appropriate answer return response["text"] |
How to design the user interface for our custom chat app in Delphi?
Now that our LangChain model is complete, we can start working on the user interface for our Delphi app.
Open up Delphi CE and create a new project using File > New > Multi-Device Application > Blank Application > Ok
. Next, set up a name for the project. Here we are naming our project ChatApplication
.
If you are unfamiliar with the various sections of the Delphi IDE, we recommend consulting our complimentary eBook bundle, which covers the Delphi Python Ecosystem and provides insights into all Python GUI offerings.
Next, let’s rename our form by right-clicking on it and selecting QuickEdit. Here we will set our form name as BioScreen
with the caption Chat Application
.
Next, we’ll rename the project’s source file to help us keep track of it. Head over to the Projects tab on the top-right corner of your screen and locate Unit1.pas
file. This is the source file of your form. We will rename it to Bio.pas
.
Next, we’ll use the head to the Object Inspector and resize our form using the ClientWidth
and ClientHeight
properties under the Properties tab.
Now, we’ll add a few labels to our form. Head over to the Palette and search for the TLabel
component. Drop a few of these into your form.
Rename these components by right-clicking on them and opening up QuickEdit. Next, head over to the Object Inspector and customize the text style for each label using the TextSettings
property based on your needs. You could additionally set the HorzAlign
and VertAlign
properties to Center
to align your text. Here is what our form looks like:
Now, we’ll add a text box next to the Name label and a number box next to the Age label. Open up the Palette and search for the TEdit
and TNumberBox
components. As the name implies, the TEdit
component will be used to input text to our form, whereas the TNumberBox
will be used to input the age. Here is what our form looks like:
Next, let’s add a button to our form, which will be used to open up the chat screen. For this, we will use the TButton
component, so navigate to the Palette and search for the component.
Next, modify the button using its Text Settings property. Finally, rename the button and set its caption. Here we’ve named our button StartButton
with the caption Start Chatting
.
Here is what our form looks like:
Finally, let’s finish our form by adding an image. Head over to the Palette and search for the TImage
component. Add this component to your form.
Now select the TImage component and open the MultiResBitMap
property in the Properties tab of the Object Inspector. Next, click the open button and select the image you want to use.
Here is what our form looks like:
Now that our BioScreen
form is complete, we can move on and create our chat screen. Head over to the projects tab, and after right-clicking on ChatApplication.exe
, select Add New > Multi-Device Form
.
This will create a new form which we have named as ChatScreen
, and the corresponding source file as Chat.pas
. We have also renamed the form caption to Chat
.
Next, let’s add a THeader
and a TFooter
component to our form. We also add a few labels, buttons, and an edit box to our form. Here is what our form looks like:
Finally, we need to add a text box to display our chats. For this, we’ll add a TMemo
component that will be large enough to display all our messages and have a scroll to move up and down in the chat. Here is what our final form looks like:
Now that we’ve completed our forms, it’s time to add a few final touches. To do this, download the free ebook bundle, which comes shipped with a multitude of styles that you can choose from. Once downloaded, extract the FMX styles zip. Next, select the style you want to use. Here we will be using AquaGraphite.style
available in the bundle.
Now, head to the Palette and add a TStyleBook
component to your form. The placement does not matter for this component, as it will be invisible.
Now, double-click on the StyleBook1
component to open up your Style Designer. Click on the open button to open up a dialog box.
Now navigate to the Dark.style
file in your styles directory, select your file and click exit and save.
Select your form and head over to the Object Inspector. Here search for the StyleBook property. Now, select the drop-down menu and select StyleBook1. This will add style to your form.
Here is what our final forms look like:
How to combine everything into our finished custom chat app?
Now that our forms are complete, we need to add some functionality to the button so we don’t lose it when we export it in Python. To do this, double-click on each button on the forms. This will add an OnClick
method to that button, creating a procedure in the .pas
file of the form. This allows you to add unique functionality to each button when clicked.
Because the Delphi4PythonExporter does not use run-time implementation, we must add at least one comment (//
) to each procedure to preserve the function when we export the form.
With this, we are ready to export our project. Select Tools > Export To Python > Export Current Entire Project
to export the project.
Here we will give the project the title Chat Application
. Next, select the directory of your choosing and click on Export:
Once the export is complete, head over to the project directory. Delphi will generate three Python files and two .pyfmx
files. The ChatApplication.py
file will be used to run the application, whereas the Bio.py
and Chat.py
files contain the class for the BioScreen
and ChatScreen
forms. The .pyfmx
files contain the visual information for the forms. Here are the contents for each file.
ChatApplication.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
from delphifmx import * from Bio import BioScreen def main(): Application.Initialize() Application.Title = 'Chat Application' Application.MainForm = BioScreen(Application) Application.MainForm.Show() Application.Run() Application.MainForm.Destroy() if __name__ == '__main__': main() |
Bio.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import os from delphifmx import * class BioScreen(Form): def __init__(self, owner): self.TitleLabel = None self.AgeLabel = None self.NameLabel = None self.AgeNumberBox = None self.NameEdit = None self.Image1 = None self.StartButton = None self.StyleBook1 = None self.LoadProps(os.path.join(os.path.dirname(os.path.abspath(__file__)), "Bio.pyfmx")) def StartButtonClick(self, Sender): pass |
Chat.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import os from delphifmx import * class ChatScreen(Form): def __init__(self, owner): self.SendButton = None self.UserQsEdit = None self.Header = None self.HeaderLabel = None self.Footer = None self.BackButton = None self.ChatMemo = None self.StyleBook1 = None self.LoadProps(os.path.join(os.path.dirname(os.path.abspath(__file__)), "Chat.pyfmx")) def SendButtonClick(self, Sender): pass def BackButtonClick(self, Sender): pass |
Now, let’s begin adding functionality to our code. Start by copying and pasting the LLMChat.py
file that we created earlier to the project folder. Next, open up Bio.py
and import the ChatScreen form from Chat.py
:
1 2 |
from chat import ChatScreen |
Next, head over to the StartButtonClick
function. Here we are simply going to create the ChatScreen
, pass in our name and age variables, and then open up the screen. Here is what the StartButtonClick
function looks like:
1 2 3 4 |
def StartButtonClick(self, Sender): chat = ChatScreen(self, name=self.NameEdit.Text, age=self.AgeNumberBox.Text) chat.Show() |
Here is what our final Bio.py
file looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import os from delphifmx import * # Import chat form from Chat import ChatScreen class BioScreen(Form): def __init__(self, owner): self.TitleLabel = None self.AgeLabel = None self.NameLabel = None self.AgeNumberBox = None self.NameEdit = None self.Image1 = None self.StartButton = None self.StyleBook1 = None self.ErrorLabel = None self.LoadProps(os.path.join(os.path.dirname( os.path.abspath(__file__)), "Bio.pyfmx")) def StartButtonClick(self, Sender): chat = ChatScreen(self, name=self.NameEdit.Text, age=self.AgeNumberBox.Text) chat.Show() |
Next, open up Chat.py
and start by importing the LLM
class from the LLMChat.py
file.
1 2 |
from LLMChat import LLM |
Now head over to the __init__
function and start by adding some additional name and age parameters to the function. Next, we’ll re-initialize the HeaderLabel
with the name of our user and store the name and age in new variables. Finally, we’ll create an instance of our LLM
class in the constructor. Here is what our __init__
function looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
def __init__(self, owner, name, age): self.SendButton = None self.UserQsEdit = None self.Header = None self.HeaderLabel = None self.Footer = None self.BackButton = None self.ChatMemo = None self.StyleBook1 = None self.LoadProps(os.path.join(os.path.dirname( os.path.abspath(__file__)), "Chat.pyfmx")) self.HeaderLabel.Text = "{}'s Chat".format(name) self.age = age self.name = name self.llm = LLM(name, age) # Create LLM chain instance |
Next, in our SendButtonClick
function, we’ll simply add a formatted user query to the ChatMemo
. Once we get a reply from the LLM
model, we will append that to the ChatMemo
too. Here is what our final function looks like:
1 2 3 4 5 6 7 8 9 |
def SendButtonClick(self, Sender): # Add user's question to the memo for chat history self.ChatMemo.Lines.Add("{}: {}".format( self.name, self.UserQsEdit.Text)) # Get the reponse to the question reply = self.llm.getReply(self.UserQsEdit.Text) # Add the response to the memo as well self.ChatMemo.Lines.Add("Chatbot:{}".format(reply)) |
Finally, our BackButtonClick
will only be used to close the chat screen, which we can simply do using self.Destroy()
. Here is what our final Chat.py
file looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
import os from delphifmx import * # More required imports from LLMChat import LLM class ChatScreen(Form): def __init__(self, owner, name, age): self.SendButton = None self.UserQsEdit = None self.Header = None self.HeaderLabel = None self.Footer = None self.BackButton = None self.ChatMemo = None self.StyleBook1 = None self.LoadProps(os.path.join(os.path.dirname( os.path.abspath(__file__)), "Chat.pyfmx")) self.HeaderLabel.Text = "{}'s Chat".format(name) self.age = age self.name = name self.llm = LLM(name, age) # Create LLM chain instance def SendButtonClick(self, Sender): # Add user's question to the memo for chat history self.ChatMemo.Lines.Add("{}: {}".format( self.name, self.UserQsEdit.Text)) # Get the reponse to the question reply = self.llm.getReply(self.UserQsEdit.Text) # Add the response to the memo as well self.ChatMemo.Lines.Add("Chatbot:{}".format(reply)) def BackButtonClick(self, Sender): self.Destroy() |
Now that our application is ready let’s test it out. Head over to ChatApplication.py
and run the file.
Next, add your name and age and click on Start Chatting.
This should open up your chat screen.
Now simply start messaging on the chat app.
What have you learned in this custom chat app tutorial?
In today’s world of advanced communication, we’re talking about smart chatbots like ChatGPT and tools like LangChain that make them work better together. This tutorial has shown us how LangChain, DelphiFMX, and Python can be paired to make a custom chat app with an amazing GUI.
LangChain is like the main character, letting computers talk like humans using trained language models. With this magic, we’re making a chat app that talks in a smart way, depending on how old you are. We saw how Delphi allows us to add more personalization by adding the user’s name in several places of the GUI. This tutorial shows how exciting things can happen when smart tech and good GUIs come together, inviting us to change how we talk and connect using computers.
What are some FAQs related to this custom chat app tutorial?
What is LangChain, and how does it relate to chat applications?
LangChain is a framework that uses language models to create text-based applications. In the context of chat apps, LangChain enables the creation of chatbots and other communication tools that understand and generate natural language responses.
Do I need to be an expert in machine learning to use LangChain?
LangChain is designed to simplify the process of using language models for developers. While some familiarity with machine learning concepts could be helpful, the framework aims to make it accessible to a wider range of developers.
What is DelphiFMX, and why is it used in this tutorial?
DelphiFMX is a graphical user interface framework that allows developers to create cross-platform applications with rich UI experiences. It’s used in this tutorial to design the user interface for the chat app.
Do I need prior experience with Delphi to follow this tutorial?
While prior experience with Delphi could be beneficial, the tutorial should provide step-by-step guidance to help you create the chat app, even if you’re new to Delphi programming.