Skip to content
Boomspot
  • Home
Loading...
Boomspot

Daily tech news, software development coverage, Apple reporting, and the gear behind modern music making.

TwitterLinkedIn

Browse

  • Categories
  • Tags
  • Authors

Company

  • About
  • Contact

Legal

  • Privacy Policy
  • Terms of Service
  • Unsubscribe

© 2026 Boomspot. All rights reserved.

Built by Boomspot
Updated hourly

AI Content Disclosure: Articles on Boomspot are researched, written, and edited with the assistance of advanced AI systems. We combine software-assisted research with editorial oversight to deliver useful, accurate, and practical technical and music production content. Learn more about our editorial approach.

Browse by Category

Technology663Coding174Linux52SEO48Music Production33Studio Gear30Apple Rumors11

Popular Posts

Raspberry Pi 5 Alternatives After the $77.50 Price Hike

Raspberry Pi 5 Alternatives After the $77.50 Price Hike

6 min read
GamesSoundCon 2026: Worth It for Composers?

GamesSoundCon 2026: Worth It for Composers?

6 min read
Why External Hard Drive Prices Are Skyrocketing in 2026

Why External Hard Drive Prices Are Skyrocketing in 2026

5 min read
Which Ethernet Cable Category Do You Actually Need?

Which Ethernet Cable Category Do You Actually Need?

6 min read
ROCm or Vulkan for llama.cpp on AMD GPUs? How to Decide

ROCm or Vulkan for llama.cpp on AMD GPUs? How to Decide

8 min read

Recent Posts

close_range Syscall Explained: What Sync Closing Changes

close_range Syscall Explained: What Sync Closing Changes

Oct 23, 2026•6 min
Internet Speed for Remote Music Collaboration Explained

Internet Speed for Remote Music Collaboration Explained

Oct 23, 2026•8 min
Serve a 503 Without PHP During Maintenance: Setup Guide

Serve a 503 Without PHP During Maintenance: Setup Guide

Oct 23, 2026•8 min
Speed Up Linux Kernel Compile Time: 7 Practical Fixes

Speed Up Linux Kernel Compile Time: 7 Practical Fixes

Oct 23, 2026•8 min
Beginner-Friendly Terminal Editor: TTT vs micro vs Edit

Beginner-Friendly Terminal Editor: TTT vs micro vs Edit

Oct 23, 2026•8 min
  1. Home
  2. Coding
  3. Build a Streamlit Chatbot With OpenRouter Free Models
coding7 min read

Build a Streamlit Chatbot With OpenRouter Free Models

A free AI tutor runs on Streamlit for $0. Here is how to build your own: chat UI, session memory, an editable system prompt, safe API keys, and a plan for when the free tier fails.

S

Staff

October 23, 2026

Reviewed byDorian

Build a Streamlit Chatbot With OpenRouter Free Models

A $0 AI chat tool still needs three decisions: where the API key lives, how the chat remembers earlier messages, and what happens when a free model is busy. The Lazy Python Tutor shows the goal. Its author describes a Streamlit app on OpenRouter's free tier with a single system prompt and session-state memory, running at $0 (source post, repo). This guide skips the recap and builds a minimal version you can own.

All code below is unexecuted. Treat it as a starting point and work through the verification checklist before trusting it.

What you need before you start

  1. A free OpenRouter account and an API key from its dashboard.
  2. Python installed locally. Check the current Streamlit documentation for supported Python versions.
  3. Two packages: streamlit and openai. OpenRouter documents an OpenAI-compatible API, so the OpenAI client should work when pointed at OpenRouter's base URL. Confirm that in the OpenRouter docs.
  4. A GitHub account, only if you plan to deploy.

The source post says an openrouter/free auto-router picks a working free open-weight model per call, with no credit card needed. It does not give rate limits, exact request formats, or package versions.

Before you rely on any of this, check the current OpenRouter docs for the free-router model name, free-tier limits, and data policies for free models. Also confirm the latest package versions on PyPI.

Step 1: Set up the project

