Enhanced ChatGPT Clone: Features Agents, MCP, DeepSeek, Anthropic, AWS, OpenAI, Responses API, Azure, Groq, o1, GPT-5, Mistral, OpenRouter, Vertex AI, Gemini, Artifacts, AI model switching, message search, Code Interpreter, langchain, DALL-E-3, OpenAPI Actions, Functions, Secure Multi-User Auth, Presets, open-source for self-hosting. Active. https://librechat.ai/
Find a file
Danny Avila 452af50eff
🧮 fix: Atomize Redis Event Sequence Counters for Multi-Replica Deployments (#12578)
* fix: atomize Redis event sequence counters for multi-replica deployments

Replace in-memory sequenceCounters Map with shared atomic Redis counter
(INCR via Lua script) so all replicas share an authoritative sequence
source. Make syncReorderBuffer async to read current sequence from Redis
instead of defaulting to 0 on non-publishing replicas.

Closes #12575

* fix: harden emitChunk error handling and syncReorderBuffer race safety

Wrap getNextSequence inside emitChunk's try/catch so a transient Redis
failure is logged-and-swallowed, preserving the non-fatal chunk emission
contract. In syncReorderBuffer, replace pending.clear() with selective
discard of stale entries (seq < currentSeq) followed by
flushPendingMessages, preventing loss of chunks that arrived during the
async GET window.

* fix: address review findings — harden error paths, validate types, DRY tests

- Wrap syncReorderBuffer in try/catch in GenerationJobManager.subscribe()
  so a Redis GET failure degrades gracefully instead of crashing SSE reconnect.
- Validate eval return type in getNextSequence; throw on unexpected type
  instead of silently computing NaN/-1 and dropping all messages.
- Add NaN guard to parseInt in syncReorderBuffer for corrupted Redis values.
- Consolidate two streams loops in destroy() into a single pass.
- Extract createMockPublisher to shared helpers/publisher.ts (DRY).
- Add tests for eval failure and unexpected eval return type in emitChunk.
- Document performance tradeoff (2x RTT per emit) and TTL rationale.

* test: add cross-replica integration tests for sequence desync fix (#12575)

Three real-Redis tests that reproduce the exact multi-replica failure:

- Late subscriber on a different replica syncs to the shared counter
  and receives chunks immediately (no 500ms force-flush).
- Multiple subscribe/unsubscribe cycles across replicas maintain
  correct sequence alignment on every reconnect.
- Shared counter key is cleaned up when the stream is destroyed.

* fix: close syncReorderBuffer race, stop destroy() from nuking shared keys

- Replace selective prune with Math.min(currentSeq, minPendingSeq) so
  chunks that arrive in pending during the async GET window are preserved
  instead of incorrectly pruned. Since unsubscribe() already clears
  pending, any entries at sync time are live messages from the current
  subscription and must not be discarded.
- Remove DEL from destroy() — the sequence key is shared across replicas
  and a shutting-down instance must not nuke it for active publishers.
  Keys expire naturally via their 1-hour TTL; cleanup() handles
  single-stream lifecycle teardown.
- Add race-condition test: pauses GET, injects a message into pending
  during the window, resolves GET, asserts the chunk is delivered.
- Add emitDone/emitError eval failure tests to cover the asymmetric
  error contract (chunk swallows, done/error propagates).
- Add explicit MockPublisher return type to helpers/publisher.ts.

* fix: context-aware syncReorderBuffer to prevent duplicate delivery, silence test logs

The Math.min(currentSeq, minPending) fix from the prior commit correctly
handles the cross-replica race (chunk arriving during async GET), but
causes duplicate delivery in same-replica mode: onAbort subscribes to
the Redis channel during createJob, so chunks published before
subscribe() may arrive via pub/sub AND earlyEventBuffer. The Math.min
logic then flushes the pub/sub copy as a "live" message.

Fix: add a clearPending parameter to syncReorderBuffer. The manager
passes true when earlyEventBuffer was replayed (same-replica: pending
entries are duplicates → clear them) and false/undefined when it was
not (cross-replica: pending entries are live → preserve via Math.min).

- Silence winston logger in stream test files via logger.silent = true,
  following the existing pattern in data-schemas/prompt.spec.ts.
- Remove sequence key DEL from destroy() — shared across replicas,
  a shutting-down instance must not nuke the counter for active
  publishers. Keys expire via TTL; cleanup() handles stream teardown.

* fix: share publisher client in cross-replica tests for Redis Cluster compat

The cross-replica tests created publisherB via (ioredisClient as Redis)
.duplicate(), which produces a plain Redis connection to a single node.
In Redis Cluster, GET/EVAL on this client can't follow MOVED redirects,
so syncReorderBuffer reads null → nextSeq=0 → chunks are buffered.

Fix: share ioredisClient as the publisher for both replicas. It's the
correct Cluster-aware client and handles slot routing automatically.
Only subscriber connections need to be separate (pub/sub requirement).

* fix: increase cross-replica test timeouts and use polling for CI cluster

Replace fixed 300ms delivery waits with polling (50ms intervals, 2s max)
to handle Redis Cluster's cross-node pub/sub broadcast latency in CI.
Increase subscription activation wait from 100ms to 500ms to match the
pattern used by existing same-instance tests.

* chore: silence winston logs in remaining stream test files

Add logger.silent = true to GenerationJobManager, RedisJobStore, and
collectedUsage test files, matching the pattern already applied to
RedisEventTransport and reconnect-reorder-desync tests.

* fix: remove flaky cluster pub/sub tests, fix log suppression for resetModules

Remove two cross-replica transport-level integration tests that are
inherently non-deterministic in Redis Cluster: cluster pub/sub fan-out
is async across nodes, so pre-subscribe PUBLISHes can arrive at the
subscriber's node after SUBSCRIBE takes effect, causing random +/- 1
message counts. The core logic is already covered by deterministic unit
tests (mock publisher) and GenerationJobManager integration tests
(end-to-end with earlyEventBuffer). The cleanup test is retained.

Switch log suppression from logger.silent (which doesn't survive
jest.resetModules) to jest.spyOn(console, 'log').mockImplementation()
for files that use resetModules (GenerationJobManager, RedisJobStore,
collectedUsage). The logger.silent approach remains for files that
don't use resetModules (RedisEventTransport, reconnect-reorder-desync).

* fix: replace Lua EVAL+TTL with plain INCR, preserve live chunks during same-replica sync

P1: syncReorderBuffer with clearPending=true was unconditionally clearing
pending, dropping NEW chunks (seq >= currentSeq) from ongoing generation
that arrived via pub/sub during the async GET. Fix: selectively prune
only entries with seq < currentSeq (duplicates of earlyEventBuffer) and
flush remaining live entries.

P2: The 1-hour TTL on sequence keys could expire mid-stream during long
quiet periods (e.g., slow tool calls), restarting the counter from zero
and causing subscribers to silently drop all subsequent messages. Fix:
remove the Lua EVAL+EXPIRE script entirely and use plain Redis INCR —
no TTL. Keys are cleaned up explicitly by cleanup()/resetSequence()
when streams end. Orphaned keys from crashed processes are a few bytes
each, negligible compared to the production risk of TTL expiry.

- Update mock publisher helper: eval → incr
- Remove "unexpected eval type" tests (not applicable to incr)
- Update remaining eval error tests to reference incr

* fix: re-arm flush timeout after syncReorderBuffer when gaps remain

syncReorderBuffer clears flushTimeout before processing, but if pending
still has gaps after flushPendingMessages, no timeout is re-armed.
Without new messages arriving to trigger scheduleFlushTimeout, those
buffered entries sit indefinitely — stalling the stream after reconnect.

* chore: address final review — fix stale docs, rename clearPending, tighten tests

Fix all 10 findings from final review pass:

F1: Fix stale TTL comment in destroy() — no TTL exists after EVAL removal
F2: Fix stale EVAL reference in emitChunk JSDoc → INCR + PUBLISH
F3: Fix syncReorderBuffer JSDoc — describes old broken behavior, not
    the current selective prune
F4: Rename clearPending → pruneStaleEntries for clarity at call sites
F5: Add publish-not-called assertion to INCR failure test
F6: Replace Math.min(...spread) with explicit loop to avoid heap alloc
F7: Remove resetSequence from IEventTransport interface (no external
    callers; implementation detail only)
F8: Consolidate cleanup/resetSequence overlap — cleanup() owns the DEL,
    resetReorderBuffer() (now private) handles buffer state only
F9: Fix import order in reconnect test (value before type imports)
F10: Export MockPublisher interface from helpers/publisher.ts

* chore: fix stale resetSequence references and hadBufferReplay JSDoc

Two comments referenced resetSequence() which was removed in the F7/F8
refactor (now private resetReorderBuffer(), which doesn't DEL the key).
Only cleanup() deletes the Redis key. Also update hadBufferReplay JSDoc
to say "prune stale entries" instead of the pre-refactor "clear pending".

* chore: grammar

* fix: use earlyReplayCount as prune cutoff instead of Redis counter

The boolean pruneStaleEntries flag used currentSeq (from Redis GET) as
the prune threshold, but INCR can advance the counter past a live
chunk's seq during the GET window. Example: earlyEventBuffer held seqs
0-4, generation emits seq 5 during GET, INCR advances counter to 6,
GET returns 6, prune condition 5 < 6 deletes the live chunk.

Fix: pass the earlyEventBuffer replay count (5) as the prune cutoff.
Entries with seq < earlyReplayCount are true duplicates; entries at or
above are live regardless of what the Redis counter reads. After
pruning, the unified Math.min(currentSeq, minPending) logic handles
both same-replica and cross-replica paths correctly.

Add a targeted test that exercises this exact race: pauses GET, emits
seq 5 during the window, resolves GET with counter=6, asserts seq 5
is preserved (would have been dropped with the old boolean approach).

* fix: pass earlyReplayCount for skipBufferReplay path to prune pub/sub duplicates

When skipBufferReplay is true (resume scenario), earlyEventBuffer events
are delivered via the resume sync payload, not replayed directly. But
earlyReplayCount was left at 0, so syncReorderBuffer treated all pending
entries as live — meaning delayed pub/sub copies of those buffered
events could be delivered again, duplicating content.

Fix: capture earlyEventBuffer.length before the skip/replay branch so
the count is always passed to syncReorderBuffer regardless of delivery
method. Seqs 0..earlyReplayCount-1 are pruned as duplicates whether
they were replayed or delivered via sync payload.

* fix: keep nextSeq monotonic in syncReorderBuffer after async GET

handleOrderedChunk can deliver in-order messages and advance nextSeq
during the async GET window. If those messages leave pending empty,
the unconditional nextSeq = currentSeq could regress nextSeq below
its already-advanced value, reopening a delivered gap and causing
subsequent messages to be buffered until force-flush.

Fix: wrap both nextSeq assignments with Math.max(nextSeq, ...) so
syncReorderBuffer never moves the delivery frontier backward.

* fix: cap nextSeq at earlyReplayCount, add 24h safety TTL, add post-GET race test

Finding 1 (CRITICAL): When earlyReplayCount > 0 and pending is empty,
nextSeq was set to currentSeq — but INCR can advance the counter past
a live chunk whose pub/sub hasn't arrived yet. Cap at earlyReplayCount
instead (what was actually delivered), so in-flight chunks are not
skipped. Adds test for the message-arrives-AFTER-GET-resolves scenario.

Finding 3: Add a 24-hour safety-net TTL set once on first INCR only
(val === 1), never refreshed. This caps orphan lifetime from crashed
processes without risking mid-stream counter resets.

Finding 6: Replace setTimeout(100) in cleanup test with polling loop.

Finding 9: Fix variadic DEL mock + add expire mock for the new TTL.

Revert P2 fix (earlyReplayCount for skipBufferReplay path) — when
skipBufferReplay is true, the resume sync payload delivers everything
up to currentSeq, so syncReorderBuffer should trust the Redis counter
as the frontier, not the buffer length.

* chore: log expire failures consistently with other fire-and-forget errors
2026-04-09 09:57:54 -04:00
.devcontainer 🪦 refactor: Remove Legacy Code (#10533) 2025-12-11 16:36:12 -05:00
.fly/gitnexus 📊 experimental: Add GitNexus CI/CD and deployment configuration (#12577) 2026-04-08 16:17:19 -04:00
.github 📊 experimental: Add GitNexus CI/CD and deployment configuration (#12577) 2026-04-08 16:17:19 -04:00
.husky 🎨 refactor: Redesign Sidebar with Unified Icon Strip Layout (#12013) 2026-03-22 01:15:20 -04:00
.vscode 🔐 feat: Granular Role-based Permissions + Entra ID Group Discovery (#7804) 2025-08-13 16:24:17 -04:00
api 🔐 fix: Add Tenant Context to Admin OAuth Callback Routes (#12579) 2026-04-08 20:04:15 -04:00
client 🌍 i18n: Update translation.json with latest translations (#12583) 2026-04-09 07:45:13 -04:00
config 📦 refactor: Consolidate DB models, encapsulating Mongoose usage in data-schemas (#11830) 2026-03-21 14:28:53 -04:00
e2e 🐘 feat: FerretDB Compatibility (#11769) 2026-03-21 14:28:49 -04:00
helm v0.8.4 (#12339) 2026-03-20 18:01:00 -04:00
packages 🧮 fix: Atomize Redis Event Sequence Counters for Multi-Replica Deployments (#12578) 2026-04-09 09:57:54 -04:00
redis-config 🔄 refactor: Migrate Cache Logic to TypeScript (#9771) 2025-10-02 09:33:58 -04:00
src/tests 🆔 feat: Add OpenID Connect Federated Provider Token Support (#9931) 2025-11-21 09:51:11 -05:00
utils 🐳 chore: Update image registry references in Docker/Helm configurations (#12026) 2026-03-02 22:14:50 -05:00
.dockerignore 🐳 : Further Docker build Cleanup & Docs Update (#1502) 2024-01-06 11:59:08 -05:00
.env.example ⚗️ feat: Agent Context Compaction/Summarization (#12287) 2026-03-21 14:28:56 -04:00
.gitattributes 🎛️ feat: DB-Backed Per-Principal Config System (#12354) 2026-03-25 19:39:29 -04:00
.gitignore 🪆 fix: Allow Nested addParams in Config Schema (#12526) 2026-04-02 20:38:46 -04:00
.prettierrc 🧹 chore: Migrate to Flat ESLint Config & Update Prettier Settings (#5737) 2025-02-09 12:15:20 -05:00
AGENTS.md 📋 chore: Move project instructions from AGENTS.md to CLAUDE.md 2026-03-31 21:50:38 -04:00
bun.lock v0.8.4 (#12339) 2026-03-20 18:01:00 -04:00
CLAUDE.md 📋 chore: Move project instructions from AGENTS.md to CLAUDE.md 2026-03-31 21:50:38 -04:00
deploy-compose.yml 🔒 chore: Bump MongoDB from 8.0.17 to 8.0.20 in Docker Compose Files (#12399) 2026-03-25 13:56:43 -04:00
docker-compose.override.yml.example 🐳 chore: Update image registry references in Docker/Helm configurations (#12026) 2026-03-02 22:14:50 -05:00
docker-compose.yml 🔒 chore: Bump MongoDB from 8.0.17 to 8.0.20 in Docker Compose Files (#12399) 2026-03-25 13:56:43 -04:00
Dockerfile v0.8.4 (#12339) 2026-03-20 18:01:00 -04:00
Dockerfile.multi v0.8.4 (#12339) 2026-03-20 18:01:00 -04:00
eslint.config.mjs 🛡️ refactor: Self-Healing Tenant Isolation Update Guard (#12506) 2026-04-01 19:07:52 -04:00
librechat.example.yaml 🧹 chore: Clean Up Config Fields (#12537) 2026-04-03 12:22:58 -04:00
LICENSE 🗒️ docs: Update LICENSE.md Year: 2025 -> 2026 (#12554) 2026-04-08 09:12:44 -04:00
package-lock.json 📦 chore: npm audit (#12570) 2026-04-07 23:08:05 -04:00
package.json 📦 chore: npm audit (#12570) 2026-04-07 23:08:05 -04:00
rag.yml 🐳 chore: Update image registry references in Docker/Helm configurations (#12026) 2026-03-02 22:14:50 -05:00
README.md 📝 docs: update deployment link for Railway in README and README.zh.md (#12449) 2026-03-28 20:10:36 -04:00
README.zh.md 📝 docs: update deployment link for Railway in README and README.zh.md (#12449) 2026-03-28 20:10:36 -04:00
turbo.json 🏎️ feat: Smart Reinstall with Turborepo Caching for Better DX (#11785) 2026-02-13 14:25:26 -05:00

LibreChat

English · 中文

Deploy on Railway Deploy on Zeabur Deploy on Sealos

Translation Progress

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
  • 🔧 Code Interpreter API:

    • 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
  • 🔦 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
      • Compatible with Custom Endpoints, OpenAI, Azure, Anthropic, AWS Bedrock, Google, Vertex AI, Responses API, and more
      • Model Context Protocol (MCP) Support for Tools
  • 🔍 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

  • 💾 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
  • 🌊 Resumable Streams:

    • 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
  • ⚙️ Configuration & Deployment:

    • Configure Proxy, Reverse Proxy, Docker, & many Deployment options
    • 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:

Other:


📝 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

Star History Chart

danny-avila%2FLibreChat | Trendshift ROSS Index - Fastest Growing Open-Source Startups in Q1 2024 | Runa Capital


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.

Locize Logo