Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/holzerjm/civichacks-demo/llms.txt

Use this file to discover all available pages before exploring further.

Overview

This step wraps the Bring Your Own Data (BYOD) experience in a Gradio web interface with drag-and-drop file upload. You get the same BYOD functionality as Step 4, but in a polished browser interface. Duration: ~2 minutes to launch What you’ll get:
  • Drag-and-drop file upload or select from userdata/ directory
  • Support for loading single or multiple files at once
  • File analysis metadata displayed in the UI
  • AI summary shown as the first chat message
  • Interactive Q&A chat with cost comparison

Prerequisites

Complete Step 4: Bring Your Own Data first. All dependencies should already be installed.

Running the app

Basic usage

python scripts/demo_step5_byod_app.py
The app starts on http://localhost:8861 and should open automatically in your default browser.
This uses port 8861 (different from Step 3’s 7860) so you can run both apps simultaneously.

Command-line options

# Launch on default port 8861
python scripts/demo_step5_byod_app.py

# Launch on custom port
python scripts/demo_step5_byod_app.py --port 8080

# Use a different model
python scripts/demo_step5_byod_app.py --model phi3:mini

# Create a public URL for sharing
python scripts/demo_step5_byod_app.py --share

# Show all options
python scripts/demo_step5_byod_app.py --help
OptionDefaultDescription
--port8861HTTP port for the web UI
--modelllama3.1Ollama model to use
--shareoffCreate a temporary public URL via Gradio’s tunneling service

UI components

The web app includes these interactive elements: Dynamic header that updates when files are loaded, showing:
  • “CivicHacks BYOD AI Assistant” title
  • Loaded file names
  • Hostname and start time
  • Privacy note: “no cloud, no cost, no data leaving this machine”

File loading section

Two tabs for loading files: Upload Files tab:
  • Drag-and-drop area for uploading .txt, .pdf, .csv, .docx files
  • Supports multiple files
  • “Load & Analyze” button
From userdata/ Directory tab:
  • Checkbox list of files in userdata/ directory
  • “Load Selected” button — load checked files
  • “Load ALL” button — load every file into a single index
  • ”↻ Refresh” button — rescan the directory for new files

File analysis display

Markdown display showing metadata for each loaded file:
  • File name (bold)
  • Type (Plain text, PDF document, CSV spreadsheet, Word document)
  • Size (KB or MB)
  • Modified date
  • Content: number of chunks and word count

Chat interface

Message-style chat with:
  • First message: AI summary of the loaded files
  • User/assistant bubbles
  • Red Hat avatar for assistant responses
  • Cost comparison metadata on each response

Question input

  • Text field with placeholder: “Ask anything about your loaded data…”
  • “Ask” button (also supports Enter key)
Shows the full open source stack and privacy information:
  • Stack: Ollama + LlamaIndex + Gradio
  • Model: [model name]
  • Host: [your hostname]
  • Data Privacy: 100% local
  • Cost: per-query estimate shown in each response

How it works

The app uses these key components:
1

File discovery and validation

  • find_userdata_files() scans the userdata/ directory for supported file types
  • validate_uploaded_file() validates file path, extension, and size (returns error tuple instead of exiting)
  • analyze_file_metadata() returns markdown string with file metadata for display
2

Load and index files

The load_and_index_files() function:
def load_and_index_files(filepaths, progress=gr.Progress()):
    progress(0.1, desc="Loading files...")

    all_documents = []
    for fp in filepaths:
        docs = SimpleDirectoryReader(input_files=[str(fp)]).load_data()
        all_documents.extend(docs)

    progress(0.5, desc="Building vector index...")
    index = VectorStoreIndex.from_documents(all_documents)

    progress(0.7, desc="Generating AI summary...")
    prompt = MULTI_SUMMARY_PROMPT if len(filepaths) > 1 else SUMMARY_PROMPT
    response = query_engine.query(prompt)

    progress(1.0, desc="Ready!")
    return index, analysis_md, summary_with_meta
Uses gr.Progress() for visual feedback during loading.
3

Query BYOD data

The query_byod_data() function queries the index and appends the response with cost metadata to chat history:
def query_byod_data(question, history, index_state, loaded_files_state):
    if index_state is None:
        return history + [
            {"role": "user", "content": question},
            {"role": "assistant", "content": "Please load a file first..."},
        ], ""

    history = history + [{"role": "user", "content": question}]

    query_engine = index_state.as_query_engine(similarity_top_k=3)
    start = time.time()
    response = query_engine.query(question)
    elapsed = time.time() - start

    answer = str(response)
    cost_info = format_cost_short(elapsed, est_input_tokens, est_output_tokens)
    answer += f"\n\n---\n*⏱️ {elapsed:.1f}s · 🤖 {MODEL_NAME} · 💰 {cost_info}*"

    history = history + [{"role": "assistant", "content": answer}]
    return history, ""
4

Session state

Uses gr.State to store per-session index and loaded file list:
with gr.Blocks(title="CivicHacks BYOD AI Assistant") as app:
    # -- State --
    index_state = gr.State(value=None)
    loaded_files_state = gr.State(value=[])

    # ... UI components ...

    upload_btn.click(
        fn=on_upload_files,
        inputs=[file_upload, index_state, loaded_files_state],
        outputs=[file_info, chatbot, index_state, loaded_files_state, header],
    )