Create a folder and a virtual environment, then add a requirements.txt:

streamlit
openai

Leave these unpinned for your first run. After the app works locally, run pip freeze and pin the versions that worked for you. Install with pip install -r requirements.txt.

Step 2: Keep the API key out of your code

Create .streamlit/secrets.toml in your project folder:

OPENROUTER_API_KEY = "paste-your-key-here"

Add .streamlit/secrets.toml to .gitignore before your first commit. Treat a key pushed to a public repo as leaked, and regenerate it. The app reads the key through st.secrets, so the code never contains it.

Step 3: Write the app

Save this as app.py. The teaching behavior lives in one block at the top, so you can retune the tutor by editing plain English. The prompt adapts the answer shape the source post describes: plain English first, a short example, one optional practice prompt, and a word limit.

## app.py (UNEXECUTED: verify locally before trusting)
import streamlit as st
from openai import (
    OpenAI,
    AuthenticationError,
    RateLimitError,
    APIConnectionError,
    APIStatusError,
)

## Check the OpenRouter docs for the current free-router model name.
MODEL = 'openrouter/free'
MAX_TURNS_SENT = 12  # only the last N messages go to the model

SYSTEM_PROMPT = '''
You are a patient Python tutor for total beginners.

Answer shape, always in this order:
1. Plain-English explanation first. Explain any jargon the moment you use it.
2. One short runnable example, under 10 lines.
3. One tiny optional practice prompt, labeled 'Feeling ambitious? Try this:'. We cover related ground in [how to draft customer support emails with ollama locally explained](/how-to-draft-customer-support-emails-with-ollama-locally).

Rules:
- Keep the answer under about 200 words, not counting code.
- Give exactly one example. Never dump code without explaining it first.
- If the user pastes broken code, name the single most likely problem,
  show the fixed version, and say what changed in one sentence.
- Be kind. Never make the user feel silly for a small syntax mistake.
- If you are not sure, say so instead of guessing.
'''

st.set_page_config(page_title='Lazy Tutor')
st.title('Lazy Python Tutor')
st.caption('One question, one plain answer, one tiny example.')

try:
    api_key = st.secrets['OPENROUTER_API_KEY']
except Exception:
    st.error('No API key found. Add OPENROUTER_API_KEY to .streamlit/secrets.toml (local) or the Secrets box (Cloud).')
    st.stop()

client = OpenAI(base_url='https://openrouter.ai/api/v1', api_key=api_key)

if 'messages' not in st.session_state:
    st.session_state.messages = []

for m in st.session_state.messages:
    with st.chat_message(m['role']):
        st.markdown(m['content'])

prompt = st.chat_input('Ask a Python question')
if prompt:
    st.session_state.messages.append({'role': 'user', 'content': prompt})
    with st.chat_message('user'):
        st.markdown(prompt)

    with st.chat_message('assistant'):
        answer = None
        try:
            with st.spinner('Thinking...'):
                resp = client.chat.completions.create(
                    model=MODEL,
                    messages=[{'role': 'system', 'content': SYSTEM_PROMPT}]
                    + st.session_state.messages[-MAX_TURNS_SENT:],
                    timeout=60,
                )
            answer = (resp.choices[0].message.content or '').strip()
            if not answer:
                st.warning('The model returned an empty answer. Please ask again.')
                answer = None
        except AuthenticationError:
            st.error('OpenRouter rejected the API key. Check or regenerate it.')
        except RateLimitError:
            st.warning('Free tier is rate limited right now. Wait a minute and retry.')
        except APIConnectionError:
            st.error('Could not reach OpenRouter. Check your connection.')
        except APIStatusError as e:
            st.error(f'Model or server error (HTTP {e.status_code}). Try again shortly.')

        if answer:
            st.markdown(answer)
            st.session_state.messages.append({'role': 'assistant', 'content': answer})
        else:
            # Drop the unanswered question so history stays user/assistant paired.
            st.session_state.messages.pop()

Why memory works this way

