Effortlessly Create an AI Dungeon Master Bot Using Julep and Chainlit
Tags: ai, python, opensource
Published: 2024-05-02 03:26:18

Thanks to open-source tools, creating AI applications has become more accessible than ever. Similar to how cloud services such as AWS and Heroku have enabled nearly anyone to launch startups, platforms provided by companies like OpenAI and Meta are democratizing AI development. This shift allows almost anyone to build their own AI bots easily.

I've long been fascinated by the potential of generative AI and its applications. As an avid tabletop RPG (TTRPG) enthusiast, I'm particularly drawn to the narrative and story-driven aspects that shows like "Critical Role" portray. Inspired by this, I decided to create a simple, accessible tool to help introduce newcomers to the world of TTRPGs through engaging storytelling.

To bring this idea to life, I developed a choose-your-own-adventure-style text game. I utilized Julep's open-source tools along with the capabilities of GPT-4 Turbo to server as the backbone of the game. 

In this project, I'll demonstrate how straightforward it is to generate story narratives and implement the user interface to showcase a practical application of generative AI in gaming. 

Full disclosure: I previously worked at Julep AI and contributed to developing the tools I utilized to create this bot. 


## 1. Install Julep and Chainlit
First, we will initiate a Python project and install Chainlit and Julep. You can find the API docs for Julep at [https://github.com/julep-ai/julep](https://github.com/julep-ai/julep)

```bash
pip install chainlit
pip install julep
```

## 2. Get Julep API Key

We're using Julep primarily to leverage its features, namely agents, sessions and memory. First, let's get an API key to initiate the client. You can get an API key from [https://platform.julep.ai](https://platform.julep.ai).

## 3. Setup Julep Client

Next, initialize the client as follows. For the purposes of using Chainlit, we'll use async-await to prevent blocking the event loop or having it timing out.

```python
from julep import AsyncClient

api_key = "<YOUR_API_KEY>"
base_url = "https://dev.julep.ai/api"

client = AsyncClient(api_key=api_key, base_url=base_url)
```


## 4. Create Agent and write prompts

Next, we'll generate the agent using `gpt-4-turbo`. This will serve as the bot for our game. Using the Julep API, we're giving it context on what the bot will be and what it should do. First, we're giving it a name and an about. Most importantly, we're giving it a set of instructions on how to run the games. 

To code a choose-your-own-adventure game would naturally require thousands of lines of dialog and hundreds of if-statements to reach specific outcomes. The agent simplifies all of this and will generate dialog, backstories, storylines and outcomes for us.

```python
agent = await client.agents.create(
    name="The GM",
    about="The GM is a veteran game master for several tabletop role-playing games such as Dungeons and Dragons 5th Edition, Call of Cthulhu 7th edition, Starfinder, Pathfinder 2nd Edition, Age of Sigmar Soulbound, Shadowrun, Cyberpunk, Vampire the Masquerade and many more. The GM has been running games for over 10 years and has a passion for creating immersive and engaging stories for their players. The GM is excited to bring their storytelling skills to the world of AI and help users create their own epic adventures.",
    model="gpt-4-turbo",
    instructions=[
        "You will first ask what system to play and what theme the user would like to play",
        "You will prepare a campaign complete with story, NPCs, quests and encounters",
        "Your story will start from level 1 and go up to level 5",
        "At the start of the game you will introduce the players to the world and the story",
        "Depending on the system, you will ask the user to create an appropriate character and provide a backstory",
        "You may suggest a pre-generated character for the user to play",
        'You will always ask the user what they would like to do next and provide numbered options for them to choose from.',
        'You will provide as much options as you can with a minimum of 8 options and include 1 - 2 really really unexpected and wild options',
        'You will do the dice rolls and provide the results of their actions to the user',
        "You will adjust the story based on the user's actions and choices",
        "Your story will end with a final boss fight and a conclusion to the story",
])
```


## 4. Create User and Session

Now, we're setting up the user and session. The user represents the individual interacting with the agent, while the session acts as a virtual space or channel facilitating the conversation between an agent and a user. 

Additionally, we'll leverage the session's memory capability to retain the context from all prior interactions with a user. This will be very useful so we don't have to write our own database to store the context.


```python
user = await client.users.create(name="Philip", about="TTRPG player")

    session = await client.sessions.create(agent_id=agent.id, user_id=user.id, situation="You are starting a new campaign. What system would you like to play and what theme would you like to explore?")
```

This is all it really takes to build the bot using Julep. 

![Done!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kztfs5lptqiyyzqy74n4.gif)


## 5. Setup Chainlit

Next, we'll build the chat interface. Chainlit makes this very easy. 


```python
import chainlit as cl


@cl.on_chat_start
async def start():
    session_id = await setup_session()

    cl.user_session.set("session_id", session_id)
    response = await client.sessions.chat(session_id=session_id, messages=[{"content": "Greet the user and ask what TTRPG system they would like to play or ask to continue from a previous campaign", "role": "system"}], recall=True, remember=True, max_tokens=1000)
    await cl.Message(
        content=response.response[0][0].content,
    ).send()

@cl.on_message
async def main(message: cl.Message):
    session_id = cl.user_session.get("session_id")

    response = await client.sessions.chat(session_id=session_id, messages=[{"content": message.content, "role": "user"}], recall=True, remember=True, max_tokens=1000)
    await cl.Message(
        content=response.response[0][0].content,
    ).send()
```

Finally run `chainlit run app.py -w` and open `http://localhost:8000`    


![RPG Bot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/di2fi9mlqysvaiavnwlz.png)


![Chef kiss](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3fssqfzj9t5g8y40dwla.gif)

With just a few lines of code and some prompting, we were able to make a simple text game. The responses might look different, but refining the instructions can make them more consistent for each game. You can view the entire code at [https://github.com/philipbalbas/rpg-bot](https://github.com/philipbalbas/rpg-bot)


Anyway, this might be one of a series of use cases, so keep an eye out. Don't forget to leave a ⭐ on the [Julep repo](https://github.com/julep-ai/infuse/) to get more updates on the incredible features the Julep team is adding.


![Star](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ou7o6x0nxio1o2zp6838.gif)