The goal of this cookbook is to show how one can build a Chainlit application on top of Mistral AI's APIs!
We will highlight the reasoning capabilities of Mistral's LLMs by letting a self-reflective agent assess whether it has gathered enough information to answer nested user questions, such as "What is the weather in Napoleon's hometown?"
To answer such questions, our application should go through multiple-step reasoning: first get Napoleon's hometown, then fetch the weather for that location.
You can read through this notebook or simply go with chainlit run app.py
since the whole code is in app.py
.
You will find here a split of the whole application code with explanations:
Here's a visual of what we will have built in a few minutes:
Setup
Requirements
We will install mistralai
, chainlit
and python-dotenv
.
Be sure to create a .env
file with the line MISTRAL_API_KEY=
followed by your Mistral AI API key.
!pip install mistralai chainlit python-dotenv
Optional - Tracing
You can get a LITERAL_API_KEY
from Literal AI to setup tracing and visualize the flow of your application.
Within the code, Chainlit offers the @chainlit.step
decorators to trace your functions, along with an automatic instrumentation of Mistral's API via chainlit.instrument_mistralai()
.
The trace for this notebook example is: https://cloud.getliteral.ai/thread/ea173d7d-a53f-4eaf-a451-82090b07e6ff.
Define available tools
In the next cell, we define the tools, and their JSON definitions, which we will provide to the agent. We have two tools:
get_current_weather
-> takes in a locationget_home_town
-> takes in a person's name
Optionally, you can decorate your tool definitions with @cl.step()
, specifying a type and name to organize the traces you can visualize from Literal AI.
import json
import chainlit as cl
@cl.step(type="tool", name="get_current_weather")
async def get_current_weather(location):
# Make an actual API call! To open-meteo.com for instance.
return json.dumps({
"location": location,
"temperature": "29",
"unit": "celsius",
"forecast": ["sunny"],
})
@cl.step(type="tool", name="get_home_town")
async def get_home_town(person: str) -> str:
"""Get the hometown of a person"""
return "Ajaccio, Corsica"
"""
JSON tool definitions provided to the LLM.
"""
tools = [
{
"type": "function",
"function": {
"name": "get_home_town",
"description": "Get the home town of a specific person",
"parameters": {
"type": "object",
"properties": {
"person": {
"type": "string",
"description": "The name of a person (first and last names) to identify."
}
},
"required": ["person"]
},
},
},
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
},
"required": ["location"],
},
},
}
]
# This helper function runs multiple tool calls in parallel, asynchronously.
async def run_multiple(tool_calls):
"""
Execute multiple tool calls asynchronously.
"""
available_tools = {
"get_current_weather": get_current_weather,
"get_home_town": get_home_town
}
async def run_single(tool_call):
function_name = tool_call.function.name
function_to_call = available_tools[function_name]
function_args = json.loads(tool_call.function.arguments)
function_response = await function_to_call(**function_args)
return {
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": function_response,
}
# Run tool calls in parallel.
tool_results = await asyncio.gather(
*(run_single(tool_call) for tool_call in tool_calls)
)
return tool_results
Agent logic
For the agent logic, we simply repeat the following pattern (max. 5 times):
- ask the user question to Mistral, making both tools available
- execute tools if Mistral asks for it, otherwise return message
You will notice that we added an optional @cl.step
of type run
and with optional tags to trace the call accordingly in Literal AI.
Visual trace: https://cloud.getliteral.ai/thread/ea173d7d-a53f-4eaf-a451-82090b07e6ff
import os
import chainlit as cl
from mistralai.client import MistralClient
mai_client = MistralClient(api_key=os.environ["MISTRAL_API_KEY"])
@cl.step(type="run", tags=["to_score"])
async def run_agent(user_query: str):
messages = [
{
"role": "system",
"content": "If needed, leverage the tools at your disposal to answer the user question, otherwise provide the answer."
},
{
"role": "user",
"content": user_query
}
]
number_iterations = 0
answer_message_content = None
while number_iterations < 5:
completion = mai_client.chat(
model="mistral-large-latest",
messages=messages,
tool_choice="auto", # use `any` to force a tool call
tools=tools,
)
message = completion.choices[0].message
messages.append(message)
answer_message_content = message.content
if not message.tool_calls:
# The LLM deemed no tool calls necessary,
# we break out of the loop and display the returned message
break
tool_results = await run_multiple(message.tool_calls)
messages.extend(tool_results)
number_iterations += 1
return answer_message_content
On message callback
The callback below, properly annotated with @cl.on_message
, ensures our run_agent
function is called upon every new user message.
import chainlit as cl
@cl.on_message
async def main(message: cl.Message):
"""
Main message handler for incoming user messages.
"""
answer_message = await run_agent(message.content)
await cl.Message(content=answer_message).send()
Starter questions
You can define starter questions for your users to easily try out your application, which will look like this:
We have got many more Chainlit features in store (authentication, feedback, Slack/Discord integrations, etc.) to let you build custom LLM applications and really take advantage of Mistral's LLM capabilities.
Please visit the Chainlit documentation to learn more!
async def set_starters():
return [
cl.Starter(
label="What's the weather in Napoleon's hometown",
message="What's the weather in Napoleon's hometown?",
icon="/images/idea.svg",
),
cl.Starter(
label="What's the weather in Paris, TX?",
message="What's the weather in Paris, TX?",
icon="/images/learn.svg",
),
cl.Starter(
label="What's the weather in Michel-Angelo's hometown?",
message="What's the weather in Michel-Angelo's hometown?",
icon="/images/write.svg",
),
]