Building a Chat Application with Streamlit, SQLite, and xAI's Grok API

Learn how to create a chat application using Streamlit, SQLite for conversation storage, and xAI's Grok API for AI responses.

Building a Chat Application with Streamlit, SQLite, and xAI's Grok API

This tutorial walks you through creating a chat application using Streamlit, SQLite for conversation storage, and xAI's Grok API for AI responses. The app allows users to start new conversations, view past conversations, and interact with Grok, an AI assistant.

Prerequisites

  • Python 3.8+
  • Streamlit (pip install streamlit)
  • OpenAI Python client (pip install openai)
  • SQLite (included in Python standard library)
  • xAI API key (obtain from xAI)

Project Structure

The application consists of a single Python script that handles:

  • Database setup with SQLite
  • Conversation management
  • API integration with xAI's Grok
  • Streamlit UI for chat and conversation navigation

Below is the complete code, followed by a detailed breakdown.

import streamlit as st
from openai import OpenAI
import sqlite3
import uuid
from datetime import datetime

# Database setup
def init_db():
    conn = sqlite3.connect('chat_history.db')
    c = conn.cursor()
    c.execute('''
    CREATE TABLE IF NOT EXISTS conversations
    (conversation_id TEXT, timestamp TEXT)
    ''')
    c.execute('''
    CREATE TABLE IF NOT EXISTS messages
    (id INTEGER PRIMARY KEY, conversation_id TEXT, role TEXT, content TEXT, timestamp TEXT)
    ''')
    conn.commit()
    conn.close()

def save_message(conversation_id, role, content):
    conn = sqlite3.connect('chat_history.db')
    c = conn.cursor()
    timestamp = datetime.now().isoformat()
    c.execute("INSERT INTO messages (conversation_id, role, content, timestamp) VALUES (?, ?, ?, ?)",
              (conversation_id, role, content, timestamp))
    conn.commit()
    conn.close()

def get_conversation_history(conversation_id):
    conn = sqlite3.connect('chat_history.db')
    c = conn.cursor()
    c.execute("SELECT role, content FROM messages WHERE conversation_id = ? ORDER BY id", (conversation_id,))
    messages = c.fetchall()
    conn.close()
    return [{"role": role, "content": content} for role, content in messages]

def get_all_conversations():
    conn = sqlite3.connect('chat_history.db')
    c = conn.cursor()
    c.execute("SELECT DISTINCT conversation_id, timestamp FROM conversations ORDER BY timestamp DESC")
    conversations = c.fetchall()
    conn.close()
    return conversations

def create_new_conversation():
    conversation_id = str(uuid.uuid4())
    conn = sqlite3.connect('chat_history.db')
    c = conn.cursor()
    timestamp = datetime.now().isoformat()
    c.execute("INSERT INTO conversations (conversation_id, timestamp) VALUES (?, ?)", 
              (conversation_id, timestamp))
    conn.commit()
    conn.close()
    
    # Add system message to the new conversation
    system_message = "You are Grok, an extremely intelligent developer and entrepreneur. Specializing in various programming languages."
    save_message(conversation_id, "system", system_message)
    
    return conversation_id

# Initialize database
init_db()

# Initialize xAI client with hardcoded API key
client = OpenAI(
    api_key="you're_api_key_here",  
    base_url="https://api.x.ai/v1"
)

# Initialize session state
if "conversation_id" not in st.session_state:
    conn = sqlite3.connect('chat_history.db')
    c = conn.cursor()
    c.execute("SELECT conversation_id FROM conversations ORDER BY timestamp DESC LIMIT 1")
    result = c.fetchone()
    conn.close()
    
    if result:
        st.session_state.conversation_id = result[0]
    else:
        st.session_state.conversation_id = create_new_conversation()

# Sidebar for conversation management
with st.sidebar:
    st.title("Conversations")
    
    if st.button("New Conversation"):
        st.session_state.conversation_id = create_new_conversation()
        st.rerun()
    
    st.subheader("Previous Conversations")
    conversations = get_all_conversations()
    
    for conv_id, timestamp in conversations:
        dt = datetime.fromisoformat(timestamp)
        formatted_time = dt.strftime("%Y-%m-%d %H:%M")
        if st.button(f"{formatted_time}", key=conv_id):
            st.session_state.conversation_id = conv_id
            st.rerun()

# Main chat interface
st.title("Chat with GrokMonster")

# Load conversation history
conversation_history = []
if st.session_state.conversation_id:
    conversation_history = get_conversation_history(st.session_state.conversation_id)

# Input box for user message
user_input = st.text_input("Your message:", key="user_input")

if st.button("Send"):
    if user_input and st.session_state.conversation_id:
        save_message(st.session_state.conversation_id, "user", user_input)
        try:
            messages = get_conversation_history(st.session_state.conversation_id)
            response = client.chat.completions.create(
                model="grok-3",
                messages=messages,
                max_tokens=2000,
                temperature=0.7
            )
            grok_response = response.choices[0].message.content
            save_message(st.session_state.conversation_id, "assistant", grok_response)
        except Exception as e:
            st.error(f"Error: {str(e)}")
        st.rerun()