Streamlit reruns the whole script on every interaction, so ordinary variables reset. st.session_state.messages survives reruns. That is why the loop near the top can redraw the conversation, and why follow-ups carry context.

The system prompt is not stored in that list. The app prepends it on every call, so it shapes each answer without cluttering the visible chat. We cover related ground in filesystem mcp server in claude desktop: safe setup guide in depth.

The [-MAX_TURNS_SENT:] slice caps how much history goes out. Smaller requests are a sensible habit on a free tier, though the right number is your call.

Step 4: Run it and check the result

Run streamlit run app.py. You should see a page titled "Lazy Python Tutor" with a chat box at the bottom. This description is expected behavior, not output captured from a real run.

Ask "What is a variable?" After a spinner, a reply should appear that explains the idea in plain English, shows a short example, and ends with a practice prompt. Exact wording will vary by model.

Verification checklist:

  1. The app starts with no red error banner.
  2. A first question returns an answer in the shape your prompt asks for (explanation, example, practice prompt).
  3. A follow-up such as "Can you show that with a list?" refers to the earlier answer, which shows that memory works.
  4. Changing a rule in SYSTEM_PROMPT, such as the word limit, and rerunning visibly changes the answers.
  5. Running git status does not list secrets.toml.

Step 5: Break it on purpose

Free tiers fail in ordinary ways. The source post says the auto-router sends a request to another model when one free model is busy. It does not say the router removes every failure, so keep the handlers.

Also read: see validate csv upload encoding in javascript, step by step

| Failure | What you see | Fix | |---|---|---| | Missing key | "No API key found" banner, app stops | Add OPENROUTER_API_KEY to secrets.toml or Cloud Secrets, then rerun | | Invalid or revoked key | "OpenRouter rejected the API key" | Regenerate the key in the OpenRouter dashboard and update the secret | | Rate limit (HTTP 429) | "Free tier is rate limited" warning | Wait and retry. Check the OpenRouter docs for current free-tier limits | | Model error or removed model | "Model or server error" with a status code | Retry, then check the docs for the current free-router name and update MODEL | | Empty response | "Returned an empty answer" warning | Ask again or rephrase the question |

To test the first row, temporarily rename the secret. To test the second, put a nonsense value in the key. Restore both afterward.

Step 6: Deploy to Streamlit Community Cloud

Push the project to GitHub with app.py and requirements.txt, but not secrets.toml. In Streamlit Community Cloud, create a new app, choose the repo, and set the main file to app.py.

Before deploying, open the advanced settings and paste the same line from your local secrets file into the Secrets box. Check Streamlit's current deployment docs for exact menu names, since interfaces change.

Once the app is live, repeat the verification checklist on the public URL, including a follow-up question.

Where to go next

The original author lists three upgrades: a paste box for error messages, voice input, and a button that turns chat history into short recap cards. Each one is a small change on top of this same loop.

Pin your working package versions first, so a future update does not quietly break a tutor that works today.

Tags

Artificial IntelligenceSoftware DevelopmentCoding TutorialsOpenaiWeb Development

Keep reading

WebAssembly: Unleashing Native Speed in Web Browsers
Coding•4 min read

WebAssembly: Unleashing Native Speed in Web Browsers

WebAssembly is transforming web development with near-native performance, enabling more complex and efficient applications.

Sep 6, 2025

Your Guide to GitHub Universe 2025: Schedule Launched!
Coding•3 min read

Your Guide to GitHub Universe 2025: Schedule Launched!

Get ready for GitHub Universe 2025! Check out the schedule, create your personalized agenda, and sign up for mentoring sessions. Join us for an exciting experience!

Sep 13, 2025

Many Hard LeetCode Problems Are Easy Constraint Problems
Coding•5 min read

Many Hard LeetCode Problems Are Easy Constraint Problems

Many hard LeetCode problems are easier when viewed through constraints. Learn how to simplify your coding challenges and enhance your skills.

Sep 13, 2025

More stories for your next project

Get tech, coding, and music production updates in your inbox.

Unsubscribe anytime.