Build an AI Chatbot
Build an AI chatbot and get it in front of real users: decide what it is actually for, design it, integrate a model, handle conversation state and retrieval, evaluate it, and deploy it.
This roadmap guides you step-by-step from zero programming background through scoping, coding, and deploying a functional AI chatbot accessible via the web. Over roughly 12 to 16 weeks at 10 hours per week, you will build an end-to-end Python backend API, integrate model APIs, add multi-turn memory and custom knowledge retrieval (RAG), and deploy a polished web interface. Finishing this roadmap leaves you with a live, production-deployed chatbot handling real user sessions and an automated evaluation suite to inspect answer quality.
By the end: You will have designed, built, and deployed a custom AI chatbot web application backed by a Python API with conversation memory and retrieval-augmented generation (RAG), and tested it with real users.
This is the map — make this roadmap yours
It shows what this journey generally looks like. Tell Kaidoro your version of the goal and it builds the plan around where you are actually starting, what to do first, the hours you really have, and what you have already finished.
Project Definition & Scope Blueprint
Establish exactly what domain your chatbot serves, its boundaries, and explicitly what features are cut from the first release.
- Define the chatbot use case, persona, and success criteria~3hScope
Clear boundaries prevent feature creep and determine exactly what prompt engineering and retrieval data you need later.
You'll learn
- System Prompt — The foundational instructions given to an LLM setting its role, behavior, and boundaries
- Persona Design — Structuring prompt rules to ensure consistent tone and voice
Decide on the specific problem your chatbot will solve (for example, a customer support agent for a fictional coffee shop, an interactive study tutor, or a personal recipe assistant). Document the user persona, system instructions, expected inputs, and measurable success criteria.
Done when: You have a one-page document detailing the bot's objective, target audience, 5 example dialogues, and clear success criteria.
How to work through it
- Select one specific domain and user problem
- Write the bot persona and tone guidelines
- Draft five ideal multi-turn conversation transcripts
- Define three failure conditions the bot must avoid
- Write the initial scope and out-of-scope cut list~2hScope
Explicit scope cuts protect personal projects from stalling before the first release.
You'll learn
- MVP (Minimum Viable Product) — The simplest version of a product that can be released to gather feedback
Formally document the absolute minimum feature set required for release (v1) versus features explicitly deferred to future updates (such as multi-modal image inputs, complex user authentication, or voice integration).
Done when: You have an explicit list of included v1 features and at least five explicitly excluded out-of-scope items signed off as deferred.
How to work through it
- List core requirements needed for a basic functional web chat
- List advanced features that will not be touched in v1
- Identify the single primary knowledge source for the bot
Python Environment & LLM API Fundamentals
Set up your local programming environment and learn to make structured calls to an LLM provider using Python.
- Set up Python, VS Code, and virtual environments~3hBuild1 resource
A clean local environment prevents package conflicts and forms the foundation of all subsequent coding tasks.
You'll learn
- venv — Python module used to create lightweight, isolated virtual environments
- pip — The package installer for Python libraries
- CLI (Command Line Interface) — The terminal interface used to run commands and scripts
Install Python 3.11+, configure Visual Studio Code, and learn how to manage isolated project dependencies using virtual environments.
Done when: You can create and activate a Python virtual environment and run a basic script from VS Code that outputs text to the terminal.
How to work through it
- Install Python 3.11 or newer on your operating system
- Install Visual Studio Code and the official Python extension
- Create a project directory and initialize a virtual environment using venv
- Run a test script verifying python execution and environment activation
- Make your first LLM API call via Python script~4hBuild1 resource
Directly interacting with the API teaches you how parameters like model, temperature, and message roles work under the hood.
You'll learn
- API Key — A secret token used to authenticate requests to external cloud services
- .env file — A plain text file used to store sensitive configuration variables locally
- Chat Completion — The API endpoint structure that takes a list of role-based messages and returns a model reply
- Temperature — A hyperparameter controlling the randomness of LLM text generation
Obtain an API key from an LLM provider (such as OpenAI or Anthropic), securely load it using environment variables, and send a structured chat completion request from Python.
Done when: Running your script sends a user message to the LLM API and prints the model's text response to the console without hardcoding credentials.
How to work through it
- Sign up for an LLM provider API account and generate an API key
- Install the official provider Python SDK and python-dotenv
- Store the API key in a .env file and add .env to a .gitignore file
- Write a script that loads the environment variable and requests a completion with system and user roles
The Walking Skeleton (End-to-End Slice)
Build the thinnest possible working version of the whole stack: a simple web page talking to a Python API backend that queries the LLM.
- Build a minimal FastAPI chat endpoint~4hBuild1 resource
Decoupling model logic into an API backend ensures that any frontend (web, mobile, or CLI) can consume the same conversational service.
You'll learn
- FastAPI — A modern, high-performance web framework for building APIs with Python
- Uvicorn — An ASGI web server implementation used to run FastAPI applications
- Pydantic — Python data validation library used by FastAPI to enforce request schemas
- REST Endpoint — A specific URL on a server designed to receive and respond to web requests
Create a lightweight REST API using FastAPI. The API exposes a single POST /chat endpoint that receives a JSON message, forwards it to the LLM, and returns the response JSON.
Done when: Sending a POST request with curl or Postman to http://localhost:8000/chat returns a valid JSON response containing the model's reply.
How to work through it
- Install FastAPI and Uvicorn in your environment
- Define a Pydantic schema for the incoming chat request and outgoing response
- Write the POST endpoint handler that invokes your LLM completion function
- Run the local development server with Uvicorn and test via the automatic interactive docs (/docs)
- Create a minimal single-page web UI connected to the backend~4hBuild
Having a complete end-to-end loop early eliminates integration unknowns and gives you a tangible product to iterate on.
You'll learn
- CORS — A security mechanism that controls how web pages on one origin access resources on another
- fetch API — JavaScript interface for making asynchronous HTTP network requests in the browser
- DOM Manipulation — Updating HTML elements dynamically via JavaScript
Write a basic HTML/JavaScript frontend that provides an input box, a submit button, and a message log. Use the browser fetch API to send the input to your local FastAPI backend and append the reply.
Done when: You can type a message in your web browser, press Send, and see the model's response appear in the message list without reloading the page.
How to work through it
- Create a simple index.html and style.css for chat layout
- Write a vanilla JavaScript function using fetch() to POST to http://localhost:8000/chat
- Enable CORS (Cross-Origin Resource Sharing) middleware in FastAPI
- Test typing a message in the browser and observing the rendered reply
Conversation Memory & Multi-Turn State
Give your chatbot conversational memory across turns and maintain separate sessions for different users using SQLite.
- Implement multi-turn message history formatting~3hBuild
LLMs are stateless by default; conversational context must be explicitly passed back with each new turn.
You'll learn
- Context Window — The maximum amount of text tokens an LLM can process in a single request
- Statelessness — The property of APIs where no client context is saved on the server across separate requests unless manually persisted
Update your API backend to accept an array of previous conversation messages rather than a single string, feeding the entire conversation history into the LLM context window.
Done when: The bot can correctly answer follow-up questions (e.g. "What was the first thing I asked you?") in a continuous multi-turn exchange.
How to work through it
- Update the Pydantic request schema to accept a list of message objects with role and content
- Ensure system instructions are prepended as the first message
- Test multi-turn context retention using API test calls
- Persist session conversations in a local SQLite database~5hBuild1 resource
Real web applications need persistence across refreshes and isolation between distinct user sessions.
You'll learn
- SQLite — A lightweight, file-based relational database management system
- Session ID (UUID) — Universally Unique Identifier used to distinguish one user conversation from another
- localStorage — Web browser storage API allowing key-value pairs to persist across page reloads
Implement persistent storage using SQLite and Python's built-in sqlite3 module. Generate unique session IDs so different conversations are saved and can be resumed across page refreshes.
Done when: Refreshing the web page and providing the same session ID restores the previous conversation history from the SQLite database.
How to work through it
- Design a simple SQL schema with sessions and messages tables
- Write helper functions in Python to create sessions, insert messages, and query history by session ID
- Update the FastAPI endpoints to load history from the DB before calling the LLM and save new turns afterwards
- Store and retrieve the active session ID in the browser using localStorage
- Add history trimming to prevent context overflow~3hBuild
Unbounded conversation history will eventually hit model token limits and dramatically increase API costs.
You'll learn
- Tokenization — The process of breaking down natural language text into numerical tokens for LLM computation
- Sliding Window — A context management strategy keeping only the most recent N items in memory
Implement a sliding-window context management utility that trims older messages or summarizes them when the conversation exceeds a predefined message or token threshold.
Done when: A test conversation with 30 consecutive turns does not exceed the model token limit or crash the API.
How to work through it
- Calculate approximate token count or message count limits for conversation history
- Implement a sliding-window algorithm that always keeps the system prompt and the last N turns
- Write unit tests verifying that old turns are safely excluded from the LLM payload while preserved in the DB
Knowledge Integration via RAG (Retrieval-Augmented Generation)
Enable your chatbot to answer domain-specific questions accurately by retrieving relevant excerpts from custom text documents.
- Prepare and chunk domain documents into text segments~3hBuild
LLMs cannot ingest entire libraries of text at once; splitting documents into focused chunks is prerequisite to accurate search retrieval.
You'll learn
- RAG (Retrieval-Augmented Generation) — Architecture where relevant data is retrieved from a database and injected into the prompt
- Chunking — Dividing large documents into small, coherent text passages for embedding and indexing
Collect the raw reference documents your bot needs (e.g., Markdown/text files or PDFs for your chosen use case) and write a Python script to clean and split them into semantic chunks.
Done when: Running the script outputs a list of document chunks averaging 200–500 words with modest overlap.
How to work through it
- Create a /data directory with 3 to 10 source reference documents for your use case
- Write a chunking script with configurable chunk size and chunk overlap
- Inspect the chunk boundaries to ensure sentences and concepts are not cut abruptly
- Generate embeddings and index chunks in ChromaDB~4hBuild
Vector embeddings allow the system to find relevant information by conceptual meaning rather than exact keyword matches.
You'll learn
- Vector Embeddings — Numerical vector representations of text capturing semantic meaning
- Vector Database — A database optimized for storing and querying high-dimensional vectors (e.g., ChromaDB)
- Cosine Similarity — A mathematical metric measuring how close two vectors are in direction
Use an embedding model to convert your text chunks into vector representations and store them in an embedded vector database like ChromaDB.
Done when: Your indexing script populates a local ChromaDB collection that can be queried programmatically.
How to work through it
- Install ChromaDB in your Python environment
- Initialize a Chroma collection using an embedding function
- Batch upload all chunked documents alongside metadata (source file, title)
- Run a test query in Python to verify that relevant chunks are returned for a semantic search query
- Integrate vector search into the backend prompt generation~4hBuild
This connects retrieval directly into the generation pipeline, grounding model answers in your specific facts.
You'll learn
- Prompt Injection (Context Augmentation) — Dynamically stuffing retrieved text into the prompt context
- Hallucination — An LLM generating plausible-sounding facts not grounded in truth or supplied context
Modify the FastAPI chat endpoint to query ChromaDB with the user's question, retrieve the top 3 relevant passages, and inject them into the system prompt as context.
Done when: The bot answers a specific question using facts found only in your source documents and explicitly cites the source.
How to work through it
- Add a retrieval step before calling the LLM API inside the chat route
- Construct a prompt template that injects retrieved context with clear framing instructions
- Instruct the model to say 'I do not have enough information' if the context is insufficient
- Test with queries both covered and uncovered by your custom documentation
Guardrails, Fallbacks & Error Handling
Harden the backend against failed API calls, edge-case user inputs, and out-of-domain requests.
- Add graceful API error handling and retries~3hBuild
Third-party APIs frequently experience intermittent latency, network drops, and rate limits that must not crash the web app.
You'll learn
- Exponential Backoff — A retry algorithm that increases the waiting time between successive attempts
- HTTP Status Codes — Standardized 3-digit codes (e.g. 200, 400, 503) indicating request results
Wrap LLM and database calls with try-except blocks, add automatic retries with exponential backoff for rate limits, and return friendly JSON error messages instead of 500 server crashes.
Done when: Simulating an invalid API key or a network timeout returns a clean HTTP 503 response with a readable user error message.
How to work through it
- Identify points of failure in API requests, database queries, and retrieval steps
- Implement tenacity or custom retry logic with exponential backoff for rate limits
- Define standard JSON error responses in FastAPI
- Test failure scenarios by triggering simulated errors
- Implement input validation and boundary guardrails~3hBuild
Unrestricted input invites abuse, high API bills, and unpredictable model behavior.
You'll learn
- Prompt Injection — An adversarial technique attempting to override an LLM's system instructions
- Input Sanitization — Cleaning and validating user input before processing
Set character length limits on incoming user prompts, sanitize empty or malformed inputs, and add system prompt rules to reject off-topic questions or prompt injection attempts.
Done when: Submitting empty messages, 10,000-character strings, or basic jailbreak phrases ("Ignore previous instructions") are safely rejected or neutralized.
How to work through it
- Add Pydantic string constraints (min/max length) on chat input schemas
- Add defensive rules in the system prompt establishing out-of-scope boundaries
- Write a simple rule-based filter rejecting prohibited keywords or oversized inputs before hitting the LLM
Frontend Polish & Streaming Responses
Upgrade the user interface from a bare-bones form into a responsive, real-time streaming chat experience.
- Implement Server-Sent Events (SSE) for streaming text~5hBuild
Streaming dramatically reduces perceived latency and delivers the standard interactive chatbot feel users expect.
You'll learn
- SSE (Server-Sent Events) — A web technology allowing a server to push real-time text updates to a client over HTTP
- Time to First Token (TTFT) — The time elapsed between a user request and the first output token rendering
Update the FastAPI endpoint to stream response tokens as they are generated by the LLM, and update the frontend JavaScript to render words incrementally.
Done when: The chatbot response begins appearing on the screen word-by-word within 1 second of submitting a message instead of waiting for the full generation.
How to work through it
- Configure the LLM API call with stream=True
- Use FastAPI StreamingResponse to emit chunks using the Server-Sent Events (text/event-stream) protocol
- Use ReadableStream and TextDecoder in frontend JavaScript to append incoming tokens to the message bubble in real time
- Polish the chat UI with Markdown rendering, auto-scroll, and responsive styling~4hBuild
Visual polish and responsive layout make the application trustworthy and usable across diverse devices.
You'll learn
- Markdown Parsing — Converting markdown syntax (e.g. asterisks, backticks) into styled HTML elements
- Responsive Design — CSS techniques ensuring a web layout adapts gracefully to different screen sizes
Integrate a client-side Markdown parser (such as marked.js) to render bold text, lists, and code blocks. Add automatic scrolling to the latest message, loading spinners, and mobile-friendly CSS.
Done when: Responses with markdown lists and code snippets render formatted properly, auto-scroll stays at the bottom during streaming, and layout works cleanly on both mobile and desktop screens.
How to work through it
- Import a lightweight Markdown library like marked.js into the frontend
- Add auto-scroll logic that sticks to the bottom during streaming unless the user manually scrolls up
- Add visual loading indicators for thinking/searching states
- Apply responsive CSS media queries for phone and tablet screens
Evaluation & Quality Benchmarking
Build an automated evaluation script to test answer quality, factual accuracy, and retrieval performance.
- Create an evaluation dataset of golden test queries~3hTest
You cannot reliably improve or tune a chatbot without a standardized benchmark of test questions.
You'll learn
- Golden Dataset — A curated set of inputs and expected reference outputs used to benchmark AI performance
- Regression Testing — Testing to verify that new code or prompt changes have not broken existing functionality
Create a JSON or CSV dataset containing at least 20 test prompts covering standard questions, tricky edge cases, out-of-scope requests, and factual questions based on your documents, alongside expected answer characteristics.
Done when: You have a documented evaluation file with 20+ test cases categorised by test type (e.g., factual, out-of-domain, adversarial).
How to work through it
- Draft 10 standard factual queries directly answerable by your RAG documents
- Draft 5 out-of-domain questions that should trigger a polite refusal
- Draft 5 boundary/injection test queries
- Save the dataset as a structured tests/eval_data.json file
- Build an automated LLM-as-a-judge evaluation runner~5hTest
Automated evaluation lets you iterate on system prompts and chunking strategies quickly without manually inspecting hundreds of outputs.
You'll learn
- LLM-as-a-Judge — Using a capable LLM to evaluate and score the output of another model against rubric criteria
- Faithfulness — Metric evaluating whether the generated answer is strictly supported by retrieved context
Write a Python script that executes the evaluation dataset against your chatbot API and uses an LLM to grade each output on faithfulness, answer relevance, and safety (score 1-5).
Done when: Running the evaluation script outputs a summary report displaying pass rates and average scores across all test categories.
How to work through it
- Write a script that iterates over eval_data.json and sends queries to the local API
- Prompt an LLM judge with criteria for faithfulness and relevance
- Parse judge scores and output a Markdown or CSV report with overall pass rate
- Fix any identified prompt weaknesses where scores fell below 4/5
Cloud Deployment & Production Hardening
Package the application and deploy both the backend API and frontend to a publicly accessible cloud hosting platform.
- Containerize the application with Docker or prepare production entry points~4hShip1 resource
Containerizing and standardizing build commands ensures the app runs identically in the cloud as it does locally.
You'll learn
- Docker — Platform for developing, shipping, and running applications in isolated containers
- Production Web Server — Multi-worker server setup capable of handling concurrent network requests reliably
Create a Dockerfile or standard requirements.txt configuration specifying production web server settings (e.g., Gunicorn/Uvicorn workers) and data directory setup.
Done when: The application builds and runs successfully in a clean container or fresh local folder using only production launch commands.
How to work through it
- Freeze all dependencies into a clean requirements.txt
- Write a Dockerfile specifying Python runtime, copying source code, and setting the startup command
- Test building and running the container locally
- Deploy the backend and frontend to a cloud platform (Render/Railway)~4hShip
Deploying to public infrastructure transforms your local code into a real, accessible product.
You'll learn
- PaaS (Platform as a Service) — Cloud hosting services that automate building, deploying, and scaling apps from Git
- Environment Variables in Production — Securely passing API credentials to cloud servers without committing them to source control
- HTTPS / SSL — Encrypted web communication protocol required for secure web applications
Deploy your backend service to a hosting platform like Render or Railway, configure cloud environment secrets for your LLM API keys, and host the web frontend.
Done when: Your chatbot is live at a public https:// URL and can be accessed and chatted with from an external device or smartphone.
How to work through it
- Push your project code to a GitHub repository
- Connect the repository to a cloud PaaS (e.g., Render, Railway, or Fly.io)
- Set environment variables (API keys, CORS origins) in the host dashboard
- Trigger deployment and verify the live HTTPS URL on an external network
- Add basic rate limiting and API budget caps~3hShip
Publicly exposed endpoints will be found by bots and scrapers; rate limits prevent unexpected bills and server overload.
You'll learn
- Rate Limiting — Restricting the number of API requests a user or client can send within a given time frame
- HTTP 429 Too Many Requests — Standard response indicating the user has sent too many requests in a given amount of time
Configure rate limiting on the public endpoint (e.g., using slowapi in FastAPI) and set hard billing spending limits in your LLM provider dashboard.
Done when: Exceeding 10 requests per minute from a single IP returns an HTTP 429 Too Many Requests response, and provider billing limits are confirmed active.
How to work through it
- Install and configure slowapi middleware on the FastAPI chat endpoint
- Set an IP-based limit (e.g., max 10 requests/minute per client)
- Log into your LLM provider dashboard and configure hard monthly usage spending limits
- Verify the 429 response using a script or rapid browser clicks
User Testing & Feedback Loop
Put the live chatbot in front of real people, inspect conversation logs, and execute targeted improvements based on real feedback.
- Conduct user testing with 5-10 real users~4hTest
Real humans ask unexpected questions in unpredictable ways that artificial evaluation sets never anticipate.
You'll learn
- User Acceptance Testing (UAT) — The phase of development where target users test the application in real-world scenarios
- Prompt Drift — Divergence between how a developer expects a system to be used versus actual user queries
Distribute your public URL to 5 to 10 testers with a brief prompt on what the bot is designed for. Observe how they converse with it, noting where the bot misunderstand requests, breaks character, or fails to find documents.
Done when: At least 5 independent users have tested the chatbot and provided structured feedback or conversation transcripts.
How to work through it
- Draft a 3-question testing prompt for your users (e.g. "Try to ask it X, then try to confuse it")
- Share the live link with 5-10 friends, colleagues, or community members
- Collect written feedback and observe any user pain points
- Analyze transcripts and ship an iteration update~4hShip
The development cycle finishes only after the product has absorbed feedback from its initial contact with reality.
You'll learn
- Iterative Development — A cycle of developing, testing, gathering feedback, and refining a software product
- Error Analysis — Systematic review of incorrect model responses to prioritize fixes
Review the chat logs and feedback from user testing. Identify the top 3 failure modes (e.g., missing documents in RAG, overly verbose answers, or confusing UI layout) and implement fixes in code and prompts.
Done when: You push a documented update to the live deployment fixing the top 3 issues identified during user testing.
How to work through it
- Group user failure modes into categories: prompt issues, missing knowledge, or UI/UX bugs
- Update source documents or RAG chunking to fix missing knowledge
- Refine system prompt constraints based on real user edge cases
- Deploy the updated build and verify improvements
How the plan fits together
10 phases. This plan names no prerequisites, so it reads as one sequence — each phase after the one before it.
An arrow points from a phase to the work it unlocks: before starting any phase, every phase with an arrow into it has to be finished first.
Resources
14 in this plan's library, beyond the links on individual tasks.
Learning Resources
Official documentation, Python guides, and LLM tutorials.
- Building Systems with the ChatGPT API
Use this short course when transitioning from basic single-prompt experiments to building multi-step chatbot workflows, safety moderation chains, and agent loops.
deeplearning.ai · DeepLearning.AI · Short course · Free
- OpenAI API Documentation and Quickstart
Refer to this definitive technical manual when configuring API credentials, structured Chat Completions, system prompts, token budgets, and real-time streaming.
platform.openai.com · OpenAI · Documentation · Free documentation; pay-per-token API usage
- OpenAI Cookbook
Use these runnable code recipes to implement vector embeddings, rate-limit backoff patterns with Tenacity, and automated evaluation workflows.
cookbook.openai.com · OpenAI · Code repository / Documentation · Free access; pay-per-token model execution
- Python sqlite3 Standard Library Documentation
Consult this guide to implement lightweight relational schemas for sessions and message histories without provisioning external database servers.
docs.python.org · Python Software Foundation · Documentation · Free
- Using Server-Sent Events
Understand the standard protocol for streaming token-by-token LLM completions directly to the web client.
developer.mozilla.org · MDN Web Docs · Documentation · Free · Intermediate
Tools & Services
Hosting platforms, vector stores, and model APIs.
- Langfuse Cloud
Integrate this observability platform to monitor LLM conversation traces, token expenditures, latency, user feedback ratings, and automated scorecards.
langfuse.com · Langfuse / ClickHouse · Observability platform · Free hobby tier (up to 50k observations/mo); open-source self-hostable
- OpenAI API Platform
The core cloud model provider platform hosting GPT-4o models, vector embeddings, and free moderation endpoints across the chatbot lifecycle.
platform.openai.com · OpenAI · Cloud API platform · Pay-as-you-go per-token (free Moderation API)
- PostHog Documentation
Integrate session recording and event analytics to monitor how real users interact with your chatbot interface.
PostHog · Platform Documentation · Generous free tier (up to 1M events/month free) · Intermediate
- Render
Use this platform-as-a-service to deploy backend FastAPI web services with automatic SSL, continuous GitHub deployments, and persistent SQLite disk storage.
render.com · Render Services, Inc. · Cloud platform (PaaS) · Free tier available; paid instances from ~$7/month
Open-Source Libraries
Core Python frameworks and UI utilities.
- ChromaDB
Embed this lightweight vector database directly into your application to store document chunks, create embeddings, and run semantic similarity searches.
docs.trychroma.com · Chroma · Python library / Vector database · Free (Apache 2.0)
- FastAPI
Adopt this high-performance asynchronous web framework to build backend API endpoints, validate incoming payloads, and stream Server-Sent Events.
fastapi.tiangolo.com · Sebastián Ramírez · Python library / Web framework · Free (MIT)
- Pydantic Documentation
Crucial for schema validation, enforcing structured LLM outputs, and catching malformed payloads.
docs.pydantic.dev · Pydantic · Official Documentation · Free · Intermediate
- Ragas
Use this evaluation framework to quantify RAG pipeline quality across metrics like Faithfulness, Context Recall, and Answer Relevancy.
docs.ragas.io · Exploding Gradients / Vibrant Labs · Python library / Evaluation framework · Free (Apache 2.0)
- Streamlit
Use this framework to rapidly create interactive chat user interfaces in pure Python using native streaming and message components.
docs.streamlit.io · Streamlit Inc. / Snowflake · Python library / UI framework · Free (Apache 2.0)