* ✋ feat: Add Reclaim-Gated Controls to In-Flight Steers Give a pending steer the same controls as a queued message — edit, convert to queue, and the during-run mode toggle — instead of only a bare cancel. Every re-homing action reclaims the steer from the server queue first and acts only on a confirmed `removed: true`. A steer leaves that queue only by injecting, so a lost race means the words are already in the run: queueing or editing them then would say the same thing twice. - Return a `SteerCancelOutcome` ('reclaimed' | 'applied' | 'failed') from useSteerCancel so callers can distinguish "the words are still mine" from "already injected" and "unknown fate" — the last two only toast. - Extract RowMenu, useDefaultToggleEntry, and the shared button classes into SteerMenu so both during-run surfaces use one implementation. - Offer controls only once `pending`: a `sending` steer has no server id to reclaim with, so its words cannot be held back. - Pin the control cluster visible while its menu is open — the portaled items hold focus outside the subtree, so `focus-within` alone would drop it. * 🩹 fix: Address Codex Findings on Reclaimed-Steer Controls Route a reclaimed steer through the shared conversion, and stop the async reclaim from stranding items or clobbering a composer that moved on. - Queue a reclaimed steer via useSteerConvert instead of enqueue, so it keeps its original id and createdAt. enqueue minted a fresh v4/Date.now() and appended, so a steer accepted BEFORE a later follow-up drained after it — breaking the invariant the leftover-steer path documents. - Submit the item directly when the run ended during the reclaim round-trip: the drain consumes its one-shot signal against an empty queue, so nothing was left to auto-send it. Read run state and conversation from refs, since the reclaim resolves after the bubble unmounts. - Refuse the composer restore when the origin conversation no longer matches or a newer draft is present, and queue the words instead of overwriting them. Neither text is the one to throw away. - Split useSteerReclaim (POST only) out of useSteerCancel, so the menu actions leave the chip alone until the outcome is known while the X stays optimistic. * 🛡️ fix: Harden Reclaimed-Steer Guards Against Stale State Both guards from the previous round read values that had moved on by the time the reclaim resolved. - Compare the origin conversation against a ref, not the closure. The `.then` holds `restoreReclaimedSteer` from the render it was clicked in, so its captured `conversationId` is the OLD chat — the guard compared that against itself and passed, while `methods` (one form, reused across conversations) wrote the steer into the chat now on screen. - Gate the direct send on the drain's own rule. `!isSubmitting` also covers a Stop or an error, so converting and then pressing Stop auto-sent the text past useQueueDrain's completed-or-armed-interrupt-only rule. Capture the run's outcome before the drain consumes the one-shot signal, and send only on a clean completion of THIS conversation. * ♻️ refactor: Re-Arm the Drain Instead of Direct-Sending Reclaimed Steers The direct send was the wrong mechanism: it re-implemented the drain badly, and each round of review found another rule it had skipped. Delete it and let useQueueDrain do the sending — it already owns every one of those rules. - Re-post the spent run-end signal under the conversation instead of calling sendNow. The drain then applies the completed-only rule, FIFO order (an older follow-up is no longer skipped), NEW_CONVO migration, and submits via `ask` — which, unlike the composer's sendNow, does not reset the form and so cannot wipe a draft typed while the reclaim was in flight. - No-op when a signal is still armed: that drain has not run yet and will see the item on its own, so arming a second carrier would send twice. - Watch the parked run-end too, not just the index one, so a run that ended while the user was in another chat is still seen. - Treat staged files, quotes, and skill picks as a draft when deciding whether a restore may overwrite the composer — editToComposer MERGES into them, so restoring over staged context would glue two submissions together. * 🎯 fix: Scope the Re-Arm Suppression to This Conversation The no-double-arm guard treated ANY armed index run-end as proof the drain would see this conversation's newly queued item. The index slot is shared: useQueueDrain parks a foreign signal under its own conversation and then inspects only the active one's queue, so a reclaimed steer sitting behind an unrelated run-end would never be looked at and would strand until sent by hand. Suppress only when the armed index signal belongs to THIS conversation — which is the case where the drain really will see the item. The parked check was already conversation-scoped by its key. * 🧭 fix: Trust the Refs Only While They Describe This Chat useSteering is reused across conversations, so after a navigation its live refs describe the NEW chat while the reclaim's callback still speaks for the old one. Restoring the conversation-identity guard I removed last round, which was wrong precisely because the refs are live but not conversation-scoped. - Skip the re-arm entirely once conversationIdRef no longer matches the steer's conversation. Reading isSubmittingRef there could suppress a needed re-arm, and lastRunEndRef could hold the NEW chat's run-end — parking that under the old conversation would make drainNext (which keys off end.conversationId) drain the wrong queue into the wrong chat. - Assert lastRunEnd.conversationId matches before re-arming, so the invariant is enforced where it is relied on rather than inferred from render order. Nothing is lost by stopping: the item is already queued under its own conversation, and that run's end parks under it and drains on return. * 🗝️ fix: Key the Captured Run-End by Conversation A single run-end slot could only answer for whichever chat was on screen when a reclaim landed, so the guard had to bail on navigation — stranding a steer whose run had already completed, contrary to "queue for after the response". Key the captured run-ends by conversation instead. The stored end always speaks for the chat the words belong to, so navigating away no longer suppresses the re-arm, and another chat's end can never be parked under this one (which would hand drainNext a foreign end.conversationId and drain the wrong queue). - Drop an entry when its conversation starts another run: a superseded end must not authorize a drain of the run now in flight. This replaces the isSubmitting guard, which described the wrong chat after navigation. - Remove conversationIdRef, now that no read depends on where the user is. * 🧹 fix: Close Three Reclaim Races Around Answer Mode and Run End - Refuse the composer restore while answer mode is active. `onSubmit` hands composer text to `answerMode.submitText` before any send/steer routing, so a restored steer would become the tool's answer on the next Enter. Read through a ref: the run can pause on ask_user_question mid-reclaim. - Skip the restore when a terminal conversion already queued the words. The chip stays interactive during the reclaim round-trip, so a run ending or erroring meanwhile converts it — restoring after would leave one copy queued and another in the draft. The queue action needed no guard; the conversion already dedupes by id. - Carry quotes/skill picks on the reclaimed steer itself. The conversion recovers them from the chip, which a competing X can delete mid-round-trip, silently dropping the picks. * 🎛️ feat: Fold Cancel Into an Always-Visible Steer Menu Make the in-flight steer bubble a single, discoverable affordance instead of two hover-hidden ones, matching how Codex/ChatGPT present the same control. - Fold Cancel into the ⋯ menu as an item (X icon), removing the standalone X button. It keeps the optimistic `useSteerCancel` path — no reclaim gate, since cancel drops the words rather than re-homing them. - Show the ⋯ at rest on every pointer instead of hover-gating it. A label-less menu hidden until hover is undiscoverable on desktop and unreachable on touch; always-visible also matches the queued rows' controls and drops the hover/focus/menu-open opacity juggling entirely. * 🪢 feat: Make Cancel and Queued Trash Non-Destructive Both removal actions now hand their text back to the composer instead of dropping it, so a message the user typed is never gone forever. - In-flight Cancel: before cancelling, restore the words to the composer via the gated `restoreReclaimedSteer` (skipped once applied — they are already in the response). The restore refuses on its own rather than clobber a draft, land in another chat, or fight answer mode; the cancel still runs reliably either way, so an unwanted steer stays killable. - Queued Trash: same safety net — thread the gated restore into the queued rows and return the words (with their carried quotes/skills) to the composer, then remove either way. Aligns the two surfaces on one behavior. - Export the shared `RestoreToComposer` type so both surfaces reuse it. Left the reliable-remove path intact (a steer sometimes must be killed before it reaches the model) and did NOT reach for a delete+Undo snackbar, which the shared Toast can't render without a cross-cutting action-button change. * 🔒 fix: Restore Cancelled Steer Text Only on a Reclaimed Outcome The cancel safety net restored the words to the composer synchronously, before the cancel POST resolved. On `applied` (cancel lost the race, steer still injects) or `failed` (POST errored, chip restored), the same text ended up both in the run/bubble and in the composer. Await `cancelSteer`'s outcome and restore ONLY on `reclaimed` — the one result that proves the steer never reached the run. `applied`/`failed` leave the words where the events place them, no composer copy. The gated restore still refuses rather than clobber a draft typed during the round-trip. * 🧵 fix: Never Drop Cancelled Text; Keep the Steer's Submit Time Two follow-ups on the cancel safety net. - Cancel no longer silently drops the words when the reclaim succeeds but the composer refuses the restore (draft typed, answer mode, navigated). The chip is already gone, so queue them like Edit does — never lost, just re-homed — with the same toast. - Preserve the true submission timestamp across submitSteer's chip states. The ACK and failure chips reset createdAt to a LATER Date.now(), so a draft queued during the 202 round-trip could sort ahead of a steer submitted before it and drain out of order. Capture the submit time once and reuse it for all three. * 🚪 fix: Refuse Reclaimed-Steer Restore Into an Unmounted Composer A reclaim/cancel round-trip can resolve after ChatForm unmounts (left the route, closed the pane). Its refs still hold the origin conversation, so `restoreReclaimedSteer` passed its checks, wrote into a dead form, and returned true — making the caller drop the steer instead of queueing it, losing the text. Track mount state and refuse the restore once unmounted, so the caller queues the words (recoil is global, so the queued chip survives the navigation). |
||
|---|---|---|
| .devcontainer | ||
| .do/gitnexus | ||
| .github | ||
| .husky | ||
| .vscode | ||
| api | ||
| client | ||
| config | ||
| e2e | ||
| helm | ||
| otel/langfuse-fanout | ||
| packages | ||
| redis-config | ||
| scripts | ||
| skill | ||
| src/tests | ||
| utils | ||
| .dockerignore | ||
| .env.example | ||
| .gitattributes | ||
| .gitignore | ||
| .nvmrc | ||
| .prettierrc | ||
| AGENTS.md | ||
| bun.lock | ||
| CLAUDE.md | ||
| deploy-compose.langfuse-fanout.yml | ||
| deploy-compose.yml | ||
| docker-compose.langfuse-fanout.yml | ||
| docker-compose.override.yml.example | ||
| docker-compose.yml | ||
| Dockerfile | ||
| Dockerfile.multi | ||
| eslint.config.mjs | ||
| librechat.example.yaml | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| rag.yml | ||
| README.md | ||
| README.zh.md | ||
| turbo.json | ||
LibreChat
English · 中文
✨ Features
-
🖥️ UI & Experience inspired by ChatGPT with enhanced design and features
-
🤖 AI Model Selection:
- Anthropic (Claude), AWS Bedrock, OpenAI, Azure OpenAI, Google, Vertex AI, OpenAI Responses API (incl. Azure)
- Custom Endpoints: Use any OpenAI-compatible API with LibreChat, no proxy required
- Compatible with Local & Remote AI Providers:
- Ollama, groq, Cohere, Mistral AI, Apple MLX, koboldcpp, together.ai,
- OpenRouter, Helicone, Perplexity, ShuttleAI, Deepseek, Qwen, and more
-
- Secure, Sandboxed Execution in Python, Node.js (JS/TS), Go, C/C++, Java, PHP, Rust, and Fortran
- Seamless File Handling: Upload, process, and download files directly
- No Privacy Concerns: Fully isolated and secure execution
- Open-Source & Self-Hostable: powered by ClickHouse/code-interpreter
-
🔦 Agents & Tools Integration:
- LibreChat Agents:
- No-Code Custom Assistants: Build specialized, AI-driven helpers
- Agent Marketplace: Discover and deploy community-built agents
- Collaborative Sharing: Share agents with specific users and groups
- Flexible & Extensible: Use MCP Servers, tools, file search, code execution, and more
- Skills: Create reusable
SKILL.mdinstruction bundles for manual, automatic, or always-on agent workflows - Subagents: Delegate focused work to isolated child agent runs with their own context windows
- Compatible with Custom Endpoints, OpenAI, Azure, Anthropic, AWS Bedrock, Google, Vertex AI, Responses API, and more
- Model Context Protocol (MCP) Support for Tools
- LibreChat Agents:
-
🔍 Web Search:
- Search the internet and retrieve relevant information to enhance your AI context
- Combines search providers, content scrapers, and result rerankers for optimal results
- Customizable Jina Reranking: Configure custom Jina API URLs for reranking services
- Learn More →
-
🪄 Generative UI with Code Artifacts:
- Code Artifacts allow creation of React, HTML, and Mermaid diagrams directly in chat
-
🎨 Image Generation & Editing
- Text-to-image and image-to-image with GPT-Image-1
- Text-to-image with DALL-E (3/2), Stable Diffusion, Flux, or any MCP server
- Produce stunning visuals from prompts or refine existing images with a single instruction
-
💾 Presets & Context Management:
- Create, Save, & Share Custom Presets
- Switch between AI Endpoints and Presets mid-chat
- Edit, Resubmit, and Continue Messages with Conversation branching
- Create and share prompts with specific users and groups
- Fork Messages & Conversations for Advanced Context control
-
💬 Multimodal & File Interactions:
- Upload and analyze images with Claude 3, GPT-4.5, GPT-4o, o1, Llama-Vision, and Gemini 📸
- Chat with Files using Custom Endpoints, OpenAI, Azure, Anthropic, AWS Bedrock, & Google 🗃️
-
🌎 Multilingual UI:
- English, 中文 (简体), 中文 (繁體), العربية, Deutsch, Español, Français, Italiano
- Polski, Português (PT), Português (BR), Русский, 日本語, Svenska, 한국어, Tiếng Việt
- Türkçe, Nederlands, עברית, Català, Čeština, Dansk, Eesti, فارسی
- Suomi, Magyar, Հայերեն, Bahasa Indonesia, ქართული, Latviešu, ไทย, ئۇيغۇرچە
-
🧠 Reasoning UI:
- Dynamic Reasoning UI for Chain-of-Thought/Reasoning AI models like DeepSeek-R1
-
🎨 Customizable Interface:
- Customizable Dropdown & Interface that adapts to both power users and newcomers
-
- Never lose a response: AI responses automatically reconnect and resume if your connection drops
- Multi-Tab & Multi-Device Sync: Open the same chat in multiple tabs or pick up on another device
- Production-Ready: Works from single-server setups to horizontally scaled deployments with Redis
-
🗣️ Speech & Audio:
- Chat hands-free with Speech-to-Text and Text-to-Speech
- Automatically send and play Audio
- Supports OpenAI, Azure OpenAI, and Elevenlabs
-
📥 Import & Export Conversations:
- Import Conversations from LibreChat, ChatGPT, Chatbot UI
- Export conversations as screenshots, markdown, text, json
-
🔍 Search & Discovery:
- Search all messages/conversations
-
👥 Multi-User & Secure Access:
- Multi-User, Secure Authentication with OAuth2, LDAP, & Email Login Support
- Built-in Moderation, and Token spend tools
-
🎛️ Admin Panel:
- Browser-based UI to manage users, groups, roles, and configuration overrides
- Edit settings and per-role/group permissions live, without redeploying
- Bundled with the Docker Compose stacks for one-command setup
-
⚙️ Configuration & Deployment:
- Configure Proxy, Reverse Proxy, Docker, & many Deployment options
- Use S3 with CloudFront for stable media links, edge delivery, signed cookies, and secured downloads
- Use completely local or deploy on the cloud
-
📖 Open-Source & Community:
- Completely Open-Source & Built in Public
- Community-driven development, support, and feedback
For a thorough review of our features, see our docs here 📚
🪶 All-In-One AI Conversations with LibreChat
LibreChat is a self-hosted AI chat platform that unifies all major AI providers in a single, privacy-focused interface.
Beyond chat, LibreChat provides AI Agents, Model Context Protocol (MCP) support, Artifacts, Code Interpreter, custom actions, conversation search, and enterprise-ready multi-user authentication.
Open source, actively developed, and built for anyone who values control over their AI infrastructure.
🌐 Resources
GitHub Repo:
- RAG API: github.com/danny-avila/rag_api
- Website: github.com/LibreChat-AI/librechat.ai
Other:
- Website: librechat.ai
- Documentation: librechat.ai/docs
- Blog: librechat.ai/blog
📝 Changelog
Keep up with the latest updates by visiting the releases page and notes:
⚠️ Please consult the changelog for breaking changes before updating.
⭐ Star History
✨ Contributions
Contributions, suggestions, bug reports and fixes are welcome!
For new features, components, or extensions, please open an issue and discuss before sending a PR.
If you'd like to help translate LibreChat into your language, we'd love your contribution! Improving our translations not only makes LibreChat more accessible to users around the world but also enhances the overall user experience. Please check out our Translation Guide.
💖 This project exists in its current state thanks to all the people who contribute
🎉 Special Thanks
We thank Locize for their translation management tools that support multiple languages in LibreChat.