Embeddable chatbot widget that answers natural-language questions about your database.
Project description
db-chat-widget
Embeddable chatbot widget that lets end users ask natural-language questions about a SQL database and get back answers with the underlying query and data.
- Any SQLAlchemy-compatible database: PostgreSQL, MySQL, SQLite, etc.
- Pluggable LLM backend: Anthropic Claude, OpenAI, Groq, or a local Ollama model.
- Read-only by default: generated SQL is parsed and only
SELECTstatements are allowed unless you explicitly opt into write access. - Embeddable widget: a small vanilla-JS chat component you can drop into
any existing web page with a single
<script>tag, backed by a FastAPI server.
Install
pip install "db-chat-widget[server]"
This includes every LLM provider (Anthropic, OpenAI, Groq, Ollama), every
database driver (PostgreSQL, MySQL), and the standalone FastAPI server + CLI
serve command used below. Pick which provider and database to use
afterwards, at configuration time, via --llm-provider / Settings.llm_provider
and --db-url / Settings.db_url.
If you only need the ChatEngine as a library — e.g. from
django-db-chat-widget,
which brings its own web layer — pip install db-chat-widget (without
[server]) skips FastAPI/uvicorn/Jinja2 entirely.
Quickstart
export DB_CHAT_LLM_API_KEY=gsk_... # your Groq API key
db-chat-widget serve \
--db-url "postgresql://user:pass@localhost/mydb" \
--llm-provider groq \
--port 8000
(--llm-provider anthropic / openai work the same way, with the matching API key.)
Open http://127.0.0.1:8000 for a demo chat page, or embed the widget in your own site.
Popup mode (floating chat bubble)
Drops a chat bubble launcher into the bottom-right corner of the page — the most common way to bolt this onto an existing template with zero markup changes. Clicking it opens/closes the chat panel.
<link rel="stylesheet" href="http://127.0.0.1:8000/static/widget.css" />
<script
src="http://127.0.0.1:8000/static/widget.js"
data-mode="bubble"
data-api-url="http://127.0.0.1:8000/api/chat"
data-title="Ask about our data"
></script>
Use data-position="bottom-left" to anchor it to the bottom-left instead.
Inline mode
Embeds the chat panel directly into an element already on the page, instead of floating:
<link rel="stylesheet" href="http://127.0.0.1:8000/static/widget.css" />
<div id="my-db-chat"></div>
<script
src="http://127.0.0.1:8000/static/widget.js"
data-target="#my-db-chat"
data-api-url="http://127.0.0.1:8000/api/chat"
data-title="Ask about our data"
></script>
Programmatic API
Both modes are also available from JavaScript, e.g. after dynamically
injecting the script, or to control the popup (.open() / .close()):
<script src="http://127.0.0.1:8000/static/widget.js"></script>
<script>
const chat = DBChatWidget.init({
mode: "bubble", // or "inline" (requires `target`)
apiUrl: "http://127.0.0.1:8000/api/chat",
title: "Ask about our data",
position: "bottom-right", // or "bottom-left"
});
// chat.open(); chat.close(); chat.ask("How many orders today?");
</script>
See examples/embed_example.html and
examples/demo_app.py for a runnable, self-contained
example with a seeded SQLite database.
Using it as a library
from db_chat_widget import ChatEngine, Settings
settings = Settings(
db_url="sqlite:///mydb.sqlite",
llm_provider="groq",
llm_api_key="gsk_...",
read_only=True, # default: only SELECT statements are allowed
max_rows=200, # cap on rows returned per query
allowed_tables=None, # optionally restrict which tables can be queried
)
engine = ChatEngine(settings)
result = engine.ask("How many orders were placed last month?")
print(result.answer)
print(result.sql)
print(result.rows)
To mount just the FastAPI app (e.g. behind your own ASGI server or reverse proxy):
from db_chat_widget.config import Settings
from db_chat_widget.server.app import create_app
app = create_app(Settings(db_url="...", llm_provider="openai", llm_api_key="..."))
How it works
- The database schema (tables, columns, types, foreign keys) is introspected via SQLAlchemy and sent to the configured LLM as context.
- The LLM is asked to translate the user's question into a single SQL
SELECTstatement (or replyCANNOT_ANSWER). - The generated SQL is parsed with
sqlglotand rejected if it contains anything other than a single read-onlySELECT(noINSERT/UPDATE/DELETE/DROP/multiple statements/disallowed tables). - The query is executed with a row limit and the result, generated SQL, and a short natural-language summary are returned to the widget.
Default encoding
MySQL/MariaDB servers commonly default their client connection to latin1,
which mangles accented characters (e.g. French text) unless the client asks
for UTF-8 explicitly. When your db_url is a MySQL URL without a charset
query parameter, db-chat-widget automatically connects with
charset=utf8mb4:
mysql+pymysql://user:pass@localhost:3306/mydb
# connects as:
mysql+pymysql://user:pass@localhost:3306/mydb?charset=utf8mb4
If your URL already specifies a charset (e.g. ?charset=latin1), it is left
untouched. Other database backends (PostgreSQL, SQLite) already default to
UTF-8 and are not affected.
Safety notes
read_only=True(the default) is enforced at the SQL-parsing level, not just by prompting the model — treat it as the actual security boundary.- Use
allowed_tablesto scope the chatbot to specific tables (e.g. exclude ausers/secretstable containing sensitive columns). - The demo server enables permissive CORS (
*) for convenience; passcors_origins=[...](or--cors-originon the CLI) to restrict this in production. - Run the database user backing
db_urlwith least-privilege, read-only grants where possible, as defense in depth.
Development
pip install -e ".[dev]"
pytest
ruff check .
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file db_chat_widget-0.1.0.tar.gz.
File metadata
- Download URL: db_chat_widget-0.1.0.tar.gz
- Upload date:
- Size: 20.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7b3fe2d4fa7c3b455adb6769c1c9e3ed481aaa5af0b7b9cba48a2bf6a7fbf5f0
|
|
| MD5 |
8ab089dcfcb53e64802df581c791a84c
|
|
| BLAKE2b-256 |
224497eeeb78c60496d63f635e5cc61d39c5e5810d1e1b30c152317843463f4d
|
File details
Details for the file db_chat_widget-0.1.0-py3-none-any.whl.
File metadata
- Download URL: db_chat_widget-0.1.0-py3-none-any.whl
- Upload date:
- Size: 24.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb0f8361f231757151bbd1effb25c8c9bbe24b4b7edba7d9547156248ffd3520
|
|
| MD5 |
16183b23f0d8abee3ac87f4641f3b4e9
|
|
| BLAKE2b-256 |
88cbcebf3d62e9eddb554364e07778ea5c884d2aa19c18804e8777960e7e4b41
|