RUNLOCALAIv38
->Will it run?Best GPUCompareTroubleshootStartLearnPulseModelsHardwareToolsBench
Run check
RUNLOCALAI

Independently operated catalog for local-AI hardware and software. Hand-written verdicts. Source-cited claims. Reproducible commands when we have them.

OP·Fredoline Eruo
DIR
  • Models
  • Hardware
  • Tools
  • Benchmarks
TOOLS
  • Will it run?
  • Compare hardware
  • Cost vs cloud
  • Choose my GPU
  • Prompting kits
  • Quick answers
REF
  • All buyer guides
  • Learn local AI
  • Methodology
  • Glossary
  • Errors KB
  • Trust
EDITOR
  • About
  • Author
  • How we make money
  • Editorial policy
  • Contact
LEGAL
  • Privacy
  • Terms
  • Sitemap
MAIL · MONTHLY DIGEST
Get monthly local AI changes
Monthly recap. No spam.
DISCLOSURE

Some links on this site are affiliate links (Amazon Associates and other first-class retailers). When you buy through them, we earn a small commission at no extra cost to you. Affiliate links do not influence our verdicts — there are cards we rate highly that we don't have affiliate relationships with, and cards that sell well that we refuse to recommend. Read more →

© 2026 runlocalai.coIndependently operated
RUNLOCALAI · v38
  1. >
  2. Home
  3. /Learn
  4. /Courses
  5. /First Local Chatbot
  6. /Ch. 8
First Local Chatbot

08. Session Management

Chapter 8 of 15 · 15 min
KEY INSIGHT

Session storage decouples the browser (stateless UI) from the conversation state (server-side history).

Sessions let multiple users share the same server without seeing each other's conversations. FastAPI stores sessions in memory:

from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
import uuid

app = FastAPI()
sessions: dict[str, list[dict]] = {}

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

@app.get("/session/{session_id}")
def get_session(session_id: str):
    return {"history": sessions.get(session_id, [])}

@app.post("/chat")
async def chat(session_id: str, model: str, messages: list[dict]):
    # Persist incoming user message
    if session_id not in sessions:
        sessions[session_id] = []
    sessions[session_id].extend(messages)

    def stream():
        from app.ollama_client import stream_chat
        for chunk in stream_chat(model, sessions[session_id]):
            yield chunk

    return StreamingResponse(stream(), media_type="text/event-stream")

@app.post("/sessions/{session_id}/clear")
def clear_session(session_id: str):
    sessions[session_id] = []
    return {"ok": True}

On the frontend, generate a session ID once and store it in localStorage:

let sessionId = localStorage.getItem("sessionId");
if (!sessionId) {
  sessionId = crypto.randomUUID();
  localStorage.setItem("sessionId", sessionId);
}

The session ID is sent as a URL path parameter or query string with every request.

A failure mode: in-memory sessions are lost on server restart. For a first chatbot this is acceptable. A production version would use Redis or a database.

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 "New Chat" button that generates a new session ID, clears localStorage, and reloads the page.

← Chapter 7
Conversation Memory
Chapter 9 →
Personality Configuration