This file provides guidance to AI agents when working with code in this repository.
All changes MUST be backward-compatible. If a refactor or breaking change is unavoidable, notify the user and stop — do not proceed without explicit approval. When approved, prefer adding a compatibility layer over keeping legacy code in place.
When available, ALWAYS prefer MCP servers over manual alternatives. Use Context7 for docs/API references, Serena for code navigation/refactoring/memory, and GitHub MCP for issues/PRs/actions/commits/releases/code search. Fall back to CLI tools, direct file reads, or web searches ONLY IF the corresponding MCP is unavailable or cannot fulfill the request.
Chainlit is a Python framework for building production-ready conversational AI applications. It consists of a Python/FastAPI backend and a React frontend, with a pnpm monorepo for the JS packages.
- Python: 3.13 (3.10+ is the framework's minimum, but development targets 3.13)
- Node.js: 24+
- uv — Python package manager
- pnpm 9 — Node.js package manager (Corepack)
| Command | Directory | |
|---|---|---|
| Backend | uv sync --all-extras |
backend/ |
| Frontend | pnpm install |
repo root |
| Command | Directory | What it does | |
|---|---|---|---|
| All JS packages | pnpm build |
repo root | Build all workspace packages (frontend, react-client, copilot) via pnpm run --recursive |
| Backend (PyPI) | uv build |
backend/ |
Build Python package — builds JS assets first, then bundles into the Python distribution |
| Single JS package | pnpm --filter @chainlit/react-client build |
repo root | Build one package (useful for publishing) |
| Command | Directory | URL | |
|---|---|---|---|
| Backend | uv run chainlit run chainlit/sample/hello.py -h |
backend/ |
http://localhost:8000 |
| Frontend | pnpm run dev |
frontend/ |
http://localhost:5173 (proxies to :8000) |
| Command | Directory | |
|---|---|---|
| Backend (all) | uv run pytest --cov=chainlit/ |
backend/ |
| Backend (single file) | uv run pytest tests/test_file.py |
backend/ |
| Frontend unit | pnpm test |
frontend/ |
| E2E (Cypress) | pnpm test |
repo root |
| Command | Directory | |
|---|---|---|
| Lint JS/TS | pnpm lint |
repo root |
| Lint fix JS/TS | pnpm lint:fix |
repo root |
| Format check JS/TS | pnpm format-check |
repo root |
| Format fix JS/TS | pnpm format |
repo root |
| Lint Python | uv run scripts/lint.py |
repo root |
| Lint fix Python | uv run scripts/lint.py --fix |
repo root |
| Format check Python | uv run scripts/format.py --check |
repo root |
| Format fix Python | uv run scripts/format.py |
repo root |
JS/TS lint and format commands accept file/directory arguments: pnpm lint frontend/, pnpm format-check:files frontend/src/App.tsx. Python scripts also accept file arguments: uv run scripts/lint.py backend/chainlit/server.py.
| Command | Directory | |
|---|---|---|
| Python | uv run scripts/type_check.py |
repo root |
| TypeScript | pnpm type-check |
repo root |
Type checking runs on whole projects (no per-file mode).
Run pnpm lint:fix and pnpm format before committing — CI enforces checks on both.
This project uses Conventional Commits. Format: <type>(<optional scope>): <description>.
Common types: feat, fix, chore, docs, refactor, test, ci. Scope is optional but encouraged (e.g. fix(data): ..., feat(i18n): ...).
All commits made with AI assistance must include a Co-Authored-By trailer identifying the AI agent. Add it as the last line of the commit message body:
Co-Authored-By: <Agent Name> <agent-email-or-noreply>
Examples:
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>Co-Authored-By: GitHub Copilot <noreply@github.com>Co-Authored-By: Gemini CLI <noreply@google.com>
| Layer | Stack |
|---|---|
| Frontend | React 18, TypeScript 5.2, Vite 5, Tailwind CSS 3, Vitest, Zod 3 |
| Frontend (state & routing) | Recoil, React Router 6, react-hook-form, socket.io-client, SWR |
| Frontend (rendering) | react-markdown + remark-gfm/math + rehype-katex/raw, highlight.js, lucide-react (icons), Radix UI (primitives), Plotly.js |
| Backend | Python 3.13, FastAPI, Starlette, Uvicorn, python-socketio, Pydantic 2, PyJWT, httpx |
| LLM integrations | MCP, LangChain, LlamaIndex, OpenAI SDK, Semantic Kernel, MistralAI |
| Infra / persistence | SQLAlchemy (PostgreSQL/SQLite), DynamoDB + S3 (boto3), Azure Blob / Data Lake, Google Cloud Storage, LiteralAI |
| DX | pre-commit hooks, linting, formatting, type checking, unit testing, E2E testing |
backend/ # Python package (published to PyPI as "chainlit")
frontend/ # React app (built output served by backend)
libs/
react-client/ # @chainlit/react-client — published npm package with React hooks
copilot/ # Copilot widget (embedded chat bubble)
cypress/ # E2E tests
The pnpm workspace includes frontend/, libs/react-client/, and libs/copilot/. The built frontend assets are copied into backend/chainlit/frontend/dist/ and served as static files.
Entry point for user apps: __init__.py re-exports all public API decorators and classes.
Key files:
server.py— FastAPI app, all REST routes (auth, elements, threads, file upload), serves the built frontend SPA, mounts the SocketIO appsocket.py— SocketIO event handlers for real-time WebSocket communication (connect, message, audio, etc.)callbacks.py— Decorator functions registered via@cl.on_message,@cl.on_chat_start,@cl.on_audio_chunk, etc. These store functions onconfig.code.*config.py— Reads.chainlit/config.tomlfromAPP_ROOT.ChainlitConfigholds both static TOML config and runtime user-registered callbacks.APP_ROOTdefaults toos.getcwd().session.py—WebsocketSession(per-connection state: user, files, MCP connections, message queue) andHTTPSessioncontext.py—ChainlitContextper-coroutine context variable (similar to thread-local), providing access to the current session and emitteremitter.py— Sends events back to the frontend through the SocketIO sessiondata/base.py—BaseDataLayerABC for persistence (threads, steps, elements, users, feedback). Implementations:sql_alchemy.py,dynamodb.py,literalai.pyauth/— JWT creation/validation (jwt.py), OAuth state cookies (cookie.py)types.py— Shared Pydantic models for API request/response types
Data layer pattern: The data layer is optional (no persistence by default). Register a custom implementation with @cl.data_layer decorator or use the built-in SQLAlchemy/DynamoDB/LiteralAI implementations. The @queue_until_user_message() decorator on BaseDataLayer methods queues write operations until the first user message arrives.
Integrations: langchain/, llama_index/, openai/, semantic_kernel/, mistralai/ — each provides callback handlers that bridge those frameworks into Chainlit steps/messages.
React 18 + TypeScript + Vite, styled with Tailwind CSS and Radix UI primitives.
main.tsx— React root, wraps app inRecoilRootandChainlitContext.ProviderApp.tsx— Handles auth readiness, chat profile selection, and WebSocket connection lifecyclerouter.tsx— Client-side routes:/(Home),/thread/:id,/element/:id,/login,/login/callback,/share/:id,/envstate/— Recoil atoms:chat.ts(messages, elements, tasks),project.ts(config, session),user.ts(env vars)components/chat/— Core chat UI (message list, input bar, elements, audio)components/header/— Top navigation barcomponents/LeftSidebar/— Thread history sidebar
Publishable npm package — the bridge between the React UI and the backend WebSocket.
api.ts—ChainlitAPIclass: HTTP calls to backend REST endpointsuseChatSession.ts— Manages socket.io connection lifecycleuseChatMessages.ts— Exposes message tree stateuseChatData.ts— Exposes elements, actions, tasklists, connection statususeChatInteract.ts—sendMessage,replyMessage,callAction,stopTask,clearstate.ts— Recoil atoms shared between the lib and consuming apps
State is managed via Recoil; consuming apps must wrap the tree in <RecoilRoot> and provide a ChainlitAPI instance via ChainlitContext.Provider.
- User sends a message →
useChatInteract.sendMessage→ emitsclient_messageover SocketIO - Backend
socket.pyhandler receives it → callsconfig.code.on_message(message) - User's app calls
cl.Message(...).send()→emitter.pyemitsnew_messageback over SocketIO - Frontend
useChatMessagesupdates Recoil state → component re-renders
Apps configure Chainlit via .chainlit/config.toml (created automatically on first run). Key sections: [project] (auth, session timeouts, CORS), [UI] (name, theme, layout).
Before writing/modifying code, verify against official docs.
Lookup order: Context7 MCP (preferred) → WebFetch → WebSearch.
Pre-resolved Context7 library IDs: docs/context7.md
Cross-reference API signatures and patterns during implementation. When uncertain, always check docs rather than relying on training data.