A reusable AI chatbot platform consisting of an embeddable React widget and a self-hosted FastAPI backend.
Quantheonix is designed for developers who want to add an AI chatbot to a React application without exposing AI provider credentials, database credentials, or authentication secrets in the browser.
The project provides:
- an embeddable React chatbot widget
- streaming AI responses
- Markdown and syntax-highlighted code rendering
- JWT authentication support
- access-token refresh integration
- conversation persistence
- PostgreSQL storage
- Gemini AI integration
- rate limiting
- Docker-based self-hosting
- automatic database migrations
Current release:
@quantheonix/chatbot v1.0.0
The frontend widget is packaged separately from the backend.
React Application
|
| @quantheonix/chatbot
|
v
Quantheonix Backend
|
+----------------------+
| |
v v
PostgreSQL Gemini API
The browser never communicates directly with Gemini.
- Reusable React component
- Streaming AI responses
- NDJSON stream processing
- Markdown rendering
- GitHub-flavored Markdown
- Syntax-highlighted code blocks
- Configurable title
- Configurable welcome message
- Configurable input placeholder
- Bottom-right positioning
- Bottom-left positioning
- Responsive mobile layout
- Stop generation
- New chat
- Conversation ID management
- JWT access-token support
- Token refresh integration
- Automatic retry after HTTP
401 - Error handling
- FastAPI REST API
- Async PostgreSQL access
- SQLAlchemy
- Alembic database migrations
- Gemini AI integration
- JWT authentication
- Access and refresh tokens
- Conversation ownership
- Persistent conversations
- Persistent messages
- Streaming responses
- Request rate limiting
- CORS configuration
- Health monitoring
- Docker deployment
quantheonix-ai-chatbot/
│
├── backend/
│ ├── alembic/
│ ├── app/
│ ├── scripts/
│ ├── tests/
│ ├── alembic.ini
│ ├── Dockerfile
│ ├── main.py
│ ├── pytest.ini
│ └── requirements.txt
│
├── frontend/
│
├── packages/
│ └── quantheonix-chatbot/
│ ├── dist/
│ ├── README.md
│ └── package.json
│
├── widget-demo/
│
├── docs/
│ ├── self-hosted-setup.md
│ └── troubleshooting.md
│
├── .env.selfhosted.example
├── compose.selfhosted.yaml
├── compose.yaml
└── README.md
There are two main parts to Quantheonix:
- Quantheonix backend
-
@quantheonix/chatbotReact widget
For a complete installation, start the backend first and then connect the React widget to it.
Clone the repository:
git clone https://github.com/madhuka2002/quantheonix-ai-chatbot.git
cd quantheonix-ai-chatbotCreate the self-hosted environment file:
cp .env.selfhosted.example .env.selfhostedOpen .env.selfhosted and configure the required values.
At minimum, configure:
POSTGRES_DB=quantheonix_chatbot
POSTGRES_USER=quantheonix_user
POSTGRES_PASSWORD=replace_with_a_strong_database_password
GEMINI_API_KEY=replace_with_your_gemini_api_key
JWT_SECRET_KEY=replace_with_a_long_random_secret
CORS_ORIGINS=["http://localhost:5173"]Generate a strong JWT secret using Python:
python -c "import secrets; print(secrets.token_urlsafe(64))"Do not commit .env.selfhosted.
Make sure Docker is running.
Then execute:
docker compose \
--env-file .env.selfhosted \
-f compose.selfhosted.yaml \
up -d --buildDocker will:
- start PostgreSQL
- wait for PostgreSQL to become healthy
- run Alembic database migrations
- start the FastAPI backend
- expose the API on port
8000
Check the containers:
docker compose \
--env-file .env.selfhosted \
-f compose.selfhosted.yaml \
psThe PostgreSQL and backend containers should report healthy states.
Check the API health endpoint:
curl http://localhost:8000/api/v1/healthA healthy installation should return a response similar to:
{
"status": "healthy",
"service": "Quantheonix AI Chatbot API",
"version": "1.0.0",
"database": "connected"
}You can also inspect backend logs:
docker compose \
--env-file .env.selfhosted \
-f compose.selfhosted.yaml \
logs backendOn a fresh database, Alembic migrations are applied automatically before the API starts.
If a migration fails, backend startup is stopped instead of continuing with an invalid database state.
For detailed instructions, see:
docs/self-hosted-setup.md
In your React application:
npm install @quantheonix/chatbotImport the component and stylesheet:
import {
QuantheonixChat,
} from "@quantheonix/chatbot";
import "@quantheonix/chatbot/chatbot.css";Use it in your application:
function App() {
return (
<QuantheonixChat
apiUrl="http://localhost:8000"
/>
);
}
export default App;The chatbot now communicates with your self-hosted Quantheonix backend.
For Vite applications, storing the backend URL in an environment variable is recommended.
Create:
.env
and add:
VITE_CHATBOT_API_URL=http://localhost:8000Then:
import {
QuantheonixChat,
} from "@quantheonix/chatbot";
import "@quantheonix/chatbot/chatbot.css";
function App() {
return (
<QuantheonixChat
apiUrl={
import.meta.env.VITE_CHATBOT_API_URL
}
/>
);
}
export default App;For production:
VITE_CHATBOT_API_URL=https://api.example.comThe backend URL is public configuration and is not a secret.
The main component is:
<QuantheonixChat />Supported properties include:
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
apiUrl |
string |
Yes | — | Quantheonix backend URL |
accessToken |
string | null |
No | null |
JWT access token |
getAccessToken |
function | null |
No | null |
Callback for obtaining or refreshing an access token |
title |
string |
No | "Quantheonix AI" |
Widget title |
welcomeMessage |
string |
No | "Hello! How can I help you?" |
Initial assistant message |
placeholder |
string |
No | "Type your message..." |
Message input placeholder |
initiallyOpen |
boolean |
No | false |
Start widget open |
position |
string |
No | "bottom-right" |
Widget placement |
Example:
<QuantheonixChat
apiUrl="http://localhost:8000"
title="Support Assistant"
welcomeMessage="Hello! How can we help?"
placeholder="Ask a question..."
initiallyOpen={false}
position="bottom-right"
/>The widget supports JWT authentication.
If your application already has an access token:
function App() {
const accessToken =
localStorage.getItem("access_token");
return (
<QuantheonixChat
apiUrl="http://localhost:8000"
accessToken={accessToken}
/>
);
}Requests include:
Authorization: Bearer ACCESS_TOKENApplications that support refresh tokens should use getAccessToken.
Example:
async function getAccessToken({
forceRefresh = false,
} = {}) {
if (!forceRefresh) {
return localStorage.getItem(
"access_token",
);
}
const response = await fetch(
"http://localhost:8000/api/v1/auth/refresh",
{
method: "POST",
credentials: "include",
},
);
if (!response.ok) {
return null;
}
const data = await response.json();
localStorage.setItem(
"access_token",
data.access_token,
);
return data.access_token;
}Then:
<QuantheonixChat
apiUrl="http://localhost:8000"
getAccessToken={getAccessToken}
/>When the backend returns 401, the widget can request a refreshed token and retry the chat request.
Chat responses are streamed from:
/api/v1/chat/stream
using:
application/x-ndjson
Example events:
{
"type": "start",
"conversation_id": "..."
}{
"type": "chunk",
"text": "Hello"
}{
"type": "done",
"conversation_id": "..."
}The widget progressively renders the assistant response as chunks arrive.
The backend creates and persists conversations.
Typical flow:
First message
|
v
Backend creates conversation
|
v
conversation_id returned
|
v
Widget stores conversation_id
|
v
Future messages continue conversation
Selecting New chat clears the current client-side conversation state and starts a new conversation on the next request.
Quantheonix currently integrates with Gemini through the backend.
Example configuration:
GEMINI_MODEL=gemini-flash-latest
GEMINI_TEMPERATURE=0.7The Gemini API key must remain on the backend.
Never place:
GEMINI_API_KEY=...inside a frontend application.
Quantheonix uses PostgreSQL.
The self-hosted deployment supports configurable:
POSTGRES_DB=quantheonix_chatbot
POSTGRES_USER=quantheonix_user
POSTGRES_PASSWORD=...The backend constructs its database connection using the self-hosted PostgreSQL service.
Database schema changes are managed with Alembic.
The Docker self-hosted deployment automatically executes:
alembic upgrade headbefore starting FastAPI.
The startup sequence is:
PostgreSQL starts
|
v
PostgreSQL becomes healthy
|
v
Alembic migrations run
|
+---- failure ----> backend stops
|
v
Uvicorn starts
|
v
API becomes healthy
You normally do not need to run Alembic manually when using the self-hosted Docker configuration.
The backend only accepts browser requests from configured origins.
Example:
CORS_ORIGINS=["http://localhost:5173"]Multiple origins:
CORS_ORIGINS=["http://localhost:5173","http://127.0.0.1:5173","https://example.com"]For production, add your real frontend domain.
Do not use Markdown-style links inside this value.
Correct:
CORS_ORIGINS=["https://example.com"]Incorrect:
["[https://example.com](https://example.com)"]
The backend supports configurable request rate limits.
Self-hosted environment variables include settings for:
- chat requests
- login requests
- registration requests
- refresh-token requests
- rate-limit window duration
Rate limiting should remain enabled in production.
Keep all sensitive credentials on the backend.
Never expose the following in frontend code:
GEMINI_API_KEY
JWT_SECRET_KEY
POSTGRES_PASSWORD
DATABASE_URL
Recommended practices:
- use strong unique database passwords
- generate a long random JWT secret
- use HTTPS in production
- restrict CORS to trusted domains
- keep
.envfiles outside Git - rotate credentials if they are accidentally exposed
- keep Docker images and dependencies updated
- keep rate limiting enabled
- use separate production credentials
The repository may contain example environment files such as:
.env.selfhosted.example
backend/.env.example
backend/.env.docker.example
These files contain placeholders only.
Actual environment files should not be committed.
Examples:
.env.selfhosted
backend/.env
backend/.env.docker
frontend/.env
Stop containers:
docker compose \
--env-file .env.selfhosted \
-f compose.selfhosted.yaml \
downThis keeps the PostgreSQL volume.
Warning: this permanently deletes the PostgreSQL data stored in the Docker volume.
docker compose \
--env-file .env.selfhosted \
-f compose.selfhosted.yaml \
down -vThen restart:
docker compose \
--env-file .env.selfhosted \
-f compose.selfhosted.yaml \
up -d --buildThis creates a fresh PostgreSQL database and reruns all migrations.
Pull the latest code:
git pullThen rebuild:
docker compose \
--env-file .env.selfhosted \
-f compose.selfhosted.yaml \
up -d --buildAlembic applies any new database migrations during backend startup.
Backend:
docker compose \
--env-file .env.selfhosted \
-f compose.selfhosted.yaml \
logs backendPostgreSQL:
docker compose \
--env-file .env.selfhosted \
-f compose.selfhosted.yaml \
logs postgresFollow backend logs:
docker compose \
--env-file .env.selfhosted \
-f compose.selfhosted.yaml \
logs -f backendFrom the backend development environment:
pytest -vCurrent backend tests cover areas including:
- rate limiting
- access-token handling
- refresh-token handling
- token-type validation
From:
packages/quantheonix-chatbot
run:
npm install
npm run lint
npm run buildTest package contents:
npm pack --dry-runIf something fails, see:
docs/troubleshooting.md
Common problems include:
- Docker not running
- incorrect PostgreSQL credentials
- an old PostgreSQL Docker volume
- database migration failure
- CORS errors
- expired access tokens
- Gemini API errors
- Gemini quota limits
- incorrect backend URL
- port conflicts
Detailed documentation:
docs/self-hosted-setup.md
docs/troubleshooting.md
packages/quantheonix-chatbot/README.md
- React
- Vite
- react-markdown
- remark-gfm
- rehype-highlight
- highlight.js
- Python
- FastAPI
- Uvicorn
- SQLAlchemy
- asyncpg
- Alembic
- PostgreSQL
- Gemini API
- JWT authentication
- Docker
- Docker Compose
- PostgreSQL 17 Alpine
This project is licensed under the MIT License.
Developed as part of the Quantheonix project ecosystem.
GitHub:
https://github.com/madhuka2002/quantheonix-ai-chatbot
npm package:
@quantheonix/chatbot