English | 简体中文
Basjoo is an AI customer-support platform with three main parts:
- a FastAPI backend for agent configuration, chat, indexing, auth, and scheduling
- a Next.js admin/dashboard frontend in
frontend-nextjs/ - an embeddable chat widget in
widget/that talks to the backend over HTTP and SSE
The stack also uses SQLite for application data, Redis for rate limiting/cache-related services, Qdrant for vector search, and nginx for Docker-based reverse proxying.
For a blank Ubuntu or Debian server, run:
curl -fsSL https://raw.githubusercontent.com/haoyiyin/basjoo/main/install-deploy.sh | sudo shIf you already have this repository checked out locally, you can also run:
sudo sh install-deploy.shbackend/— FastAPI app, data models, chat APIs, auth, ingestion, indexing, testsfrontend-nextjs/— active admin/dashboard UIwidget/— embeddable chat widget bundlenginx/— Docker nginx configdocker-compose.yml— dev/prod orchestrationfrontend/— legacy frontend reference; the active frontend isfrontend-nextjs/
- Configurable AI agents with multiple provider settings
- Independent Embedding API selection for knowledge retrieval: Jina or SiliconFlow
- URL ingestion and Q&A knowledge management
- Qdrant-backed retrieval and index rebuild jobs
- Streaming chat responses over Server-Sent Events
- Embeddable website widget with session persistence
- Widget copy auto-translation by visitor locale
- Per-agent widget domain whitelist for public chat embeds
- Offline agent fallback replies and admin-side error alerts
- Admin authentication and dashboard management flows
- Dockerized development and production-style deployment paths
The admin dashboard is the operational center for configuring agents, reviewing knowledge coverage, and accessing the major management modules.
The Playground lets admins test replies, inspect retrieval behavior, and adjust model/provider settings from the same workflow.
The Websites page handles URL ingestion, crawling, auto-fetch settings, and retraining/index-refresh workflows for web content.
The Q&A page is used to create, batch import, edit, and rebuild structured question/answer knowledge.
The Sessions page shows live conversations, supports human takeover, and gives operators a single place to monitor visitor activity.
System Settings covers language/theme preferences, widget appearance, embed behavior, and other operational controls.
The widget provides the visitor-facing chat window with persisted sessions, multilingual copy, streaming responses, and knowledge-assisted replies.
- FastAPI
- SQLAlchemy async + SQLite
- Redis
- Qdrant
- APScheduler
- Provider SDKs for OpenAI-compatible APIs, Anthropic, and Google Gemini
- Next.js 14
- React 18
- TypeScript
- i18next
- TypeScript
- esbuild
- Browser-native fetch + SSE handling
Development stack:
docker compose --profile dev up -dProduction-style stack:
docker compose --profile prod up -dUseful Docker commands:
docker compose logs -f backend-dev frontend-dev nginx
docker compose --profile dev up -d --build backend-dev frontend-dev
bash scripts/prod_stability_check.shDefault dev ports:
- Frontend:
http://localhost:3000 - Backend API:
http://localhost:8000 - Qdrant:
http://localhost:6333 - Redis:
127.0.0.1:6379
The dev frontend and backend ports are bound as 3000:3000 and 8000:8000, so they are reachable from other devices that can access the host.
cd backend
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python3 main.pyBackend health check:
curl http://localhost:8000/healthcd frontend-nextjs
npm install
npm run devcd widget
npm install
npm run devnpm install
npm run dev
npm run build
npm run start # production build locally
npm run lint
npm run typecheck
npm run test # vitestnpm install
npm run dev # dev bundle + example server
npm run build # full build (typecheck + dev + prod bundles)
npm run build:dev # unminified ESM bundle (dist/basjoo-widget.js)
npm run build:prod # minified IIFE bundle (dist/basjoo-widget.min.js)
npm run typecheck
npm run test # vitestpip install -r requirements.txt
python3 main.py
pytest
pytest tests/test_api.py
pytest tests/test_api.py::test_namenpm run test:e2e # smoke tests (dev environment)
npm run test:e2e:all # all Playwright test projects
npm run test:e2e:prod # production-like E2E tests
npm run test:e2e:widget # widget cross-origin embed tests
npm run sync-widget # sync widget bundle to backenddocker compose --profile dev up --watchThe backend reads settings from environment variables and .env via pydantic-settings.
Important runtime settings used in the current codebase include:
DATABASE_URLREDIS_URLQDRANT_HOSTQDRANT_PORTSECRET_KEYSECRET_KEY_FILEDEFAULT_AGENT_IDJINA_API_KEYDEEPSEEK_API_KEYALLOWED_ORIGINSALLOWED_METHODSALLOWED_HEADERSRATE_LIMIT_PER_MINUTERATE_LIMIT_BURST_SIZELOG_LEVELSERVER_DOMAINENCRYPTION_KEY(optional; auto-generated and persisted if missing)ENCRYPTION_KEY_FILE(default/app/data/.encryption_key)REQUIRE_SECRET_KEY(settruein production to reject insecure secret keys)
Notes:
- If
SECRET_KEYis missing or insecure, the backend generates one and persists it toSECRET_KEY_FILE. DEFAULT_AGENT_IDcan be used to restore or pin a known widget agent ID during migrations; see the deployment section below for the preservation workflow.- If
ENCRYPTION_KEYis not set, the backend auto-generates a Fernet key and persists it toENCRYPTION_KEY_FILE; stored provider API keys are encrypted with this key. cors_allow_null_origin(boolean, defaultfalse) controls whetherOrigin: null(e.g.,file://widget preview) receives wildcard CORS headers. Off by default for security.SERVER_DOMAINis consumed by the nginx service in the production compose profile to enforce a canonical host and block direct IP/other-host access.- The dev compose profile sets permissive CORS and local API URLs by default.
- The production compose profile expects mounted persistent backend data under
/app/data.
backend/main.py builds the FastAPI app and wires together:
- auth routes under
/api/admin - v1 APIs under
/api/v1(chat, agent config, sessions, quotas, task status) - admin-only routers:
url_endpoints.py(URL ingestion, Q&A management, crawling) andindex_endpoints.py(index rebuild jobs) are protected at the router level viaDepends(get_current_admin) - public v1 routes:
/api/v1/chat,/api/v1/chat/stream,/api/v1/contexts,/api/v1/config:public - CORS middleware with a shared
apply_cors_headers()helper for early responses (rate limit 429, body size 413) - i18n middleware
- rate limiting middleware
- Redis and scheduler startup in non-test mode
- static routes for widget assets like
/sdk.js - a 10MB request body guard that returns JSON 413 errors before the request reaches downstream handlers
The main backend domains are:
- Agent config: provider/model/system-prompt/widget settings
- Knowledge sources: URLs and Q&A items, with SSRF protection via
backend/services/url_safety.py - Indexing: chunking content and rebuilding Qdrant collections; rebuilds replace the entire collection (clear + add)
- Chat: session creation, streaming replies, source citations, quota checks
- Admin auth: dashboard login and registration
- Scheduling: URL fetch scheduler, history cleanup, session auto-close (30-min inactivity timeout)
The main persistent entities in backend/models.py are:
WorkspaceAgentURLSourceQAItemDocumentChunkChatSessionChatMessageWorkspaceQuotaIndexJobAdminUser
The retrieval/indexing pipeline spans:
backend/api/v1/url_endpoints.pybackend/api/v1/index_endpoints.pybackend/services/qdrant_store.pybackend/services/rag_qdrant.pybackend/services/scraper.pybackend/services/crawler.py
The LLM abstraction is in backend/services/llm_service.py. Provider selection is driven by Agent.provider_type. The current code supports OpenAI-compatible providers plus dedicated paths for OpenAI Native and Google.
Embedding settings are independent from the chat model provider. Admins can choose Jina or SiliconFlow for knowledge-base indexing/retrieval in Playground; the Websites and Q&A pages only require the API key for the currently selected embedding provider. SiliconFlow can use a dedicated SiliconFlow Embedding API key, with legacy fallback to the main SiliconFlow AI key when the AI provider is also SiliconFlow.
The active UI is the Next.js app in frontend-nextjs/.
- App Router routes live under
frontend-nextjs/app/ - most screen logic lives in
frontend-nextjs/src/views/ - shared components live in
frontend-nextjs/src/components/ - admin auth state is stored in
frontend-nextjs/src/context/AuthContext.tsx - API calls and SSE parsing are centralized in
frontend-nextjs/src/services/api.ts
widget/src/BasjooWidget.tsx is a self-contained embeddable widget that:
- auto-detects
apiBasefrom the script source URL, infers from dev port 3000 → backend 8000, or falls back towindow.location.origin - fetches
/api/v1/config:publicon init to resolvedefault_agent_id, widget title/color, and welcome message - stores visitor/session IDs in
localStorage - streams chat replies from
/api/v1/chat/streamvia SSE with a 90-second read timeout and one automatic retry on network errors - polls
/api/v1/chat/messages?role=assistantat 3-second intervals during human takeover scenarios - relies on server-side widget origin whitelist checks when configured
The backend serves widget-related assets directly, including /sdk.js.
- SSRF protection:
backend/services/url_safety.pyvalidates all user-provided URLs. It blockslocalhost, direct IP literals, URLs with embedded credentials, and hostnames that resolve to private/special-use IPs (loopback, RFC1918, link-local, cloud metadata). DNS resolution results are cached (512-entry LRU) to avoid repeated lookups during crawls. - Widget origin whitelist: Public chat routes enforce a per-agent origin whitelist configured in the admin dashboard. Admin users bypass the whitelist for testing.
- CORS policy: Early responses (rate limit 429, body size 413) apply CORS headers through a shared helper in
backend/middleware/rate_limit.py.Origin: nullonly receives wildcard CORS whencors_allow_null_originis explicitly enabled. Requests without anOriginheader do not receive CORS headers. - Secret persistence:
SECRET_KEY,DEFAULT_AGENT_ID, andENCRYPTION_KEYare auto-generated and persisted on first boot if not provided via environment variables, ensuring stable widget embed behavior and encrypted API key storage across redeployments. - Task concurrency: A shared
TaskLockservice prevents conflicting operations (e.g., rebuild blocks fetch, fetch blocks rebuild) on the same agent.
Backend tests are under backend/tests/.
Key testing behavior from backend/tests/conftest.py:
- sets
BASJOO_TEST_MODE=1 - uses isolated SQLite databases under
backend/.pytest_dbs/ - monkeypatches Qdrant/Jina/LLM integrations for many tests
- falls back between Docker hostnames and localhost for Redis/Qdrant where needed
Run all tests:
cd backend
pytestRun a file:
pytest tests/test_api.pyRun a single test:
pytest tests/test_api.py::test_namedocker-compose.ymlis the main orchestration entrypoint.install-deploy.shis the one-command production installer/deployer for Ubuntu and Debian. It can auto-install Docker/Compose, clone the repo, and force-sync an existing clone to the chosen remote branch before deploying.- The active frontend service is
frontend-nextjs, not the legacyfrontend/directory. - nginx is configured with
client_max_body_size 12mso oversized requests can reach the backend and return JSON errors instead of nginx HTML errors. - Optional HTTPS is enabled only when readable certificate and key files exist in
./ssl. - When certificates are present, nginx serves HTTPS on port 443 and redirects HTTP requests on port 80 to HTTPS automatically.
SERVER_DOMAINcan be set for the nginx service to enforce a canonical hostname. When set, nginx serves only that host, rejects direct IP or unexpected Host access with nginx 444, and keeps/healthavailable for load balancer probes.- If
SERVER_DOMAINis not set, nginx keeps accepting requests by the incoming host as before. - Backend responses that bypass standard middleware should still apply CORS headers so embedded widget requests do not fail cross-origin.
- The backend persists the default widget agent ID to
/app/data/.agent_id. As long as the backend data volume is preserved, existing widget embed codes keep working after redeployments. - If you know an older widget agent ID that must keep working, set
DEFAULT_AGENT_ID=agt_xxxxxxxxxxxxbefore first boot of the new deployment. - Avoid
docker compose down -vor deleting the backend data volume unless you are intentionally rotating widget/embed identity. - The one-command installer only force-resets repository files; it does not delete Docker named volumes, so
/app/datapersistence remains intact across redeployments.
Recommended production workflow:
- Preserve the backend data volume mounted at
/app/data. - Redeploy with
docker compose --profile prod up -d --build. - If you are migrating to a new server and know the old widget
agentId, setDEFAULT_AGENT_IDbefore starting the backend. - Back up at least
/app/data/basjoo.dband/app/data/.agent_id.
Example .env snippet for migration:
SECRET_KEY=
DEFAULT_AGENT_ID=agt_123456789abcIf the old data volume is lost and the old agentId is unknown, old widget embeds cannot be recovered automatically because the embed code references the previous agent ID directly.
Examples of backend endpoints present in the codebase:
/health/api/admin/login/api/admin/register/api/v1/chat/api/v1/chat/stream/api/v1/agent:default/api/v1/urls:create/api/v1/urls:list/api/v1/urls:refetch/api/v1/index:rebuild/api/v1/index:status
This README reflects the repository as it exists now. If you change deployment flows, provider support, or package scripts, update this file alongside the code.