# AGENTS.md This file provides guidance to AI agents when working with code in this repository. ## Backward Compatibility (CRITICAL) 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. ## MCP-First Approach (CRITICAL) 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. ## Overview 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. ## Prerequisites - Python: **3.13** (3.10+ is the framework's minimum, but development targets 3.13) - Node.js: **24+** - [uv](https://docs.astral.sh/uv/) — Python package manager - [pnpm 9](https://pnpm.io/) — Node.js package manager (Corepack) ## Quick Start ### Install | | Command | Directory | | -------- | ---------------------- | ---------- | | Backend | `uv sync --all-extras` | `backend/` | | Frontend | `pnpm install` | repo root | ### Build | | 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) | ### Dev servers | | 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) | ### Tests | | 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 | ### Lint & Format | | 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`. ### Type checking | | 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. ### Commits This project uses [Conventional Commits](https://www.conventionalcommits.org/). Format: `(): `. 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: ``` Examples: - `Co-Authored-By: Claude Opus 4.6 ` - `Co-Authored-By: GitHub Copilot ` - `Co-Authored-By: Gemini CLI ` ## Tech Stack | 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 | ## Architecture ### Monorepo structure ``` 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. ### Backend (`backend/chainlit/`) **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 app - `socket.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 on `config.code.*` - `config.py` — Reads `.chainlit/config.toml` from `APP_ROOT`. `ChainlitConfig` holds both static TOML config and runtime user-registered callbacks. `APP_ROOT` defaults to `os.getcwd()`. - `session.py` — `WebsocketSession` (per-connection state: user, files, MCP connections, message queue) and `HTTPSession` - `context.py` — `ChainlitContext` per-coroutine context variable (similar to thread-local), providing access to the current session and emitter - `emitter.py` — Sends events back to the frontend through the SocketIO session - `data/base.py` — `BaseDataLayer` ABC for persistence (threads, steps, elements, users, feedback). Implementations: `sql_alchemy.py`, `dynamodb.py`, `literalai.py` - `auth/` — 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. ### Frontend (`frontend/src/`) React 18 + TypeScript + Vite, styled with Tailwind CSS and Radix UI primitives. - `main.tsx` — React root, wraps app in `RecoilRoot` and `ChainlitContext.Provider` - `App.tsx` — Handles auth readiness, chat profile selection, and WebSocket connection lifecycle - `router.tsx` — Client-side routes: `/` (Home), `/thread/:id`, `/element/:id`, `/login`, `/login/callback`, `/share/:id`, `/env` - `state/` — 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 bar - `components/LeftSidebar/` — Thread history sidebar ### `@chainlit/react-client` (`libs/react-client/src/`) Publishable npm package — the bridge between the React UI and the backend WebSocket. - `api.ts` — `ChainlitAPI` class: HTTP calls to backend REST endpoints - `useChatSession.ts` — Manages socket.io connection lifecycle - `useChatMessages.ts` — Exposes message tree state - `useChatData.ts` — Exposes elements, actions, tasklists, connection status - `useChatInteract.ts` — `sendMessage`, `replyMessage`, `callAction`, `stopTask`, `clear` - `state.ts` — Recoil atoms shared between the lib and consuming apps State is managed via Recoil; consuming apps must wrap the tree in `` and provide a `ChainlitAPI` instance via `ChainlitContext.Provider`. ### Communication flow 1. User sends a message → `useChatInteract.sendMessage` → emits `client_message` over SocketIO 2. Backend `socket.py` handler receives it → calls `config.code.on_message(message)` 3. User's app calls `cl.Message(...).send()` → `emitter.py` emits `new_message` back over SocketIO 4. Frontend `useChatMessages` updates Recoil state → component re-renders ### App configuration Apps configure Chainlit via `.chainlit/config.toml` (created automatically on first run). Key sections: `[project]` (auth, session timeouts, CORS), `[UI]` (name, theme, layout). --- ## Documentation Verification Requirements Before writing/modifying code, verify against official docs. **Lookup order**: Context7 MCP (preferred) → WebFetch → WebSearch. Pre-resolved Context7 library IDs: [docs/context7.md](docs/context7.md) Cross-reference API signatures and patterns during implementation. When uncertain, always check docs rather than relying on training data.