This supports multiple concurrent browser sessions, each with its own index.

Workflow examples

Upload a single file

1

Go to Upload Files tab

Drag and drop a PDF, DOCX, TXT, or CSV file into the upload area.
2

Click Load & Analyze

Watch the progress indicator as it:
  1. Loads the file
  2. Builds the vector index
  3. Generates an AI summary
3

Review the summary

The AI summary appears as the first chat message, covering:
  • What the document is about
  • Key data points or findings
  • Three suggested questions
4

Ask questions

Type questions in the input field and click “Ask” or press Enter.Each response includes:
  • AI-generated answer grounded in your data
  • Timing (elapsed seconds)
  • Cost comparison (local electricity vs. cloud API)

Load multiple files from userdata/

1

Drop files into userdata/ directory

cp ~/Documents/*.pdf userdata/
2

Go to From userdata/ Directory tab

Click ”↻ Refresh” to rescan the directory.
3

Select files

Check the boxes next to the files you want to load.
4

Click Load Selected or Load ALL

  • Load Selected: Loads only checked files
  • Load ALL: Loads every file in the directory
Watch the progress indicator as it builds a combined index.
5

Ask cross-document questions

Now you can ask questions that span multiple files:
  • “What changed between these reports?”
  • “Which document discusses budget constraints?”
  • “What themes appear across all the data?”

Progress indicators

The app shows real-time progress during loading:
Loading files... 10%
Loading report.pdf... 30%
Building vector index... 50%
Generating AI summary... 70%
Ready! 100%
Progress indicators use Gradio’s gr.Progress() API to provide visual feedback during long-running operations.

Error handling

The app gracefully handles errors:
ErrorBehavior
Unsupported file typeShows error message, doesn’t load the file
Empty fileShows error message, doesn’t load the file
PDF with no textShows warning, skips the file
File read errorShows error message with details, skips the file
No files selectedShows prompt: “No files selected. Check the boxes…”
Query before loadingShows message: “Please load a file first…”
Errors are shown in the File Analysis section (markdown display) and don’t crash the app.

Session management

Each browser session maintains its own state:
  • Index: Each session builds its own vector index
  • Loaded files: Each session tracks its own loaded files
  • Chat history: Each session has its own message history
This means:
  • Multiple users can use the app simultaneously with different data
  • Refreshing the browser resets the session (clears index and chat)
  • Opening a new tab creates a new independent session

Customizing the theme

The Gradio theme is set with:
THEME = gr.themes.Soft(primary_hue="red", secondary_hue="slate")
CSS = """
    .header { text-align: center; margin-bottom: 1rem; }
    .header h1 { color: #CC0000; margin-bottom: 0.25rem; }
    .file-info { background: #f8f9fa; border-radius: 8px; padding: 1rem; }
"""
Change primary_hue to customize the color scheme:
THEME = gr.themes.Soft(primary_hue="blue", secondary_hue="slate")

Deployment options

Local only (default)

The app runs entirely on localhost:8861. No data ever leaves the machine.

Temporary public URL

python scripts/demo_step5_byod_app.py --share
Gradio provides a temporary public URL (valid ~72 hours) via tunneling. Example output:
Running on local URL:  http://127.0.0.1:8861
Running on public URL: https://xyz789abc123.gradio.live

This share link expires in 72 hours.
The public URL routes traffic through Gradio’s servers, but your data and model still run locally. Only the UI is tunneled.

Troubleshooting

Navigate manually to http://localhost:8861 in your browser.
Kill the existing process or change the port:
python scripts/demo_step5_byod_app.py --port 9000
Ensure the file is one of the supported types: .txt, .pdf, .csv, .docxCheck the File Analysis section for specific error messages.
Click ”↻ Refresh” to rescan the directory.Ensure files have supported extensions and are in the userdata/ directory (not a subdirectory).
This may indicate:
  • Very large file (>10 MB) — wait longer
  • PDF with images — text extraction is slow
  • Insufficient RAM — close other applications
Check the terminal output for error messages.
The file may not have extracted meaningful text. Check the File Analysis section for word count.For PDFs, ensure they’re text-based (not scanned images).

Comparison: Step 4 vs Step 5

FeatureStep 4 (Terminal)Step 5 (Web App)
InterfaceCommand-line REPLBrowser-based chat
File loadingCLI argument or promptDrag-and-drop or directory
Multiple files--all flagCheckboxes or “Load ALL”
File discoveryAuto-discoveryAuto-discovery + refresh
Progress feedbackTerminal outputProgress bar
Session persistenceSingle sessionMultiple concurrent sessions
SharingScreen share onlyPublic URL via --share
Best forQuick analysis, demosCollaboration, presentations

Next steps

You’ve completed all 5 tutorial steps! Now you can:

Deploy your app

Learn how to deploy your app to Hugging Face Spaces, Streamlit Cloud, or your own server.

Change the AI model

Try different models like phi3:mini for speed or deepseek-r1:7b for reasoning.

Customize the UI

Change themes, colors, and layout to match your style.

View on GitHub

Star the repo, open issues, and share your projects.

Build docs developers (and LLMs) love