05. HTML Frontend Basics

Chapter 5 of 15 · 15 min

Create app/templates/index.html. This is a single-file frontend—no build step, no framework:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Local Chatbot</title>
  <style>
    body { font-family: system-ui; max-width: 720px; margin: 2rem auto; padding: 0 1rem; }
    #chat { border: 1px solid #ccc; height: 400px; overflow-y: auto; padding: 1rem; }
    .msg { margin: 0.5rem 0; }
    .user { color: #0066cc; }
    .assistant { color: #333; }
  </style>
</head>
<body>
  <h1>Chatbot</h1>
  <div id="chat"></div>
  <form id="form">
    <textarea id="input" rows="3" style="width:100%;"></textarea>
    <button type="submit">Send</button>
  </form>
  <script>
    // JS goes here
  </script>
</body>
</html>

Wire it into FastAPI. Update app/main.py:

from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles

app = FastAPI()

@app.get("/", response_class=HTMLResponse)
async def root():
    with open("app/templates/index.html") as f:
        return f.read()

If the file path is wrong, FastAPI returns a 500 with FileNotFoundError. Use an absolute path or mount the templates directory. For now, use a relative path from the project root.

Local verification checkpoint

Run the smallest example from this chapter in a local workspace and record the package version, runtime, data path, and observed output. If the result depends on model size, vector count, CPU/GPU backend, or available memory, note that constraint beside the exercise so the lesson remains reproducible.

Local verification checkpoint

Run the smallest example from this chapter in a local workspace and record the package version, runtime, data path, and observed output. If the result depends on model size, vector count, CPU/GPU backend, or available memory, note that constraint beside the exercise so the lesson remains reproducible.

EXERCISE

Add a model selector <select id="model"> to the HTML and populate it via a fetch("/models") call on page load.