2. AI Chatbot serving with Django & LangCorn | Unveiling YouTube Insights w/ LLM
In part 2, we will develop a website that integrates sentiment analysis techniques and a Large Language Model to provide a comprehensive understanding of YouTube comments, enabling users to extract meaningful information effortlessly.
Feb 24, 2025 · Geraldus Wilsen
In retrospect to Part 1, we've successfully developed a fully functional website capable of scraping, processing, and storing YouTube data into our database. As we culminate that endeavor, I left you a small challenge—to integrate Django REST Framework. Before delving into our exploration of Django REST Framework, it's crucial to understand the significance of APIs and why Django REST Framework holds such importance. APIs serve as conduits of seamless communication between software systems, enabling data and functionality exchange, a critical component that elevates our application's capabilities. Now, introducing Django REST Framework, a robust toolkit amplifying Django's prowess, simplifying Web API creation with features like serialization, authentication, and streamlined CRUD operations. This dynamic duo, API integration, and DRF, equips us to traverse the digital landscape with flexibility, scalability, and innovation, setting the stage for our exciting exploration in Part 2.
Setting up Django REST Framework
Begin by installing the Django REST Framework with a simple command:
Then, go to settings.py and add 'rest_framework' in INSTALLED_APPS
Create a new file named serializers.py and define a class named ResultSerializer inside it.
Then, connect the serializers by importing them into views.py, and add some functionality from Django REST Framework
If you still remember, previously we store data directly to database, like this:
Right now, we can simply delete it, and change it like this
This code snippet is used to serialize data and interact with the Result
model in Django using the ResultSerializer
class. Let's break it down step by step:
-
serializer = ResultSerializer(data=source)
: This line initializes an instance of theResultSerializer
class and provides it with data from thesource
dictionary. TheResultSerializer
is responsible for converting complex data types, like thesource
dictionary, into a format that can be stored in theResult
model. -
if serializer.is_valid():
: This conditional statement checks if the data provided to the serializer is valid according to the validation rules defined in theResultSerializer
class. -
try:
: This marks the beginning of a try-except block, where the code attempts to perform certain operations, and if an error occurs, it is caught and handled in theexcept
block. -
result = Result.objects.get(user=request.user, videoid=source['videoid'])
: This line attempts to retrieve aResult
object from the database that matches both the logged-in user and thevideoid
from thesource
dictionary. If a match is found, it means there's an existing record with the samevideoid
. -
serializer.update(result, serializer.validated_data)
: If an existing record is found, this line updates the fields of the existingresult
object with the new validated data from the serializer. This is a way of updating the existing record with new information. -
except Result.DoesNotExist:
: If no matching record is found in the database, this block is executed, indicating that a new record needs to be created. -
result = serializer.save()
: This line saves the data from the serializer to theResult
model, creating a new record. -
return redirect(reverse('chat') + f'?id={result.id}')
: After either updating an existing record or creating a new one, the code redirects the user to a view named'chat'
with a query parameterid
that corresponds to the ID of the savedResult
object. -
else:
: If the serializer data is not valid, this block is executed. -
print(serializer.errors)
: This line prints out any validation errors that occurred during the serialization process. This can help in identifying and debugging issues with the data being processed.
In summary, this code segment serializes data using ResultSerializer
, updates existing records or creates new ones based on the validity of the data, and handles redirection and error reporting. It's a key part of the logic for managing and interacting with Result
objects in the application.
Our final getoutput
function in views.py will be like this:
Creating API Endpoints in Django Rest Framework
Before we jump to the next step, if you look at the top of the code, we utilize a decorator (@) provided by Django REST Framework (DRF) to define the behavior and permissions associated with a specific API view. Let's break down each line:
-
@api_view(['POST']): This decorator, @api_view, is used to specify which HTTP methods are allowed for this API view. In this case, it's set to ['POST'], meaning this view will only respond to HTTP POST requests. This decorator ensures that the view is properly configured for handling POST data. It's not only limited to POST method, we can use DELETE, POST, etc.
-
@permission_classes([IsAuthenticated]): This decorator, @permission_classes, is used to define the permissions required to access the view. In this case, [IsAuthenticated] is specified, which means that only authenticated users are allowed to access this view. The IsAuthenticated permission class is a built-in DRF permission class that restricts access to authenticated users.
Our setup of Django REST Framework is nearly complete. Up to this point, we've primarily utilized serializers to populate our model with data.
However, if we intend to retrieve data from our model through an API endpoint, we need to take an additional step.
To begin, we must establish a function that provides access to our API, followed by seamlessly incorporating this new pathway into our urls.py
configuration.
This function defines an API endpoint that retrieves a list of Result objects associated with a specific user. The user parameter is received from the URL. The code attempts to retrieve Result objects filtered by the provided user. If no results are found, a 404 Not Found response is returned. If the HTTP method is GET, the ResultSerializer is used to serialize the results and return them as a response.
This function defines an API endpoint to retrieve, update, or delete a specific Result object identified by both the user and the id parameter from the URL. Similar to the previous function, it tries to retrieve a Result object based on the user and id. If the object is not found, a 404 Not Found response is returned. For GET requests, the serialized data of the result is returned. For DELETE requests, the result is deleted and a 204 No Content response is returned.
Overall, these views provide API endpoints to retrieve lists of Result objects for a specific user and to perform CRUD operations (GET, PUT, DELETE) on individual Result objects based on the user and id. The use of DRF decorators and serializers simplifies the process of building these API endpoints while adhering to best practices for API design. Next step is registering a new path in urls.py
We have successfully integrated Django REST Framework. Now, upon entering http://127.0.0.1:8000/result/1
into your browser,
the result_list_by_user
view corresponding to the result/<int:user>
path pattern will be executed. Ultimately, you will be presented with a Django Rest Framework template showcasing your data, as follows:
Develop an AI Chatbot
Now we're all set to construct a cutting-edge chatbot that will enable our users to engage in dynamic interactions and seek information pertinent to their YouTube videos.
If you recall the progression laid out in Part 1, we will replicate those steps for this endeavor as well. Our initial stride involves crafting a foundational code that will serve as the essence of our chatbot, leveraging the capabilities of Langchain and Large Language Model. Following this, we'll proceed to elevate our Django views.py
by incorporating a new view aptly named 'chat'. Let's get started!
First, let's revisit our maincodes.py
, where we previously composed our scraping code. As a swift reminder, if you have completed Part 1, you will encounter crucial components such as the imported libraries and our prominent large language model, depicted as follows:
Secondly, create a new function named answer_question with a set of parameters, including question, videoid, videotitle, view, like, comment, total_positive_comment, positive_comment, total_negative_comment, negative_comment, total_neutral_comment, and neutral_comment.
Next, let's proceed by crafting a template—a command that will guide our Large Language Model. Here's the format you can follow:
With the template in place, the subsequent step involves integrating it into a PromptTemplate
from Langchain and storing it within a variable named prompt
. The PromptTemplate
constructor accepts two crucial parameters: template
and input_variables
. We'll provide our formulated template for the template
parameter, while the input_variables
parameter encompasses the variables employed within our template.
Subsequently, it becomes imperative to establish a chain model using LLMChain
, requiring two pivotal parameters. Initially, the prompt
- the template we've devised - assumes the forefront. Meanwhile, the second parameter necessitates specifying the Large Language Model of our choice. Given that we've stored our LLM under the variable falcon_llm
, we can readily reference it within this context.
Lastly, to get the result, we can use llm_chain.run command with adding some parameter as below:
Our final answer_question
function would look like this:
One last thing is to update our views.py. Since we already implement Django REST Framework and created API endpoints, let's utilize it in our chat function.
A quick recap: What does chat
do?
To gain a better understanding, let's break down its functionality:
-
@login_required(login_url='login')
: This decorator ensures that only authenticated users can access thechat
view. If a user is not authenticated, they will be redirected to the specified login page ('login'
). -
context
: A dictionary containing the current user's ID is created, which will be passed to the template for rendering. -
global last_id
: Declares a global variable namedlast_id
, presumably intended to keep track of a specific ID for user interactions. -
Various variables are assigned using the request's POST and GET data, such as
button_id
andid
, both of which are print-debugged for monitoring. -
The code checks whether
button_id
is provided and updateslast_id
accordingly, forming an API URL to fetch data. -
If no
button_id
is provided, the code checks for the existence ofid
, and if present, constructs an API URL withid
. If neitherbutton_id
norid
is available, the code useslast_id
to generate the API URL. -
The application sends a GET request to the API URL and retrieves data, storing it in the
source
dictionary. -
Key data elements (e.g.,
videoid
,videotitle
, etc.) are extracted from thesource
dictionary. -
The view handles both POST and GET requests. In case of a user input, the function calls the
answer_question
function to generate a response. If no user input is provided, a default response is generated. -
The view renders the
home.html
template, passing the generated response, source data, and context for rendering.
In summary, this view function handles user interactions with the web application's chat feature. It manages user input, communicates with an API to retrieve data, generates responses using the answer_question
function, and ultimately renders the response along with other relevant data in the home.html
template.
In reference to our previous discussion on APIs, during the journey of developing this project, I came across a highly beneficial approach: implementing the Large Language Model as a separate API. The decision to compartmentalize the model has proven to have significant advantages, particularly when dealing with large-sized models. By segregating the model into an API, we have effectively optimized scalability, security, and the ease of model updates. This strategic separation has proven to be an immensely advantageous choice in our project's development journey.
The way we transform a Large Language Model into an API endpoint is by using Langcorn and FASTAPI.
I found a great tutorial on using Langcorn and FASTAPI in this video by Assembly AI. Langcorn is also well-documented; please take a look at this Langcorn Github Repository. The process is quite straightforward, so please watch the tutorial and follow the steps one by one. While I won't include the entire sequence in this article, I will provide you with some essential code snippets that were not covered in the video. These snippets explain how to call the API endpoint and integrate it into our chat view. Before proceeding with the modification of our chat function in views.py
, ensure that when you enter your API endpoint in your browser, you receive a result similar to the following:
If you haven't encountered this view, there's no need to worry. I have also provided a code for you. Please kindly check my Github Repository. One last step of this project, let's update our chat function in views.py
become like this:
Once again, don't forget to change your_langchain_model_api_endpoint_here
in
with your api endpoint. Right after we change the chat function, we can delete or comment the answer_question
function in maincodes.py since we don't need it anymore.
Conclusion
As we conclude our project journey, we recognize that our implementation merely scratches the surface of the expansive capabilities offered by Large Language Models (LLMs). I encourage you to delve deeper into the myriad features that LLMs, especially those harnessed through Langchain, have to offer. While certain aspects remained unexplored within this project, you can broaden your horizons by exploring a playlist titled LangChain & LLM tutorials (ft. gpt3, chatgpt, llamaindex, chroma) explained by Samuel Chan. I hope that this project has laid a strong foundation for your endeavors, and I eagerly anticipate witnessing the innovative AI creations that you will undoubtedly develop.
Until our next article, stay inspired and keep innovating!
Further reading
Unveiling YouTube insights with LLM: Part 1
Read More
Tags: llm, django, YouTube API, sentiment-analysis