# Display conversation
for msg in conversation_history:
    if msg["role"] == "user":
        st.write(f"**You**: {msg['content']}")
    elif msg["role"] == "assistant":
        st.write(f"**Grok**: {msg['content']}")

Step-by-Step Breakdown

1. Setting Up the Database

The app uses SQLite to store conversations and messages. The init_db function creates two tables:

  • conversations: Stores unique conversation IDs and timestamps.
  • messages: Stores individual messages with their conversation ID, role (user, assistant, or system), content, and timestamp.
def init_db():
    conn = sqlite3.connect('chat_history.db')
    c = conn.cursor()
    c.execute('''
    CREATE TABLE IF NOT EXISTS conversations
    (conversation_id TEXT, timestamp TEXT)
    ''')
    c.execute('''
    CREATE TABLE IF NOT EXISTS messages
    (id INTEGER PRIMARY KEY, conversation_id TEXT, role TEXT, content TEXT, timestamp TEXT)
    ''')
    conn.commit()
    conn.close()

The database is initialized when the app starts with init_db().

2. Database Operations

Several functions manage data storage and retrieval:

  • save_message: Saves a message to the messages table with the conversation ID, role, content, and timestamp.
  • get_conversation_history: Retrieves all messages for a given conversation ID, formatted as a list of role-content dictionaries.
  • get_all_conversations: Fetches all conversation IDs and their timestamps, ordered by recency.
  • create_new_conversation: Generates a unique conversation ID, saves it to the conversations table, and adds a system message defining Grok's role.
def create_new_conversation():
    conversation_id = str(uuid.uuid4())
    conn = sqlite3.connect('chat_history.db')
    c = conn.cursor()
    timestamp = datetime.now().isoformat()
    c.execute("INSERT INTO conversations (conversation_id, timestamp) VALUES (?, ?)", 
              (conversation_id, timestamp))
    conn.commit()
    conn.close()
    
    # Add system message to the new conversation
    system_message = "You are Grok, an extremely intelligent developer and entrepreneur. Specializing in various programming languages."
    save_message(conversation_id, "system", system_message)
    
    return conversation_id

3. xAI API Integration

The app uses the OpenAI client to connect to xAI's API:

client = OpenAI(
    api_key="you're_api_key_here",  
    base_url="https://api.x.ai/v1"
)

When a user sends a message, the app:

  1. Saves the user's message to the database
  2. Retrieves the full conversation history
  3. Sends the history to xAI's API
  4. Saves Grok's response to the database
if st.button("Send"):
    if user_input and st.session_state.conversation_id:
        save_message(st.session_state.conversation_id, "user", user_input)
        try:
            messages = get_conversation_history(st.session_state.conversation_id)
            response = client.chat.completions.create(
                model="grok-3",
                messages=messages,
                max_tokens=2000,
                temperature=0.7
            )
            grok_response = response.choices[0].message.content
            save_message(st.session_state.conversation_id, "assistant", grok_response)
        except Exception as e:
            st.error(f"Error: {str(e)}")
        st.rerun()

4. Streamlit UI

The UI is built with Streamlit and consists of:

  • A sidebar for managing conversations
  • A main chat interface for displaying messages
  • An input box for sending new messages
# Sidebar for conversation management
with st.sidebar:
    st.title("Conversations")
    
    if st.button("New Conversation"):
        st.session_state.conversation_id = create_new_conversation()
        st.rerun()
    
    st.subheader("Previous Conversations")
    conversations = get_all_conversations()
    
    for conv_id, timestamp in conversations:
        dt = datetime.fromisoformat(timestamp)
        formatted_time = dt.strftime("%Y-%m-%d %H:%M")
        if st.button(f"{formatted_time}", key=conv_id):
            st.session_state.conversation_id = conv_id
            st.rerun()

The chat display shows messages with different formatting based on their role:

# Display conversation
for msg in conversation_history:
    if msg["role"] == "user":
        st.write(f"**You**: {msg['content']}")
    elif msg["role"] == "assistant":
        st.write(f"**Grok**: {msg['content']}")

Running the Application

To run this application:

  1. Save the code in a file (e.g., app.py).
  2. Install dependencies: pip install streamlit openai.
  3. Replace the API key placeholder with your actual xAI API key.
  4. Run the app: streamlit run app.py.

The app will launch in your browser, allowing you to chat with Grok and manage conversations.

Conclusion

This tutorial demonstrated how to build a chat application with Streamlit and xAI's Grok API. The app provides a simple yet effective interface for interacting with Grok, with the added benefit of conversation persistence through SQLite.

You can extend this application by:

  • Adding user authentication
  • Implementing conversation titles
  • Adding the ability to delete conversations
  • Enhancing the UI with custom CSS
  • Implementing streaming responses for a more interactive experience

Happy coding!

All rights reserved.