Compare commits

...
Author SHA1 Message Date
Hanzo Dev 01597b9bb2 merge: complete @hanzochat fork + org/project/user switcher
Brings the fork completion (de-librechat, @hanzochat scope rename, @hanzo/iam
0.13 API) and the org/project/user switcher (validated active-org -> X-Org-Id
on both cloud seams) onto main for release.
2026-07-14 12:46:42 -07:00
Hanzo Dev 9c4488d66e chat: org/project/user switcher — active org rides X-Org-Id to cloud
The account menu shows user -> org -> project and lets a multi-org member
switch their working org. The switch pins hanzo_active_org (httpOnly),
validated server-side against the caller's own membership set (owner + groups)
via POST /v1/chat/user/active-org. resolveActiveOrg() forwards it as X-Org-Id
on both on-behalf-of seams — the inference path (custom/initialize.ts) and
cloud agents (CloudAgentsClient) — where the gateway re-validates it in the
member's set (HIP-0026), so the header is a validated selection, never a trust
assertion. Adds Account/Console/Billing links. openidStrategy persists
organization/project/groups from the JWT claims; the user schema + TUser carry
them and getUserController serves them.
2026-07-14 12:46:26 -07:00
Hanzo Dev b237551ffa chat: de-librechat — CHAT_ env, chat.yaml, Chat identifiers; README rewrite + zh
- env vars LIBRECHAT_* -> CHAT_* (USER_ID, GRAPH_ACCESS_TOKEN, OPENID_*, LOG_DIR, ...)
- config file librechat.yaml -> chat.yaml (matches prod CONFIG_PATH) + .example
- identifiers LibreChat -> Chat (extractChatParams, ChatKeys, ChatParams, importChatConvo)
- lowercase librechat -> chat: i18n keys, helm charts (helm/chat, helm/chat-rag-api),
  CI workflows, OTel service/span keys, codeapi JWT issuer, x-chat-thread-id header
- README.md rewritten (Node 24, pnpm 10, Zen family, Cloud Agents, hanzo.id OIDC);
  README.zh.md added (Simplified Chinese)

MIT LICENSE copyright + one 'Forked from LibreChat (MIT)' attribution retained.
Build 5/5 green.
2026-07-14 10:41:50 -07:00
Hanzo Dev c7f7ef984e chat: drop all librechat.ai URLs; attribute as Hanzo
- librechat.ai doc/ToS/privacy URLs -> hanzo.ai / hanzo.chat
- OpenRouter attribution HTTP-Referer + X-Title -> https://hanzo.chat / Hanzo Chat
  (source + specs updated in lockstep)
- registry.librechat.ai dev images -> hanzoai/chat + ghcr.io/hanzoai/rag-api
- remove upstream README.zh.md (Hanzo zh translation follows)

MIT LICENSE copyright retained (legal attribution).
2026-07-14 10:31:38 -07:00
Hanzo Dev 74954c3f81 fix: @hanzo/iam 0.13 API (BrowserIamSdk->IAM) + regenerate pnpm-lock
The static/IAM SPA path (client/src/utils/iam.ts, OAuthCallback.tsx) imported
BrowserIamSdk, removed when @hanzo/iam bumped 0.4->0.13 (HIP-0111); the class is
now IAM with an identical constructor config. pnpm-lock.yaml regenerated for the
@hanzochat rename. Fork now builds 5/5 tasks green.
2026-07-14 10:23:39 -07:00
Hanzo Dev e9b4c41d2d fork: complete @hanzochat rename — agents runtime + lockfile canonicalization
- @librechat/agents -> @hanzochat/agents@^3.2.63 (published fork of
  danny-avila/agents at github.com/hanzochat/agents; upstream remote kept
  so agent-runtime fixes stay pullable)
- final sweep: zero @librechat/* package refs (chat internals + runtime)
- remove dead packages/agents residue (stale dist, no package.json)
- drop stale npm/bun lockfiles; pnpm-lock.yaml is the canonical lockfile
2026-07-14 09:31:40 -07:00
Hanzo Dev ba0eb6f2d2 fork: rename internal workspace packages to @hanzochat scope
librechat-data-provider  → @hanzochat/data-provider
@librechat/data-schemas  → @hanzochat/data-schemas
@librechat/client        → @hanzochat/client
(@hanzochat/api already forked; external @librechat/agents fork follows)

All imports, package.json names/deps, tsconfig path aliases, jest
moduleNameMapper, and rollup/vite build aliases updated. The data-provider
react-query subpath resolves via the exports map. Lockfiles regenerated in a
follow-on once the canonical package manager is confirmed.
2026-07-14 05:55:49 -07:00
zeekayandClaude Opus 4.8 3bb78e514c Mobile polish: true-black convergence, overlay cap, 44px composer targets
True-black (client/src/style.css, client/tailwind.config.cjs):
- Converge the off-scale #0a0a0a onto the token scale. --gray-925 (consumed by
  --presentation / --surface-primary-alt / --surface-dialog) and --green-950,
  plus the Tailwind mono ramp's 950, now resolve to #050505, so inner panels /
  dialogs match the elevated-surface value instead of reading as a lighter grey.
  Verified in-browser: --gray-925/#050505, dark --surface-primary-alt/-dialog =
  #050505, --surface-primary/-chat = #000.
- Root.tsx auth-loading fallback and LandingPage.tsx local tokens (bg #000 /
  card #050505 / muted #171717) converged off #0a0a0a onto the same scale.

Overlay cap (client/src/mobile.css):
- Cap .nav-mask and .sidenav-mask at max-width:420px (anchored to the drawer's
  own edge — left for nav, right for sidenav) instead of a full-width scrim, so
  on a tablet the dim covers only the drawer footprint, not the whole viewport.
  Align the nav-mask media query to 768px to match Nav.tsx's
  useMediaQuery('(max-width: 768px)') so the cap also covers the tablet-portrait
  boundary where the drawer is active. Verified: at 768px the scrim renders 420px
  (55% of viewport), not 100%.

Touch targets (client/src/mobile.css, client/src/components/Chat/Input/ChatForm.tsx):
- Add a `composer-actions` marker on the composer action row and bump its round
  action buttons (attach / tools / mic / send-stop, previously size-9 = 36px) to
  a 44px min touch target on touch viewports. Verified 44x44 at 375px, unchanged
  36px at desktop.

Build unblock (pre-existing dependency drift that hard-crashed the client):
- NetworkWallet.tsx: @hanzo/ui 5.x dropped ./network and ./wallet exports, whose
  named imports failed the Vite dep-scan and Rollup build; render null with a
  re-enable recipe (mirrors hanzo/app).
- utils/iam.ts: @hanzo/iam 0.4.x exports the SPA client as BrowserIamSdk, not
  IAM; the wrong name crashed the app at mount and failed the build. Same
  constructor config; swap to BrowserIamSdk.

Verified: `vite build` passes; Playwright at 375/768/1280 confirms true-black
surfaces, the 420px overlay cap, and 44px composer targets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 23:42:54 -07:00
zeekayandClaude Opus 4.8 d4a9b14ba6 chore(release): chat v0.9.20 (QueryBuilder.distinct ACL fix)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 16:23:51 -07:00
zeekayandClaude Opus 4.8 b49354fd1a fix(store/acl): implement QueryBuilder.distinct so ACL resolution stops crashing
The SQLite DocModel (the Mongo replacement chat migrated onto) exposed
`distinct()` only on the model (static call sites like
`PromptGroup.distinct('category', filter)` worked), but `find()` returns a
`QueryBuilder` that lacked it. Every query-chain call site —
`AclEntry.find(q).distinct('resourceId')` (findAccessibleResources,
findPubliclyAccessibleResources) and `AgentCategory.find(q).distinct('value')`
— threw "find(...).distinct is not a function", crashing permission
resolution.

One-place DRY fix mirroring mongoose's `Query.prototype.distinct(field)`:
QueryBuilder.distinct(field) is chainable (so a trailing `.lean()` still
works) and delegates to the existing DocModel.distinct, keeping filter/tenant
scoping identical to `.find()`. No mongoose reintroduced.

Tests: added ACL `$or`+`$bitsAllSet` distinct + AgentCategory
`.distinct().lean()` cases to DocModel.spec.ts (14/14 pass).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 16:21:19 -07:00
zeekayandClaude Opus 4.8 3759e382f6 chat: monochrome the composer badge-row chrome
Neutralize the per-tool accent hues on the composer's active
CheckboxButton chips so the shell chrome recedes and stays true-black
monochrome (matching hanzo.ai). Each tool's "on" state painted itself a
distinct color — Search=blue, Code=purple, FileSearch=green,
Skills=cyan, Artifacts=amber (+ its split-menu button) — reading as a
rainbow in the composer. Unify to one neutral active treatment via the
existing semantic tokens (border-border-heavy + bg-surface-active-alt),
which already track the dark/true-black theme.

Also drops the stray text-cyan-500 on the queued-skills chip icon →
text-text-secondary.

Shell top-left (H mark alone + brand right-click menu) and bottom-left
(consolidated AccountSettings cluster) are already correct via the
shared @hanzo/ui HanzoHeader (v5.5.1) + AccountSettings — no fork.
No org switcher exists in chat (org is server-derived from the hanzo.id
owner claim); none fabricated. Semantic hues (balance status, errors,
online dots) left intact. Message content / syntax highlighting
untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 14:11:12 -07:00
zeekay 963399cedc chat: add login util 2026-07-10 09:40:34 -07:00
6110349085 fix(chat/sse): surface upstream errors instead of a silent empty bubble (#68)
A rejected gateway key or an out-of-credits balance can end a `stream:true`
completion with NO content and WITHOUT throwing — api.hanzo.ai answers as a
200 event-stream that yields zero deltas — so the agent run "succeeds" with an
empty response and the client renders an EMPTY assistant bubble. This is the
exact silent failure behind the dead-guest-key GA blocker (a dead key -> 401 ->
{"msg":"invalid API key"} that the SSE client rendered as an empty reply).

Add a backstop in ResumableAgentController: right after `client.sendMessage`,
if the response carries no user-visible content and the run was not aborted,
throw. The throw lands in the existing catch -> `GenerationJobManager.emitError`
-> the `event: error` SSE frame the client already renders as an error bubble.
No new SSE plumbing — it reuses the one error path, so a dead key or exhausted
balance can never again be invisible.

The emptiness decision is a pure, unit-tested helper (`isEmptyAgentResponse`):
non-empty iff there is non-whitespace `text`, a non-blank text/think part, or
any non-text content part (error, tool_call, image, ...). An ERROR part still
renders, so the gateway-threw path is untouched.

Tests:
- emptyResponse.spec.js: 8 cases pinning the predicate (empty content array,
  blank-text-only, error/tool_call/text parts non-empty).
- emptyResponseController.spec.js: drives the real controller — empty response
  emits `error` (not a silent `done`); a response with content emits `done`.

Claude-Session: https://claude.ai/code/session_016yg7GPhYdWCh9vpp4HEwLZ

Co-authored-by: hanzo-dev <dev@hanzo.ai>
2026-07-09 22:14:41 -07:00
zeekayandClaude Opus 4.8 dd8fb7194c feat(picker): Zen family at top, Agents second, providers after
Model selector now surfaces the house family as "Zen": custom endpoints honor
their configured modelDisplayLabel as the group label, so the endpoint VALUE
stays "Hanzo" (existing conversations + pinned guest endpoint unaffected) while
the picker group reads "Zen". Config bumps the third-party providers to
customOrder 2+ so the built-in Agents endpoint (order 1) sits alone between Zen
(0) and the providers — Zen first, Agents second, other providers after. Does
not conflate Hanzo (agents) with Zen (models).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 17:19:14 -07:00
zeekayandClaude Opus 4.8 4448e8d49c feat(chat): project-scoped conversations linked across surfaces
Accept ?project=<slug> (the org-unique key of the ONE cloud /v1/projects store,
org from the IAM JWT). A slim ProjectBanner shows the active project and links
the same slug back to the hanzo.app builder and console.hanzo.ai; a new
conversation opened for a project seeds the composer with a short opener so the
assistant has project context. Slug validated against the store's grammar and
persisted for the session so the scope survives the /c/new -> /c/:id navigation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 16:24:49 -07:00
cb0649e10b feat(chat): shared NetworkSwitcher + WalletMenu — the ONE hanzo.network standard (#67)
* feat(chat): shared NetworkSwitcher + WalletMenu in the HanzoHeader

Puts hanzo.chat on the ONE hanzo.network standard (same model as the
hanzo CLI, console, desktop, app): @hanzo/ui/network + @hanzo/ui/wallet
via the npm alias @hanzo/ui -> @hanzo/ui-shadcn@^5.7.3 (the 5.x line
continued under its post-v8-rename name; existing imports untouched).

- Nav/NetworkWallet: the surface adapter — injected EIP-1193 wallet
  (non-custodial, zero key material) pinned to the selected network env
  (mainnet 36963 / testnet 36964 / devnet 36965 / local / custom).
- Mounted via the HanzoHeader headerRight slot; dark-scoped to match the
  monochrome bar.
- tailwind: scan the shared components in node_modules; add the missing
  popover/destructive tokens (fallback to background/foreground vars).

vite build green; tsc baseline unchanged (1014 pre-existing errors on
clean main, zero added — upstream CI gates vite build only).

* chore(chat): bump @hanzo/ui-shadcn 5.7.3 -> 5.7.4 (genesis-canonical chain IDs)

Picks up the corrected network set (testnet 36962, devnet 36964, local
1337). vite build green.

---------

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-08 09:12:23 -07:00
zeekay 31af61cb8b Merge remote-tracking branch 'origin/main'
# Conflicts:
#	api/server/middleware/__tests__/enforceGuestScope.spec.js
#	api/server/middleware/enforceGuestScope.js
#	api/server/services/guestConfig.js
2026-07-08 06:05:49 -07:00
212ba7d822 feat(mobile): honor server-driven auto-routing defaults from admin (#66)
Mirror the web client's server-driven Smart Routing onto the mobile
surface. On boot (authed), fetch the SAME backend route the web client
uses — GET /v1/chat/routing-defaults — fail-soft to { available: false }
so any error keeps today's local-toggle-only behavior.

- lib/routing.ts: mirror of web resolveSmartRouting (pure) plus org
  derivation helpers and a module cache for the non-React api layer.
- lib/settings.ts: smart-routing pref is now nullable (null === follow
  org default; true/false === explicit user choice that wins).
- lib/api.ts: fetchRoutingDefaults() boot fetch; chat() resolves the
  effective model from pref + cached org defaults.
- App.tsx: authed AppShell fetches defaults once, caches + holds state.
- Usage.tsx: toggle uses resolveSmartRouting; locks off with copy
  'Disabled for your organization' when auto_routing_active is false.

Resolver is mirrored (not imported) across app boundaries, matching the
three web apps; web copy carries the jest coverage. Typecheck + build
green.

Co-authored-by: Hanzo AI <ai@hanzo.ai>
2026-07-07 23:31:40 -07:00
zeekay 16745e26fa fix: true-black #000 chat body canvas (was #212121 gray) — DESIGN.md §4 2026-07-07 22:24:03 -07:00
e661f4c0b6 feat(routing): honor server-driven auto-routing defaults from admin (#65)
New conversations resolve smart routing from the org's server-driven
defaults (GET /v1/chat/routing-defaults proxying cloud's
/v1/get-routing-defaults on-behalf-of) with the local toggle as a
nullable user override (null = follow org default). Fail-soft: an older
cloud-api (404), network error, or non-ok wrapper behaves exactly as
today (local preference only). When auto_routing_active is false the
toggle is locked with honest copy.

Effective state is one pure function, resolveSmartRouting
(client/src/utils/endpoints.ts), unit-tested in endpoints.spec.ts.

Co-authored-by: Hanzo AI <ai@hanzo.ai>
2026-07-07 18:40:42 -07:00
Hanzo AI 9e5e158145 feat(routing): user-facing Smart Routing toggle (web + mobile)
Web: 'Smart routing' toggle in the Usage settings tab, persisted via the
smartRouting localStorage recoil atom. When on, new conversations on the
Hanzo endpoint default to model "auto" (gateway routes each prompt to the
best/cheapest capable model; billed as whatever serves it). Scoped to the
Hanzo house endpoint and only applied to defaults — an explicit model pick
or a non-Hanzo provider family is never rewritten. Message header surfaces
the served model (msg.model) when a routed convo echoes it. Docs link +
en localization strings.

Mobile: same toggle on the Usage screen, persisted to localStorage via a
small settings helper; flips chat between VITE_CHAT_MODEL and "auto" in
lib/api.ts.
2026-07-07 17:49:06 -07:00
zeekayandClaude Opus 4.8 c1df3816e3 chat: tier-aware guest model deep-links (GUEST_MODELS allowlist)
Guest deep-links (hanzo.chat/?endpoint=Hanzo&model=zen4-max) honored flexibly:
allowlisted models run free (within GUEST_MESSAGE_MAX); off-list/paid models return
402 MODEL_REQUIRES_LOGIN so the client opens login. Back-compat; fail-closed preserved.
guestConfig GUEST_MODELS allowlist + resolveGuestModel; enforceGuestScope honors it;
buildGuestModelsConfig exposes it. 19 tests pass. Client wiring + GUEST_MODELS env + deploy remain.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:47:41 -07:00
Hanzo AI 77f768f7fa feat(usage): unified per-user AI Usage settings tab
Backend: GET /api/usage (requireJwtAuth) aggregates this user's prompt+
completion Transaction spend over today/7d/30d plus a per-model breakdown,
enriched with the org tier from CommerceClient. Response mirrors the
@hanzo/usage UsageSnapshot shape (providerId 'hanzo', totals, providerCost)
for one wire format across Hanzo products.

Frontend: new Usage settings tab beside Balance, reusing the UsageBar idiom;
useGetUserUsage hook follows useGetUserBalance; en localization strings added.
2026-07-07 15:31:03 -07:00
Hanzo AI 885f6ec33c feat(mobile): scaffold Hanzo AI mobile app (Tauri v2 + React 19 + @hanzo/gui)
Self-contained mobile/ app (not in any workspace):
- Tauri v2 shell (productName "Hanzo AI", id ai.hanzo.chat) with fs + http
  plugins, scoped capabilities (fs: ~/.codex ~/.claude ~/.hanzo; http:
  chatgpt.com, api.anthropic.com, api.hanzo.ai, claude.ai, hanzo.chat).
- React 19 + Vite + @hanzo/gui (createGui + defaultConfig from
  @hanzogui/config/v5). Three state-nav screens: Chat, Sessions, Usage.
- Chat -> POST /api/agents/v1/chat/completions (OpenAI-compatible);
  Sessions -> GET /api/convos; both Bearer-auth. Usage via @hanzo/usage
  (createTauriHost + useUsage), connect empty-state outside Tauri.
- Bearer token via VITE_CHAT_TOKEN or paste screen (localStorage); IAM TODO.
- Rust engine/node feature flags OFF by default; cfg-gated engine_status +
  device_register stubs pointing at hanzoai/engine + hanzoai/node.

vite build green; cargo check green (default + engine,node).
2026-07-07 15:29:38 -07:00
hanzo-dev 83554c557d Merge branch 'feat/unified-iam-tenant-auth' 2026-07-07 10:43:17 -07:00
hanzo-dev 98d0dad34b feat(auth): unified IAM user-token tenancy; delete guest/shared-key cruft 2026-07-07 10:42:29 -07:00
zeekayandClaude Opus 4.8 44956e86b9 fix(test): faithful winston.format mock so data-schemas logger loads
The api jest winston mock returned the raw transform fn from
`winston.format(fn)`, so any `redactFormat()` call ran `fn(undefined)` and
crashed at `info.level`. `@librechat/data-schemas`'s own `config/winston`
calls `redactFormat()` at import time, so every backend suite that pulls in
`createModels`/`logger` failed to run ("Cannot read properties of undefined
(reading 'level')" at data-schemas parsers.ts:63).

Make the mock faithful to winston's contract: `format(fn)` returns a factory
that yields a Format instance (`{ transform: fn }`) — it never invokes the
transform at construction. On the models/strategies/Config slice this turns
20 fail-to-run suites into 20 passing (tests running 174 -> 681); the
level-throw is fully gone. Remaining failures are unrelated pre-existing
issues (DB-backed model tests, azureOpenAI config, role permissions).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 09:59:00 -07:00
hanzo-dev 167108dca5 release(0.9.17): P1 session/token/transaction store-aware fix + frontend build-hang fix 2026-07-04 21:10:29 -07:00
hanzo-dev c57194b535 Merge commit '8b4a7a6b4b' into integrate-0917
# Conflicts:
#	packages/data-schemas/dist/index.cjs.map
#	packages/data-schemas/dist/index.es.js.map
2026-07-04 20:11:07 -07:00
hanzo-dev 6cab482687 fix(store/sqlite): decouple store types from better-sqlite3 driver types — fixes frontend build hang
The node:sqlite → better-sqlite3-multiple-ciphers driver swap made the store's
public type surface (SqliteHandle.db, DocModel.db, openDatabase) reference the
driver's shipped `Database.Database` type. That type is large and conditional-
generic-heavy (`prepare<...>` → `Statement<...>`). Because @hanzochat/api imports
@librechat/data-schemas' public types (253 files), the driver types entered api's
TS program and the checker's structural-relation pass exploded (recursiveTypeRelatedTo
/ instantiateType depth-limit storm): api's rollup type-check ran >27 min and CI
canceled it. Pre-swap, node:sqlite's DatabaseSync was absent from @types/node@20 so
it resolved to `any` — trivial surface, build completed in ~3 min.

Fix: declare the minimal better-sqlite3 surface the store actually uses
(exec/prepare/pragma/close, run/get/all) as a local `SqliteDatabase`/`SqliteStatement`
interface and use it in place of `Database.Database`; cast the lazy require to the
minimal ctor. Runtime is unchanged — the native handle satisfies the contract
structurally. Rebuilt data-schemas dist.

Local proof (Node 20):
- api tsc --noEmit: >27min/canceled -> 151s
- api rollup build (--max-old-space-size=6144, as Dockerfile): 98s, emits dist
- pnpm run frontend: completes end-to-end, exit 0
2026-07-04 20:02:26 -07:00
hanzo-dev 8b4a7a6b4b fix(api/models): route Transaction + Role creation through the store-aware .create()
The prod hand-written model layer (api/models/*, separate from the data-schemas
methods) still constructed mongoose documents, which throw under the CHAT_STORE_SQLITE
flip where these models resolve to the SQLite DocModel/DualWriteModel:

- api/models/Transaction.js (createTransaction / createStructuredTransaction /
  createAutoRefillTransaction): 'new Transaction(txData); ...; await transaction.save()'
  -> a persistTransaction(txData, calculate) helper that runs the calculator FIRST
  (it mutates rate/tokenValue/rateDetail in place, so ordering matters), strips the
  non-schema calculator inputs (endpointTokenConfig/inputTokenCount/rateDetail) to
  match mongoose's on-save strip, then Transaction.create(). Balance math, the
  returned {rate,user,balance,[tokenType]} and result.transaction are unchanged.
- api/models/Role.js getRoleByName: 'new Role(defaults).save()' -> Role.create();
  '.toObject()' is present on the created doc across all backends. The name-required
  guard is untouched. Prevents 'Role is not a constructor' breaking RBAC self-heal.

Same bug class as the session P1; these are the actual prod paths (a DRY smell vs
the data-schemas methods — consolidation deferred). api-only; no data-schemas dist
change. The existing api Transaction/Role specs cannot run in this env due to a
PRE-EXISTING logger-mock harness break (reproduced on clean origin/main); the
identical persistTransaction pattern is proven green in data-schemas on both the
mongoose and SQLite/DualWrite stores.
2026-07-04 19:59:21 -07:00
hanzo-dev ca72857533 build(data-schemas): rebuild dist; annotate return types to unblock declaration emit
Typing the store-resolved model as Model<IUser>/Model<ITransaction> (matching the
28 migrated domains) ballooned the inferred return of the methods that had NO
explicit return type — searchUsers, and getTransactions/deleteTransactions/
deleteBalances/createAutoRefillTransaction — past TS's serialization limit
(TS7056), so their factory .d.ts stopped emitting. Add explicit return types to
exactly those methods (pure type annotations + two no-op .lean() casts; zero
runtime change). Declaration emit is restored.

Rebuild the shipped dist (npm run build, exit 0): bundles + session/token/user
type declarations now carry the store-aware DataHandle signatures. Transaction
methods are internal (not in AllMethods), so they are tree-shaken from the bundle
and their .d.ts is not part of the shipped surface.
2026-07-04 19:48:40 -07:00
hanzo-dev 3595b2af22 test(data-schemas/auth): prove Session/Token/User/Transaction against the SQLite + DualWrite store
Adds authStore.sqlite.spec.ts (isolated native-sqlite spec): the REAL production
factories run against both served shapes the flip produces —
  1) DualWriteModel (SQLite primary + SQLite mirror), and
  2) the real createModels() wiring under CHAT_STORE_SQLITE (DocModel served).

Covers the exact regression (createSession no longer throws 'Session is not a
constructor'): createSession → findSession(refreshToken|sessionId) →
updateExpiration → generateRefreshToken(rotate) → deleteSession →
deleteAllUserSessions → countActiveSessions; createToken → findToken →
updateToken → deleteTokens (verify/reset + email normalization); createUser →
findUser → getUserById → updateUser → deleteUserById + Balance seeding;
createTransaction (debit + non-schema-field stripping) / createStructuredTransaction
/ createAutoRefillTransaction → getTransactions / findBalanceByUser /
deleteTransactions. Asserts refresh tokens stay hashed and the served model is
never a mongoose Model. Registered in test/ci.mjs ISOLATED (native driver).

14/14 green; the 101 pre-existing mongoose specs for the touched files still pass.
2026-07-04 19:32:51 -07:00
hanzo-dev 0359f4c8fb fix(data-schemas/auth): route User+Transaction onto DataHandle; drop storeHandle cast
Same store-bypass class as Session: transaction.ts constructed documents with
new Transaction() + .save() (latent P1 — breaks identically once Transaction is
SQLite-served); user.ts + transaction.ts resolved their model via mongoose.models.
Refactor both factories to take a DataHandle and resolve via handle.models.<Name>.

Transaction create/auto-refill/structured now persist via the bounded .create()
(persistTransaction), stripping the non-schema calculator inputs
(endpointTokenConfig/inputTokenCount/rateDetail) so the persisted document is
byte-identical to the mongoose path (which strips non-schema paths on save);
balance math + returned TransactionResult unchanged. User already used .create()/
.findByIdAndUpdate(); only its model resolution moves.

createMethods now hands the real DataHandle to all four (User/Session/Token) —
the storeHandle mongoose-cast hack is removed; every domain resolves the store
uniformly.
2026-07-04 19:25:21 -07:00
hanzo-dev 04d041c2f4 fix(data-schemas/auth): route Session+Token methods through the store-aware DataHandle
Cold logins have been failing since Session was flipped to the SQLite store
(CHAT_STORE_SQLITE): createSession threw 'Session is not a constructor' because
session.ts used the mongoose-document constructor (new Session()) + doc.save(),
which the DocModel/DualWriteModel served under the flip do not implement.

Refactor createSessionMethods + createTokenMethods to take a DataHandle (the same
seam the 28 migrated domains use) and resolve their model via handle.models.<Name>.
Session create/rotate now use the bounded Model API: createSession generates the
_id up front (handle.Types.ObjectId), signs the refresh token bound to it, and
persists via .create(); generateRefreshToken (rotation) persists via .updateOne();
updateExpiration via .findByIdAndUpdate(). Refresh tokens stay hashed (hashToken),
JWT claims + expiry math unchanged, no session fixation. Token methods already used
the bounded API; only their model resolution moves off mongoose.models.

storeHandle (mongoose-cast) in createMethods still feeds these factories unchanged
(it is structurally a DataHandle); the caller cleanup follows in the next commit.
2026-07-04 19:24:49 -07:00
hanzo-devandz 3fe63b68d6 ci(data-schemas): isolate native-sqlite specs one-file-per-process; v0.9.16
The 15 specs that instantiate the native better-sqlite3 driver corrupt each
other under a shared jest worker: jest gives each spec file its own JS module
realm while the native addon is process-cached per worker, so cross-file state
breaks sibling in-memory databases (driver-agnostic — reproduces with mainstream
better-sqlite3; a compound json_extract UNIQUE stops firing). Prod is unaffected
(one realm, one shared handle for the process lifetime).

test:ci now runs test/ci.mjs: the normal coverage pass for everything except the
native-driver set (via testPathIgnorePatterns), then each of those 15 specs in
its own jest process, exiting non-zero on any failure. NOT --runInBand /
maxWorkers=1 (a single shared worker still corrupts). No test weakening, no skips.

Bump chat 0.9.15 -> 0.9.16 (patch).
2026-07-04 15:55:19 -07:00
hanzo-devandz b6e5d1082b fix(store/sqlite): close shared handle on rekey + test teardown (real leak)
sharedSqliteHandle() reassigned sharedHandle on a collection-key change without
closing the prior connection — a latent native-handle leak on any rekey. Close
it before replacing, and export closeSharedSqliteHandle() so storeRegistry.spec
(rekeys twice) and tenantIsolation.coverage.spec tear the handle down instead of
leaking it past the file.

NOTE: this fixes the real leak the review identified, but does NOT resolve the
cross-file store-spec flake. That flake is a separate, deeper issue (see report):
driver-agnostic (reproduces with mainstream better-sqlite3 too), isolated to the
store's create path, timing/heap-sensitive (any probe masks it), and NOT fixed by
closing handles/GC/statement-cache/wrapper-pinning. Every store suite passes in
its own process; production (single long-lived handle, one module realm) is
unaffected.
2026-07-04 15:55:19 -07:00
hanzo-devandz 1990fa4072 fix(store/sqlite): swap node:sqlite→better-sqlite3-multiple-ciphers (Node 20) + honor tenant sentinel in debug log
node:sqlite (DatabaseSync) is a Node 22+ builtin; prod runs Node 20 (Alpine),
so require('node:sqlite') threw ERR_UNKNOWN_BUILTIN_MODULE, cascading through
createModels → applySqliteOverrides → openDatabase and breaking the built dist
("createModels is not a function"). Swap to better-sqlite3-multiple-ciphers
12.11.1 (engines include 20.x, synchronous drop-in, SQLCipher AES-256 at rest —
the Node embodiment of the hanzoai/sqlite contract).

- data-schemas dep + root pnpm.onlyBuiltDependencies allowlist (native addon);
  rollup externalizes the driver.
- openDatabase(): lazy require of the driver, identical WAL/synchronous/
  busy_timeout/foreign_keys pragmas. Wire the CHAT_SQLITE_KEY encryption seam
  (SQLCipher cipher/legacy/key pragmas applied before any page-touching
  statement; 64-hex validated, fail-closed; key/path never logged). KMS/CEK
  derivation is Milestone 2.
- DocModel: retype db to the driver's Database instance; binding/return parity
  already correct (booleans→1/0, dates→ISO, TEXT reads only). engine untouched
  (27/27 held).
- parsers: restore the structured-logging-context impl dropped in an upstream
  merge (spec #13110 landed without its parsers.ts) — appendRequestContext for
  non-debug lines and drop the __SYSTEM__ tenant sentinel from debug traversal.
- models: register the SystemGrant model in createModels (schema/model/methods/
  SQLite-spec + coverage guard all existed; only the registration was dangling).

Scrub all node:sqlite/DatabaseSync references from src (comments + spec names).
2026-07-04 15:55:19 -07:00
2020 changed files with 22269 additions and 78613 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ services:
# - /var/run/docker.sock:/var/run/docker.sock
environment:
- HOST=0.0.0.0
- MONGO_URI=mongodb://mongodb:27017/LibreChat
- MONGO_URI=mongodb://mongodb:27017/Chat
# - OPENAI_REVERSE_PROXY=http://host.docker.internal:8070/v1
- MEILI_HOST=http://meilisearch:7700
+1 -1
View File
@@ -29,7 +29,7 @@ services:
gitnexus:
# Override via GITNEXUS_IMAGE in /opt/gitnexus/.env to use a fork or
# a pinned version tag like :v1.5.3 for reproducible rollbacks.
image: ${GITNEXUS_IMAGE:-ghcr.io/danny-avila/librechat-gitnexus:latest}
image: ${GITNEXUS_IMAGE:-ghcr.io/danny-avila/chat-gitnexus:latest}
container_name: gitnexus
restart: unless-stopped
networks:
+3 -3
View File
@@ -14,10 +14,10 @@ export NODE_OPTIONS="${NODE_OPTIONS:---max-old-space-size=1280}"
# metadata pointer without touching the index data.
#
# Registration failure handling:
# - main (LibreChat) and dev (LibreChat-dev) are critical. If either
# - main (Chat) and dev (Chat-dev) are critical. If either
# fails to register, exit 1 so docker marks the container unhealthy
# and the deploy workflow's readiness check surfaces the error.
# - PR indexes (LibreChat-pr-*) are best-effort. A corrupt PR index
# - PR indexes (Chat-pr-*) are best-effort. A corrupt PR index
# shouldn't take the whole server down.
if [ -d /indexes ]; then
for dir in /indexes/*/; do
@@ -27,7 +27,7 @@ if [ -d /indexes ]; then
echo "Registering index: $name"
if ! gitnexus index "$dir" --allow-non-git; then
case "$name" in
LibreChat|LibreChat-dev)
Chat|Chat-dev)
echo "ERROR: failed to register critical index $name" >&2
exit 1
;;
+5 -5
View File
@@ -484,7 +484,7 @@ ALLOW_PASSWORD_RESET=false
# server-side to the free model only and rejected from every other route.
ALLOW_GUEST_CHAT=false
# GUEST_MESSAGE_MAX=3 # free messages per IP before login is required
# GUEST_ENDPOINT=Hanzo # custom endpoint name from librechat.yaml (api.hanzo.ai)
# GUEST_ENDPOINT=Hanzo # custom endpoint name from chat.yaml (api.hanzo.ai)
# GUEST_MODEL=zen3-nano # free Zen model id guests are pinned to
# GUEST_TOKEN_EXPIRY=3600000 # guest JWT lifetime in ms (default 1h)
# GUEST_TOKEN_MAX=20 # max guest sessions issued per IP per window
@@ -775,7 +775,7 @@ HELP_AND_FAQ_URL=https://hanzo.ai/chat
# Use environment variable name for dynamic prefix (recommended for cloud deployments)
# REDIS_KEY_PREFIX_VAR=K_REVISION
# Or use static prefix directly
# REDIS_KEY_PREFIX=librechat
# REDIS_KEY_PREFIX=chat
# Redis connection limits
# REDIS_MAX_LISTENERS=40
@@ -839,8 +839,8 @@ OPENWEATHER_API_KEY=
# "Run Code" routes through the Hanzo unified backend (api.hanzo.ai/v1/exec),
# which executes in a Hanzo sandbox. execute_code POSTs {BASEURL}/exec with
# X-API-Key. https://hanzo.ai/docs/chat/code-interpreter
# LIBRECHAT_CODE_BASEURL=https://api.hanzo.ai/v1
# LIBRECHAT_CODE_API_KEY=your-key # prod: KMS chat-secrets/LIBRECHAT_CODE_API_KEY
# CHAT_CODE_BASEURL=https://api.hanzo.ai/v1
# CHAT_CODE_API_KEY=your-key # prod: KMS chat-secrets/CHAT_CODE_API_KEY
#======================#
# Web Search #
@@ -848,7 +848,7 @@ OPENWEATHER_API_KEY=
# Web Search routes through the Hanzo unified backend ONLY — no external SaaS.
# The searxng provider + firecrawl scraper both point at api.hanzo.ai/v1/websearch
# (Hanzo metasearch + Hanzo Crawl). See librechat.yaml `webSearch`.
# (Hanzo metasearch + Hanzo Crawl). See chat.yaml `webSearch`.
# https://hanzo.ai/docs/chat/features/web_search
# SEARXNG_INSTANCE_URL=https://api.hanzo.ai/v1/websearch
# FIRECRAWL_API_URL=https://api.hanzo.ai/v1/websearch
+20 -20
View File
@@ -1,10 +1,10 @@
#=====================================================================#
# LibreChat Configuration #
# Chat Configuration #
#=====================================================================#
# Please refer to the reference documentation for assistance #
# with configuring your LibreChat environment. #
# with configuring your Chat environment. #
# #
# https://www.librechat.ai/docs/configuration/dotenv #
# https://hanzo.ai/docs/chat/configuration/dotenv #
#=====================================================================#
#==================================================#
@@ -14,7 +14,7 @@
HOST=localhost
PORT=3080
MONGO_URI=mongodb://127.0.0.1:27017/LibreChat
MONGO_URI=mongodb://127.0.0.1:27017/Chat
DOMAIN_CLIENT=http://localhost:3080
DOMAIN_SERVER=http://localhost:3080
@@ -52,7 +52,7 @@ DEBUG_CONSOLE=false
#===============#
# Use an absolute path, a relative path, or a URL
# CONFIG_PATH="/alternative/path/to/librechat.yaml"
# CONFIG_PATH="/alternative/path/to/chat.yaml"
#===================================================#
# Endpoints #
@@ -63,9 +63,9 @@ DEBUG_CONSOLE=false
PROXY=
#===================================#
# Known Endpoints - librechat.yaml #
# Known Endpoints - chat.yaml #
#===================================#
# https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints
# https://hanzo.ai/docs/chat/configuration/chat_yaml/ai_endpoints
# ANYSCALE_API_KEY=
# APIPIE_API_KEY=
@@ -96,8 +96,8 @@ ANTHROPIC_API_KEY=user_provided
#============#
# Note: these variables are DEPRECATED
# Use the `librechat.yaml` configuration for `azureOpenAI` instead
# You may also continue to use them if you opt out of using the `librechat.yaml` configuration
# Use the `chat.yaml` configuration for `azureOpenAI` instead
# You may also continue to use them if you opt out of using the `chat.yaml` configuration
# AZURE_OPENAI_DEFAULT_MODEL=gpt-3.5-turbo # Deprecated
# AZURE_OPENAI_MODELS=gpt-3.5-turbo,gpt-4 # Deprecated
@@ -207,7 +207,7 @@ ASSISTANTS_API_KEY=user_provided
# The models for Azure Assistants are also determined by your Azure OpenAI configuration.
# More info, including how to enable use of Assistants with Azure here:
# https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints/azure#using-assistants-with-azure
# https://hanzo.ai/docs/chat/configuration/chat_yaml/ai_endpoints/azure#using-assistants-with-azure
#============#
# Plugins #
@@ -320,7 +320,7 @@ TTS_API_KEY=
#==================================================#
# RAG #
#==================================================#
# More info: https://www.librechat.ai/docs/configuration/rag_api
# More info: https://hanzo.ai/docs/chat/configuration/rag_api
# RAG_OPENAI_BASEURL=
# RAG_OPENAI_API_KEY=
@@ -513,7 +513,7 @@ EMAIL_ALLOW_SELFSIGNED=
EMAIL_USERNAME=
EMAIL_PASSWORD=
EMAIL_FROM_NAME=
EMAIL_FROM=noreply@librechat.ai
EMAIL_FROM=noreply@hanzo.ai
#========================#
# Mailgun API #
@@ -522,7 +522,7 @@ EMAIL_FROM=noreply@librechat.ai
# MAILGUN_API_KEY=your-mailgun-api-key
# MAILGUN_DOMAIN=mg.yourdomain.com
# EMAIL_FROM=noreply@yourdomain.com
# EMAIL_FROM_NAME="LibreChat"
# EMAIL_FROM_NAME="Chat"
# # Optional: For EU region
# MAILGUN_HOST=https://api.eu.mailgun.net
@@ -572,16 +572,16 @@ ALLOW_SHARED_LINKS_PUBLIC=true
# STATIC_CACHE_MAX_AGE=172800
# STATIC_CACHE_S_MAX_AGE=86400
# If you have another service in front of your LibreChat doing compression, disable express based compression here
# If you have another service in front of your Chat doing compression, disable express based compression here
# DISABLE_COMPRESSION=true
#===================================================#
# UI #
#===================================================#
APP_TITLE=LibreChat
APP_TITLE=Chat
# CUSTOM_FOOTER="My custom footer"
HELP_AND_FAQ_URL=https://librechat.ai
HELP_AND_FAQ_URL=https://hanzo.chat
# SHOW_BIRTHDAY_ICON=true
@@ -630,11 +630,11 @@ HELP_AND_FAQ_URL=https://librechat.ai
OPENWEATHER_API_KEY=
#====================================#
# LibreChat Code Interpreter API #
# Chat Code Interpreter API #
#====================================#
# https://code.librechat.ai
# LIBRECHAT_CODE_API_KEY=your-key
# https://code.hanzo.ai
# CHAT_CODE_API_KEY=your-key
#======================#
# Web Search #
@@ -644,7 +644,7 @@ OPENWEATHER_API_KEY=
# Omit values to allow user to provide them.
# For more information on configuration values, see:
# https://librechat.ai/docs/features/web_search
# https://hanzo.ai/docs/chat/features/web_search
# Search Provider (Required)
# SERPER_API_KEY=your_serper_api_key
+7 -7
View File
@@ -6,15 +6,15 @@ OPENAI_API_KEY=sk-hanzo-your-api-key-here
OPENAI_BASE_URL=https://api.hanzo.ai/v1
# Code Interpreter ("Run Code") — Hanzo unified backend.
# LibreChat's execute_code tool POSTs {LIBRECHAT_CODE_BASEURL}/exec with header
# Chat's execute_code tool POSTs {CHAT_CODE_BASEURL}/exec with header
# X-API-Key. cloud-api serves /v1/exec (-> sandboxed executor). The key is
# KMS-sourced (chat-secrets/LIBRECHAT_CODE_API_KEY). These are the ONLY vars
# LibreChat reads — the old CODE_EXECUTION_ENDPOINT/RUNTIME_API_KEY were dead.
LIBRECHAT_CODE_BASEURL=https://api.hanzo.ai/v1
LIBRECHAT_CODE_API_KEY=set-in-KMS-chat-secrets
# KMS-sourced (chat-secrets/CHAT_CODE_API_KEY). These are the ONLY vars
# Chat reads — the old CODE_EXECUTION_ENDPOINT/RUNTIME_API_KEY were dead.
CHAT_CODE_BASEURL=https://api.hanzo.ai/v1
CHAT_CODE_API_KEY=set-in-KMS-chat-secrets
# Web Search — Hanzo unified backend (searxng + firecrawl compat over Hanzo
# search + crawl). See librechat.yaml `webSearch`. One shared service key.
# search + crawl). See chat.yaml `webSearch`. One shared service key.
SEARXNG_INSTANCE_URL=https://api.hanzo.ai/v1/websearch
FIRECRAWL_API_URL=https://api.hanzo.ai/v1/websearch
WEBSEARCH_API_KEY=set-in-KMS-chat-secrets
@@ -85,5 +85,5 @@ DEBUG_LOGGING=false
# ====== NOTES ======
# 1. Get your Hanzo API key from https://hanzo.ai/dashboard
# 2. All AI providers are accessed through api.hanzo.ai -- no individual keys needed
# 3. Model list is configured in librechat.yaml under endpoints.custom (Hanzo endpoint)
# 3. Model list is configured in chat.yaml under endpoints.custom (Hanzo endpoint)
# 4. zen4 is the default model for new conversations
+1 -1
View File
@@ -38,7 +38,7 @@ Project maintainers have the right and responsibility to remove, edit, or reject
- Install [MongoDB Community Edition](https://www.mongodb.com/docs/manual/administration/install-community/), ensure that `mongosh` connects to your local instance.
- Run: `npx install playwright`, then `npx playwright install`.
- Copy `config.local`: `cp e2e/config.local.example.ts e2e/config.local.ts`.
- Copy `librechat.yaml`: `cp librechat.example.yaml librechat.yaml`.
- Copy `chat.yaml`: `cp chat.example.yaml chat.yaml`.
- Run: `npm run e2e`.
## 2. Development Notes
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
CHART_PATH="${CHART_PATH:-helm/librechat/Chart.yaml}"
CHART_PATH="${CHART_PATH:-helm/chat/Chart.yaml}"
DEFAULT_BRANCH="${DEFAULT_BRANCH:-main}"
BASE_REF="${BASE_REF:-refs/remotes/origin/${DEFAULT_BRANCH}}"
BACKFILL_FROM_VERSION="${BACKFILL_FROM_VERSION:-1.9.0}"
+3 -3
View File
@@ -67,11 +67,11 @@ jobs:
- name: Run unit tests
run: cd api && pnpm run test:ci
- name: Run librechat-data-provider unit tests
- name: Run @hanzochat/data-provider unit tests
run: cd packages/data-provider && pnpm run test:ci
- name: Run @librechat/data-schemas unit tests
- name: Run @hanzochat/data-schemas unit tests
run: cd packages/data-schemas && pnpm run test:ci
- name: Run @librechat/api unit tests
- name: Run @hanzochat/api unit tests
run: cd packages/api && pnpm run test:ci
+2 -2
View File
@@ -1,4 +1,4 @@
name: Publish `@librechat/client` to NPM
name: Publish `@hanzochat/client` to NPM
on:
push:
@@ -47,7 +47,7 @@ jobs:
working-directory: packages/client
run: |
PACKAGE_VERSION=$(node -p "require('./package.json').version")
PUBLISHED_VERSION=$(npm view @librechat/client version 2>/dev/null || echo "0.0.0")
PUBLISHED_VERSION=$(npm view @hanzochat/client version 2>/dev/null || echo "0.0.0")
if [ "$PACKAGE_VERSION" = "$PUBLISHED_VERSION" ]; then
echo "No version change, skipping publish"
echo "skip=true" >> $GITHUB_OUTPUT
+1 -1
View File
@@ -1,4 +1,4 @@
name: Publish `librechat-data-provider` to NPM
name: Publish `@hanzochat/data-provider` to NPM
on:
push:
+2 -2
View File
@@ -1,4 +1,4 @@
name: Publish `@librechat/data-schemas` to NPM
name: Publish `@hanzochat/data-schemas` to NPM
on:
push:
@@ -47,7 +47,7 @@ jobs:
working-directory: packages/data-schemas
run: |
PACKAGE_VERSION=$(node -p "require('./package.json').version")
PUBLISHED_VERSION=$(npm view @librechat/data-schemas version 2>/dev/null || echo "0.0.0")
PUBLISHED_VERSION=$(npm view @hanzochat/data-schemas version 2>/dev/null || echo "0.0.0")
if [ "$PACKAGE_VERSION" = "$PUBLISHED_VERSION" ]; then
echo "No version change, skipping publish"
echo "skip=true" >> $GITHUB_OUTPUT
+3 -3
View File
@@ -14,7 +14,7 @@ jobs:
deploy:
runs-on: hanzo-deploy-linux-amd64
if: |
github.repository == 'danny-avila/LibreChat' &&
github.repository == 'danny-avila/Chat' &&
(github.event_name == 'workflow_dispatch' ||
(github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'dev'))
steps:
@@ -34,10 +34,10 @@ jobs:
run: |
ssh ${DO_USER}@${DO_HOST} << EOF
sudo -i -u danny bash << 'EEOF'
cd ~/LibreChat && \
cd ~/Chat && \
git fetch origin main && \
sudo npm run stop:deployed && \
sudo docker images --format "{{.Repository}}:{{.ID}}" | grep -E "lc-dev|librechat" | cut -d: -f2 | xargs -r sudo docker rmi -f || true && \
sudo docker images --format "{{.Repository}}:{{.ID}}" | grep -E "lc-dev|chat" | cut -d: -f2 | xargs -r sudo docker rmi -f || true && \
sudo npm run update:deployed && \
git checkout dev && \
git pull origin dev && \
+2 -2
View File
@@ -27,10 +27,10 @@ jobs:
include:
- target: api-build
file: Dockerfile.multi
image_name: librechat-dev-api
image_name: chat-dev-api
- target: node
file: Dockerfile
image_name: librechat-dev
image_name: chat-dev
steps:
# Check out the repository
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
NODE_OPTIONS: '--max-old-space-size=${{ secrets.NODE_MAX_OLD_SPACE_SIZE || 6144 }}'
NODE_ENV: CI
SEARCH: false
MONGO_URI: mongodb://localhost:27017/librechat-test
MONGO_URI: mongodb://localhost:27017/chat-test
JWT_SECRET: e2e-test-jwt-secret
JWT_REFRESH_SECRET: e2e-test-jwt-refresh-secret
CREDS_KEY: e2e-test-creds-key-32chars00000000
+1 -1
View File
@@ -74,7 +74,7 @@ jobs:
run: |
ssh -i ~/.ssh/deploy_key "$SSH_USER@$SSH_HOST" PR_NUM="$PR_NUM" bash <<'REMOTE'
set -e
TARGET="/opt/gitnexus/indexes/LibreChat-pr-$PR_NUM"
TARGET="/opt/gitnexus/indexes/Chat-pr-$PR_NUM"
if [ -d "$TARGET" ]; then
echo "Removing $TARGET"
rm -rf "$TARGET"
+11 -11
View File
@@ -71,7 +71,7 @@ permissions:
# cancel-in-progress so rapid pushes to the same ref coalesced but deploys
# targeting different refs ran in parallel. That had a data race: the
# prune-stale-indexes step computes its active_names up front, so if
# deploy A is rsyncing /opt/gitnexus/indexes/LibreChat-pr-12580 while
# deploy A is rsyncing /opt/gitnexus/indexes/Chat-pr-12580 while
# deploy B (started slightly later with a different ref) prunes, B can
# rm -rf a folder A is still uploading into.
#
@@ -85,7 +85,7 @@ concurrency:
env:
GITNEXUS_VERSION: '1.5.3'
IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/librechat-gitnexus
IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/chat-gitnexus
jobs:
# Rebuilds the long-lived image only when Dockerfile/entrypoint/extensions
@@ -200,8 +200,8 @@ jobs:
// --- main and dev branches ---
for (const [branch, name] of [
['main', 'LibreChat'],
['dev', 'LibreChat-dev'],
['main', 'Chat'],
['dev', 'Chat-dev'],
]) {
const artifactName = `gitnexus-index-${branch}`;
const fresh = await latestArtifact(artifactName);
@@ -260,11 +260,11 @@ jobs:
for (const { pr, artifactName, fresh } of keptPrs) {
serve.push({
name: `LibreChat-pr-${pr.number}`,
name: `Chat-pr-${pr.number}`,
artifactName,
runId: fresh.workflow_run.id,
});
core.info(`PR #${pr.number}: run ${fresh.workflow_run.id} -> LibreChat-pr-${pr.number}`);
core.info(`PR #${pr.number}: run ${fresh.workflow_run.id} -> Chat-pr-${pr.number}`);
}
if (evictedPrs.length) {
core.info(
@@ -305,7 +305,7 @@ jobs:
--name "$artifact" \
--dir "$target"; then
case "$name" in
LibreChat|LibreChat-dev)
Chat|Chat-dev)
echo "::error::Failed to download critical artifact $artifact"
exit 1
;;
@@ -438,7 +438,7 @@ jobs:
# is visible and the container isn't restarted with stale
# or missing data. PR indexes are best-effort.
case "$name" in
LibreChat|LibreChat-dev)
Chat|Chat-dev)
echo "::error::rsync failed for critical index $name — aborting deploy"
exit 1
;;
@@ -566,10 +566,10 @@ jobs:
const matrix = JSON.parse(process.env.MATRIX || '[]');
const triggerRunId = Number(process.env.TRIGGER_RUN_ID);
const match = matrix.find(
(m) => m.runId === triggerRunId && m.name.startsWith('LibreChat-pr-'),
(m) => m.runId === triggerRunId && m.name.startsWith('Chat-pr-'),
);
if (match) {
prNum = parseInt(match.name.replace('LibreChat-pr-', ''), 10);
prNum = parseInt(match.name.replace('Chat-pr-', ''), 10);
}
}
@@ -584,7 +584,7 @@ jobs:
`### GitNexus: ${ok ? '🚀 deployed' : '❌ deploy failed'}`,
'',
ok
? `The \`LibreChat-pr-${prNum}\` index is now live on the MCP server.`
? `The \`Chat-pr-${prNum}\` index is now live on the MCP server.`
: `The deploy failed — the previous index (if any) continues to be served.`,
`[Deploy run](${deployUrl})`,
].join('\n');
+12 -12
View File
@@ -30,9 +30,9 @@ jobs:
- name: Build Subchart Deps
run: |
cd helm/librechat
cd helm/chat
helm dependency build
cd ../librechat-rag-api
cd ../chat-rag-api
helm dependency build
- name: Get Chart Version
@@ -50,26 +50,26 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
# Run Helm OCI Charts Releaser
# This is for the librechat chart
- name: Release Helm OCI Charts for librechat
# This is for the chat chart
- name: Release Helm OCI Charts for chat
uses: appany/helm-oci-chart-releaser@v0.5.0
with:
name: librechat
repository: ${{ github.actor }}/librechat-chart
name: chat
repository: ${{ github.actor }}/chat-chart
tag: ${{ steps.chart-version.outputs.CHART_VERSION }}
path: helm/librechat
path: helm/chat
registry: ghcr.io
registry_username: ${{ github.actor }}
registry_password: ${{ secrets.GITHUB_TOKEN }}
# this is for the librechat-rag-api chart
- name: Release Helm OCI Charts for librechat-rag-api
# this is for the chat-rag-api chart
- name: Release Helm OCI Charts for chat-rag-api
uses: appany/helm-oci-chart-releaser@v0.5.0
with:
name: librechat-rag-api
repository: ${{ github.actor }}/librechat-chart
name: chat-rag-api
repository: ${{ github.actor }}/chat-chart
tag: ${{ steps.chart-version.outputs.CHART_VERSION }}
path: helm/librechat-rag-api
path: helm/chat-rag-api
registry: ghcr.io
registry_username: ${{ github.actor }}
registry_password: ${{ secrets.GITHUB_TOKEN }}
+2 -2
View File
@@ -15,10 +15,10 @@ jobs:
include:
- target: api-build
file: Dockerfile.multi
image_name: librechat-api
image_name: chat-api
- target: node
file: Dockerfile
image_name: librechat
image_name: chat
steps:
- name: Checkout
+2 -2
View File
@@ -39,14 +39,14 @@ jobs:
env:
BASE_REF: refs/remotes/origin/main
BACKFILL_FROM_VERSION: 1.9.0
CHART_PATH: helm/librechat/Chart.yaml
CHART_PATH: helm/chat/Chart.yaml
DEFAULT_BRANCH: main
DISPATCH_WORKFLOW: helmcharts.yml
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
RELEASE_EXISTING_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_existing_tag || '' }}
REPO_DIR: /tmp/librechat-sync
REPO_DIR: /tmp/chat-sync
TAG_PREFIX: chart-
steps:
- name: Fetch main and tags
+2 -2
View File
@@ -17,10 +17,10 @@ jobs:
include:
- target: api-build
file: Dockerfile.multi
image_name: librechat-api
image_name: chat-api
- target: node
file: Dockerfile
image_name: librechat
image_name: chat
steps:
# Check out the repository
+1 -1
View File
@@ -68,7 +68,7 @@ jobs:
run: pnpm run test:api
env:
NODE_ENV: test
MONGO_URI: mongodb://localhost:27017/librechat-test
MONGO_URI: mongodb://localhost:27017/chat-test
MEILI_HOST: http://localhost:7700
MEILI_MASTER_KEY: test_master_key
+28 -28
View File
@@ -117,52 +117,52 @@ jobs:
extract_deps_from_code "packages/client" packages_client_used_code.txt
extract_deps_from_code "packages/api" packages_api_used_code.txt
- name: Get @librechat/client dependencies
id: get-librechat-client-deps
- name: Get @hanzochat/client dependencies
id: get-chat-client-deps
run: |
if [[ -f "packages/client/package.json" ]]; then
# Get all dependencies from @librechat/client (dependencies, devDependencies, and peerDependencies)
# Get all dependencies from @hanzochat/client (dependencies, devDependencies, and peerDependencies)
DEPS=$(jq -r '.dependencies // {} | keys[]' packages/client/package.json 2>/dev/null || echo "")
DEV_DEPS=$(jq -r '.devDependencies // {} | keys[]' packages/client/package.json 2>/dev/null || echo "")
PEER_DEPS=$(jq -r '.peerDependencies // {} | keys[]' packages/client/package.json 2>/dev/null || echo "")
# Combine all dependencies
echo "$DEPS" > librechat_client_deps.txt
echo "$DEV_DEPS" >> librechat_client_deps.txt
echo "$PEER_DEPS" >> librechat_client_deps.txt
echo "$DEPS" > chat_client_deps.txt
echo "$DEV_DEPS" >> chat_client_deps.txt
echo "$PEER_DEPS" >> chat_client_deps.txt
# Also include dependencies that are imported in packages/client
cat packages_client_used_code.txt >> librechat_client_deps.txt
cat packages_client_used_code.txt >> chat_client_deps.txt
# Remove empty lines and sort
grep -v '^$' librechat_client_deps.txt | sort -u > temp_deps.txt
mv temp_deps.txt librechat_client_deps.txt
grep -v '^$' chat_client_deps.txt | sort -u > temp_deps.txt
mv temp_deps.txt chat_client_deps.txt
else
touch librechat_client_deps.txt
touch chat_client_deps.txt
fi
- name: Get @librechat/api dependencies
id: get-librechat-api-deps
- name: Get @hanzochat/api dependencies
id: get-chat-api-deps
run: |
if [[ -f "packages/api/package.json" ]]; then
# Get all dependencies from @librechat/api (dependencies, devDependencies, and peerDependencies)
# Get all dependencies from @hanzochat/api (dependencies, devDependencies, and peerDependencies)
DEPS=$(jq -r '.dependencies // {} | keys[]' packages/api/package.json 2>/dev/null || echo "")
DEV_DEPS=$(jq -r '.devDependencies // {} | keys[]' packages/api/package.json 2>/dev/null || echo "")
PEER_DEPS=$(jq -r '.peerDependencies // {} | keys[]' packages/api/package.json 2>/dev/null || echo "")
# Combine all dependencies
echo "$DEPS" > librechat_api_deps.txt
echo "$DEV_DEPS" >> librechat_api_deps.txt
echo "$PEER_DEPS" >> librechat_api_deps.txt
echo "$DEPS" > chat_api_deps.txt
echo "$DEV_DEPS" >> chat_api_deps.txt
echo "$PEER_DEPS" >> chat_api_deps.txt
# Also include dependencies that are imported in packages/api
cat packages_api_used_code.txt >> librechat_api_deps.txt
cat packages_api_used_code.txt >> chat_api_deps.txt
# Remove empty lines and sort
grep -v '^$' librechat_api_deps.txt | sort -u > temp_deps.txt
mv temp_deps.txt librechat_api_deps.txt
grep -v '^$' chat_api_deps.txt | sort -u > temp_deps.txt
mv temp_deps.txt chat_api_deps.txt
else
touch librechat_api_deps.txt
touch chat_api_deps.txt
fi
- name: Extract Workspace Dependencies
@@ -173,14 +173,14 @@ jobs:
local package_json=$1
local output_file=$2
# Get all workspace dependencies (starting with @librechat/)
# Get all workspace dependencies (starting with @hanzochat/)
if [[ -f "$package_json" ]]; then
local workspace_deps=$(jq -r '.dependencies // {} | to_entries[] | select(.key | startswith("@librechat/")) | .key' "$package_json" 2>/dev/null || echo "")
local workspace_deps=$(jq -r '.dependencies // {} | to_entries[] | select(.key | startswith("@hanzochat/")) | .key' "$package_json" 2>/dev/null || echo "")
# For each workspace dependency, get its dependencies
for dep in $workspace_deps; do
# Convert @librechat/api to packages/api
local workspace_path=$(echo "$dep" | sed 's/@librechat\//packages\//')
# Convert @hanzochat/api to packages/api
local workspace_path=$(echo "$dep" | sed 's/@chat\//packages\//')
local workspace_package_json="${workspace_path}/package.json"
if [[ -f "$workspace_package_json" ]]; then
@@ -223,8 +223,8 @@ jobs:
chmod -R 755 client
cd client
UNUSED=$(depcheck --json | jq -r '.dependencies | join("\n")' || echo "")
# Exclude dependencies used in scripts, code, workspace packages, and @librechat/client imports
UNUSED=$(comm -23 <(echo "$UNUSED" | sort) <(cat ../client_used_deps.txt ../client_used_code.txt ../client_workspace_deps.txt ../packages_client_used_code.txt ../librechat_client_deps.txt 2>/dev/null | sort -u) || echo "")
# Exclude dependencies used in scripts, code, workspace packages, and @hanzochat/client imports
UNUSED=$(comm -23 <(echo "$UNUSED" | sort) <(cat ../client_used_deps.txt ../client_used_code.txt ../client_workspace_deps.txt ../packages_client_used_code.txt ../chat_client_deps.txt 2>/dev/null | sort -u) || echo "")
# Filter out false positives
UNUSED=$(echo "$UNUSED" | grep -v "^micromark-extension-llm-math$" || echo "")
echo "CLIENT_UNUSED<<EOF" >> $GITHUB_ENV
@@ -240,8 +240,8 @@ jobs:
chmod -R 755 api
cd api
UNUSED=$(depcheck --json | jq -r '.dependencies | join("\n")' || echo "")
# Exclude dependencies used in scripts, code, workspace packages, and @librechat/api imports
UNUSED=$(comm -23 <(echo "$UNUSED" | sort) <(cat ../api_used_deps.txt ../api_used_code.txt ../api_workspace_deps.txt ../packages_api_used_code.txt ../librechat_api_deps.txt 2>/dev/null | sort -u) || echo "")
# Exclude dependencies used in scripts, code, workspace packages, and @hanzochat/api imports
UNUSED=$(comm -23 <(echo "$UNUSED" | sort) <(cat ../api_used_deps.txt ../api_used_code.txt ../api_workspace_deps.txt ../packages_api_used_code.txt ../chat_api_deps.txt 2>/dev/null | sort -u) || echo "")
echo "API_UNUSED<<EOF" >> $GITHUB_ENV
echo "$UNUSED" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
+3 -3
View File
@@ -121,12 +121,12 @@ docker-compose.override.yml
dump.rdb
helm/**/.values.yaml
helm/**/charts/
helm/librechat/Chart.lock
helm/chat/Chart.lock
hive-mind-prompt-*.txt
junit.xml
lib-cov
librechat.yaml
librechat.yml
chat.yaml
chat.yml
logs
meili_data*
meili_data/
+166 -166
View File
@@ -11,41 +11,41 @@ All notable changes to this project will be documented in this file.
### ✨ New Features
- ✨ feat: implement search parameter updates by **@mawburn** in [#7151](https://github.com/danny-avila/LibreChat/pull/7151)
- 🎏 feat: Add MCP support for Streamable HTTP Transport by **@benverhees** in [#7353](https://github.com/danny-avila/LibreChat/pull/7353)
- 🔒 feat: Add Content Security Policy using Helmet middleware by **@rubentalstra** in [#7377](https://github.com/danny-avila/LibreChat/pull/7377)
- ✨ feat: Add Normalization for MCP Server Names by **@danny-avila** in [#7421](https://github.com/danny-avila/LibreChat/pull/7421)
- 📊 feat: Improve Helm Chart by **@hofq** in [#3638](https://github.com/danny-avila/LibreChat/pull/3638)
- 🦾 feat: Claude-4 Support by **@danny-avila** in [#7509](https://github.com/danny-avila/LibreChat/pull/7509)
- 🪨 feat: Bedrock Support for Claude-4 Reasoning by **@danny-avila** in [#7517](https://github.com/danny-avila/LibreChat/pull/7517)
- ✨ feat: implement search parameter updates by **@mawburn** in [#7151](https://github.com/danny-avila/Chat/pull/7151)
- 🎏 feat: Add MCP support for Streamable HTTP Transport by **@benverhees** in [#7353](https://github.com/danny-avila/Chat/pull/7353)
- 🔒 feat: Add Content Security Policy using Helmet middleware by **@rubentalstra** in [#7377](https://github.com/danny-avila/Chat/pull/7377)
- ✨ feat: Add Normalization for MCP Server Names by **@danny-avila** in [#7421](https://github.com/danny-avila/Chat/pull/7421)
- 📊 feat: Improve Helm Chart by **@hofq** in [#3638](https://github.com/danny-avila/Chat/pull/3638)
- 🦾 feat: Claude-4 Support by **@danny-avila** in [#7509](https://github.com/danny-avila/Chat/pull/7509)
- 🪨 feat: Bedrock Support for Claude-4 Reasoning by **@danny-avila** in [#7517](https://github.com/danny-avila/Chat/pull/7517)
### 🌍 Internationalization
- 🌍 i18n: Add `Danish` and `Czech` and `Catalan` localization support by **@rubentalstra** in [#7373](https://github.com/danny-avila/LibreChat/pull/7373)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7375](https://github.com/danny-avila/LibreChat/pull/7375)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7468](https://github.com/danny-avila/LibreChat/pull/7468)
- 🌍 i18n: Add `Danish` and `Czech` and `Catalan` localization support by **@rubentalstra** in [#7373](https://github.com/danny-avila/Chat/pull/7373)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7375](https://github.com/danny-avila/Chat/pull/7375)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7468](https://github.com/danny-avila/Chat/pull/7468)
### 🔧 Fixes
- 💬 fix: update aria-label for accessibility in ConvoLink component by **@berry-13** in [#7320](https://github.com/danny-avila/LibreChat/pull/7320)
- 🔑 fix: use `apiKey` instead of `openAIApiKey` in OpenAI-like Config by **@danny-avila** in [#7337](https://github.com/danny-avila/LibreChat/pull/7337)
- 🔄 fix: update navigation logic in `useFocusChatEffect` to ensure correct search parameters are used by **@mawburn** in [#7340](https://github.com/danny-avila/LibreChat/pull/7340)
- 🔄 fix: Improve MCP Connection Cleanup by **@danny-avila** in [#7400](https://github.com/danny-avila/LibreChat/pull/7400)
- 🛡️ fix: Preset and Validation Logic for URL Query Params by **@danny-avila** in [#7407](https://github.com/danny-avila/LibreChat/pull/7407)
- 🌘 fix: artifact of preview text is illegible in dark mode by **@nhtruong** in [#7405](https://github.com/danny-avila/LibreChat/pull/7405)
- 🛡️ fix: Temporarily Remove CSP until Configurable by **@danny-avila** in [#7419](https://github.com/danny-avila/LibreChat/pull/7419)
- 💽 fix: Exclude index page `/` from static cache settings by **@sbruel** in [#7382](https://github.com/danny-avila/LibreChat/pull/7382)
- 💬 fix: update aria-label for accessibility in ConvoLink component by **@berry-13** in [#7320](https://github.com/danny-avila/Chat/pull/7320)
- 🔑 fix: use `apiKey` instead of `openAIApiKey` in OpenAI-like Config by **@danny-avila** in [#7337](https://github.com/danny-avila/Chat/pull/7337)
- 🔄 fix: update navigation logic in `useFocusChatEffect` to ensure correct search parameters are used by **@mawburn** in [#7340](https://github.com/danny-avila/Chat/pull/7340)
- 🔄 fix: Improve MCP Connection Cleanup by **@danny-avila** in [#7400](https://github.com/danny-avila/Chat/pull/7400)
- 🛡️ fix: Preset and Validation Logic for URL Query Params by **@danny-avila** in [#7407](https://github.com/danny-avila/Chat/pull/7407)
- 🌘 fix: artifact of preview text is illegible in dark mode by **@nhtruong** in [#7405](https://github.com/danny-avila/Chat/pull/7405)
- 🛡️ fix: Temporarily Remove CSP until Configurable by **@danny-avila** in [#7419](https://github.com/danny-avila/Chat/pull/7419)
- 💽 fix: Exclude index page `/` from static cache settings by **@sbruel** in [#7382](https://github.com/danny-avila/Chat/pull/7382)
### ⚙️ Other Changes
- 📜 docs: CHANGELOG for release v0.7.8 by **@github-actions[bot]** in [#7290](https://github.com/danny-avila/LibreChat/pull/7290)
- 📦 chore: Update API Package Dependencies by **@danny-avila** in [#7359](https://github.com/danny-avila/LibreChat/pull/7359)
- 📜 docs: Unreleased Changelog by **@github-actions[bot]** in [#7321](https://github.com/danny-avila/LibreChat/pull/7321)
- 📜 docs: Unreleased Changelog by **@github-actions[bot]** in [#7434](https://github.com/danny-avila/LibreChat/pull/7434)
- 🛡️ chore: `multer` v2.0.0 for CVE-2025-47935 and CVE-2025-47944 by **@danny-avila** in [#7454](https://github.com/danny-avila/LibreChat/pull/7454)
- 📂 refactor: Improve `FileAttachment` & File Form Deletion by **@danny-avila** in [#7471](https://github.com/danny-avila/LibreChat/pull/7471)
- 📊 chore: Remove Old Helm Chart by **@hofq** in [#7512](https://github.com/danny-avila/LibreChat/pull/7512)
- 🪖 chore: bump helm app version to v0.7.8 by **@austin-barrington** in [#7524](https://github.com/danny-avila/LibreChat/pull/7524)
- 📜 docs: CHANGELOG for release v0.7.8 by **@github-actions[bot]** in [#7290](https://github.com/danny-avila/Chat/pull/7290)
- 📦 chore: Update API Package Dependencies by **@danny-avila** in [#7359](https://github.com/danny-avila/Chat/pull/7359)
- 📜 docs: Unreleased Changelog by **@github-actions[bot]** in [#7321](https://github.com/danny-avila/Chat/pull/7321)
- 📜 docs: Unreleased Changelog by **@github-actions[bot]** in [#7434](https://github.com/danny-avila/Chat/pull/7434)
- 🛡️ chore: `multer` v2.0.0 for CVE-2025-47935 and CVE-2025-47944 by **@danny-avila** in [#7454](https://github.com/danny-avila/Chat/pull/7454)
- 📂 refactor: Improve `FileAttachment` & File Form Deletion by **@danny-avila** in [#7471](https://github.com/danny-avila/Chat/pull/7471)
- 📊 chore: Remove Old Helm Chart by **@hofq** in [#7512](https://github.com/danny-avila/Chat/pull/7512)
- 🪖 chore: bump helm app version to v0.7.8 by **@austin-barrington** in [#7524](https://github.com/danny-avila/Chat/pull/7524)
@@ -56,38 +56,38 @@ Changes from v0.7.8-rc1 to v0.7.8.
### ✨ New Features
- ✨ feat: Enhance form submission for touch screens by **@berry-13** in [#7198](https://github.com/danny-avila/LibreChat/pull/7198)
- 🔍 feat: Additional Tavily API Tool Parameters by **@glowforge-opensource** in [#7232](https://github.com/danny-avila/LibreChat/pull/7232)
- 🐋 feat: Add python to Dockerfile for increased MCP compatibility by **@technicalpickles** in [#7270](https://github.com/danny-avila/LibreChat/pull/7270)
- ✨ feat: Enhance form submission for touch screens by **@berry-13** in [#7198](https://github.com/danny-avila/Chat/pull/7198)
- 🔍 feat: Additional Tavily API Tool Parameters by **@glowforge-opensource** in [#7232](https://github.com/danny-avila/Chat/pull/7232)
- 🐋 feat: Add python to Dockerfile for increased MCP compatibility by **@technicalpickles** in [#7270](https://github.com/danny-avila/Chat/pull/7270)
### 🔧 Fixes
- 🔧 fix: Google Gemma Support & OpenAI Reasoning Instructions by **@danny-avila** in [#7196](https://github.com/danny-avila/LibreChat/pull/7196)
- 🛠️ fix: Conversation Navigation State by **@danny-avila** in [#7210](https://github.com/danny-avila/LibreChat/pull/7210)
- 🔄 fix: o-Series Model Regex for System Messages by **@danny-avila** in [#7245](https://github.com/danny-avila/LibreChat/pull/7245)
- 🔖 fix: Custom Headers for Initial MCP SSE Connection by **@danny-avila** in [#7246](https://github.com/danny-avila/LibreChat/pull/7246)
- 🛡️ fix: Deep Clone `MCPOptions` for User MCP Connections by **@danny-avila** in [#7247](https://github.com/danny-avila/LibreChat/pull/7247)
- 🔄 fix: URL Param Race Condition and File Draft Persistence by **@danny-avila** in [#7257](https://github.com/danny-avila/LibreChat/pull/7257)
- 🔄 fix: Assistants Endpoint & Minor Issues by **@danny-avila** in [#7274](https://github.com/danny-avila/LibreChat/pull/7274)
- 🔄 fix: Ollama Think Tag Edge Case with Tools by **@danny-avila** in [#7275](https://github.com/danny-avila/LibreChat/pull/7275)
- 🔧 fix: Google Gemma Support & OpenAI Reasoning Instructions by **@danny-avila** in [#7196](https://github.com/danny-avila/Chat/pull/7196)
- 🛠️ fix: Conversation Navigation State by **@danny-avila** in [#7210](https://github.com/danny-avila/Chat/pull/7210)
- 🔄 fix: o-Series Model Regex for System Messages by **@danny-avila** in [#7245](https://github.com/danny-avila/Chat/pull/7245)
- 🔖 fix: Custom Headers for Initial MCP SSE Connection by **@danny-avila** in [#7246](https://github.com/danny-avila/Chat/pull/7246)
- 🛡️ fix: Deep Clone `MCPOptions` for User MCP Connections by **@danny-avila** in [#7247](https://github.com/danny-avila/Chat/pull/7247)
- 🔄 fix: URL Param Race Condition and File Draft Persistence by **@danny-avila** in [#7257](https://github.com/danny-avila/Chat/pull/7257)
- 🔄 fix: Assistants Endpoint & Minor Issues by **@danny-avila** in [#7274](https://github.com/danny-avila/Chat/pull/7274)
- 🔄 fix: Ollama Think Tag Edge Case with Tools by **@danny-avila** in [#7275](https://github.com/danny-avila/Chat/pull/7275)
### ⚙️ Other Changes
- 📜 docs: CHANGELOG for release v0.7.8-rc1 by **@github-actions[bot]** in [#7153](https://github.com/danny-avila/LibreChat/pull/7153)
- 🔄 refactor: Artifact Visibility Management by **@danny-avila** in [#7181](https://github.com/danny-avila/LibreChat/pull/7181)
- 📦 chore: Bump Package Security by **@danny-avila** in [#7183](https://github.com/danny-avila/LibreChat/pull/7183)
- 🌿 refactor: Unmount Fork Popover on Hide for Better Performance by **@danny-avila** in [#7189](https://github.com/danny-avila/LibreChat/pull/7189)
- 🧰 chore: ESLint configuration to enforce Prettier formatting rules by **@mawburn** in [#7186](https://github.com/danny-avila/LibreChat/pull/7186)
- 🎨 style: Improve KaTeX Rendering for LaTeX Equations by **@andresgit** in [#7223](https://github.com/danny-avila/LibreChat/pull/7223)
- 📝 docs: Update `.env.example` Google models by **@marlonka** in [#7254](https://github.com/danny-avila/LibreChat/pull/7254)
- 💬 refactor: MCP Chat Visibility Option, Google Rates, Remove OpenAPI Plugins by **@danny-avila** in [#7286](https://github.com/danny-avila/LibreChat/pull/7286)
- 📜 docs: Unreleased Changelog by **@github-actions[bot]** in [#7214](https://github.com/danny-avila/LibreChat/pull/7214)
- 📜 docs: CHANGELOG for release v0.7.8-rc1 by **@github-actions[bot]** in [#7153](https://github.com/danny-avila/Chat/pull/7153)
- 🔄 refactor: Artifact Visibility Management by **@danny-avila** in [#7181](https://github.com/danny-avila/Chat/pull/7181)
- 📦 chore: Bump Package Security by **@danny-avila** in [#7183](https://github.com/danny-avila/Chat/pull/7183)
- 🌿 refactor: Unmount Fork Popover on Hide for Better Performance by **@danny-avila** in [#7189](https://github.com/danny-avila/Chat/pull/7189)
- 🧰 chore: ESLint configuration to enforce Prettier formatting rules by **@mawburn** in [#7186](https://github.com/danny-avila/Chat/pull/7186)
- 🎨 style: Improve KaTeX Rendering for LaTeX Equations by **@andresgit** in [#7223](https://github.com/danny-avila/Chat/pull/7223)
- 📝 docs: Update `.env.example` Google models by **@marlonka** in [#7254](https://github.com/danny-avila/Chat/pull/7254)
- 💬 refactor: MCP Chat Visibility Option, Google Rates, Remove OpenAPI Plugins by **@danny-avila** in [#7286](https://github.com/danny-avila/Chat/pull/7286)
- 📜 docs: Unreleased Changelog by **@github-actions[bot]** in [#7214](https://github.com/danny-avila/Chat/pull/7214)
[See full release details][release-v0.7.8]
[release-v0.7.8]: https://github.com/danny-avila/LibreChat/releases/tag/v0.7.8
[release-v0.7.8]: https://github.com/danny-avila/Chat/releases/tag/v0.7.8
---
## [v0.7.8-rc1] -
@@ -96,141 +96,141 @@ Changes from v0.7.7 to v0.7.8-rc1.
### ✨ New Features
- 🔍 feat: Mistral OCR API / Upload Files as Text by **@danny-avila** in [#6274](https://github.com/danny-avila/LibreChat/pull/6274)
- 🤖 feat: Support OpenAI Web Search models by **@danny-avila** in [#6313](https://github.com/danny-avila/LibreChat/pull/6313)
- 🔗 feat: Agent Chain (Mixture-of-Agents) by **@danny-avila** in [#6374](https://github.com/danny-avila/LibreChat/pull/6374)
- ⌛ feat: `initTimeout` for Slow Starting MCP Servers by **@perweij** in [#6383](https://github.com/danny-avila/LibreChat/pull/6383)
- 🚀 feat: `S3` Integration for File handling and Image uploads by **@rubentalstra** in [#6142](https://github.com/danny-avila/LibreChat/pull/6142)
- 🔒feat: Enable OpenID Auto-Redirect by **@leondape** in [#6066](https://github.com/danny-avila/LibreChat/pull/6066)
- 🚀 feat: Integrate `Azure Blob Storage` for file handling and image uploads by **@rubentalstra** in [#6153](https://github.com/danny-avila/LibreChat/pull/6153)
- 🚀 feat: Add support for custom `AWS` endpoint in `S3` by **@rubentalstra** in [#6431](https://github.com/danny-avila/LibreChat/pull/6431)
- 🚀 feat: Add support for LDAP STARTTLS in LDAP authentication by **@rubentalstra** in [#6438](https://github.com/danny-avila/LibreChat/pull/6438)
- 🚀 feat: Refactor schema exports and update package version to 0.0.4 by **@rubentalstra** in [#6455](https://github.com/danny-avila/LibreChat/pull/6455)
- 🔼 feat: Add Auto Submit For URL Query Params by **@mjaverto** in [#6440](https://github.com/danny-avila/LibreChat/pull/6440)
- 🛠 feat: Enhance Redis Integration, Rate Limiters & Log Headers by **@danny-avila** in [#6462](https://github.com/danny-avila/LibreChat/pull/6462)
- 💵 feat: Add Automatic Balance Refill by **@rubentalstra** in [#6452](https://github.com/danny-avila/LibreChat/pull/6452)
- 🗣️ feat: add support for gpt-4o-transcribe models by **@berry-13** in [#6483](https://github.com/danny-avila/LibreChat/pull/6483)
- 🎨 feat: UI Refresh for Enhanced UX by **@berry-13** in [#6346](https://github.com/danny-avila/LibreChat/pull/6346)
- 🌍 feat: Add support for Hungarian language localization by **@rubentalstra** in [#6508](https://github.com/danny-avila/LibreChat/pull/6508)
- 🚀 feat: Add Gemini 2.5 Token/Context Values, Increase Max Possible Output to 64k by **@danny-avila** in [#6563](https://github.com/danny-avila/LibreChat/pull/6563)
- 🚀 feat: Enhance MCP Connections For Multi-User Support by **@danny-avila** in [#6610](https://github.com/danny-avila/LibreChat/pull/6610)
- 🚀 feat: Enhance S3 URL Expiry with Refresh; fix: S3 File Deletion by **@danny-avila** in [#6647](https://github.com/danny-avila/LibreChat/pull/6647)
- 🚀 feat: enhance UI components and refactor settings by **@berry-13** in [#6625](https://github.com/danny-avila/LibreChat/pull/6625)
- 💬 feat: move TemporaryChat to the Header by **@berry-13** in [#6646](https://github.com/danny-avila/LibreChat/pull/6646)
- 🚀 feat: Use Model Specs + Specific Endpoints, Limit Providers for Agents by **@danny-avila** in [#6650](https://github.com/danny-avila/LibreChat/pull/6650)
- 🪙 feat: Sync Balance Config on Login by **@danny-avila** in [#6671](https://github.com/danny-avila/LibreChat/pull/6671)
- 🔦 feat: MCP Support for Non-Agent Endpoints by **@danny-avila** in [#6775](https://github.com/danny-avila/LibreChat/pull/6775)
- 🗃️ feat: Code Interpreter File Persistence between Sessions by **@danny-avila** in [#6790](https://github.com/danny-avila/LibreChat/pull/6790)
- 🖥️ feat: Code Interpreter API for Non-Agent Endpoints by **@danny-avila** in [#6803](https://github.com/danny-avila/LibreChat/pull/6803)
- ⚡ feat: Self-hosted Artifacts Static Bundler URL by **@danny-avila** in [#6827](https://github.com/danny-avila/LibreChat/pull/6827)
- 🐳 feat: Add Jemalloc and UV to Docker Builds by **@danny-avila** in [#6836](https://github.com/danny-avila/LibreChat/pull/6836)
- 🤖 feat: GPT-4.1 by **@danny-avila** in [#6880](https://github.com/danny-avila/LibreChat/pull/6880)
- 👋 feat: remove Edge TTS by **@berry-13** in [#6885](https://github.com/danny-avila/LibreChat/pull/6885)
- feat: nav optimization by **@berry-13** in [#5785](https://github.com/danny-avila/LibreChat/pull/5785)
- 🗺️ feat: Add Parameter Location Mapping for OpenAPI actions by **@peeeteeer** in [#6858](https://github.com/danny-avila/LibreChat/pull/6858)
- 🤖 feat: Support `o4-mini` and `o3` Models by **@danny-avila** in [#6928](https://github.com/danny-avila/LibreChat/pull/6928)
- 🎨 feat: OpenAI Image Tools (GPT-Image-1) by **@danny-avila** in [#7079](https://github.com/danny-avila/LibreChat/pull/7079)
- 🗓️ feat: Add Special Variables for Prompts & Agents, Prompt UI Improvements by **@danny-avila** in [#7123](https://github.com/danny-avila/LibreChat/pull/7123)
- 🔍 feat: Mistral OCR API / Upload Files as Text by **@danny-avila** in [#6274](https://github.com/danny-avila/Chat/pull/6274)
- 🤖 feat: Support OpenAI Web Search models by **@danny-avila** in [#6313](https://github.com/danny-avila/Chat/pull/6313)
- 🔗 feat: Agent Chain (Mixture-of-Agents) by **@danny-avila** in [#6374](https://github.com/danny-avila/Chat/pull/6374)
- ⌛ feat: `initTimeout` for Slow Starting MCP Servers by **@perweij** in [#6383](https://github.com/danny-avila/Chat/pull/6383)
- 🚀 feat: `S3` Integration for File handling and Image uploads by **@rubentalstra** in [#6142](https://github.com/danny-avila/Chat/pull/6142)
- 🔒feat: Enable OpenID Auto-Redirect by **@leondape** in [#6066](https://github.com/danny-avila/Chat/pull/6066)
- 🚀 feat: Integrate `Azure Blob Storage` for file handling and image uploads by **@rubentalstra** in [#6153](https://github.com/danny-avila/Chat/pull/6153)
- 🚀 feat: Add support for custom `AWS` endpoint in `S3` by **@rubentalstra** in [#6431](https://github.com/danny-avila/Chat/pull/6431)
- 🚀 feat: Add support for LDAP STARTTLS in LDAP authentication by **@rubentalstra** in [#6438](https://github.com/danny-avila/Chat/pull/6438)
- 🚀 feat: Refactor schema exports and update package version to 0.0.4 by **@rubentalstra** in [#6455](https://github.com/danny-avila/Chat/pull/6455)
- 🔼 feat: Add Auto Submit For URL Query Params by **@mjaverto** in [#6440](https://github.com/danny-avila/Chat/pull/6440)
- 🛠 feat: Enhance Redis Integration, Rate Limiters & Log Headers by **@danny-avila** in [#6462](https://github.com/danny-avila/Chat/pull/6462)
- 💵 feat: Add Automatic Balance Refill by **@rubentalstra** in [#6452](https://github.com/danny-avila/Chat/pull/6452)
- 🗣️ feat: add support for gpt-4o-transcribe models by **@berry-13** in [#6483](https://github.com/danny-avila/Chat/pull/6483)
- 🎨 feat: UI Refresh for Enhanced UX by **@berry-13** in [#6346](https://github.com/danny-avila/Chat/pull/6346)
- 🌍 feat: Add support for Hungarian language localization by **@rubentalstra** in [#6508](https://github.com/danny-avila/Chat/pull/6508)
- 🚀 feat: Add Gemini 2.5 Token/Context Values, Increase Max Possible Output to 64k by **@danny-avila** in [#6563](https://github.com/danny-avila/Chat/pull/6563)
- 🚀 feat: Enhance MCP Connections For Multi-User Support by **@danny-avila** in [#6610](https://github.com/danny-avila/Chat/pull/6610)
- 🚀 feat: Enhance S3 URL Expiry with Refresh; fix: S3 File Deletion by **@danny-avila** in [#6647](https://github.com/danny-avila/Chat/pull/6647)
- 🚀 feat: enhance UI components and refactor settings by **@berry-13** in [#6625](https://github.com/danny-avila/Chat/pull/6625)
- 💬 feat: move TemporaryChat to the Header by **@berry-13** in [#6646](https://github.com/danny-avila/Chat/pull/6646)
- 🚀 feat: Use Model Specs + Specific Endpoints, Limit Providers for Agents by **@danny-avila** in [#6650](https://github.com/danny-avila/Chat/pull/6650)
- 🪙 feat: Sync Balance Config on Login by **@danny-avila** in [#6671](https://github.com/danny-avila/Chat/pull/6671)
- 🔦 feat: MCP Support for Non-Agent Endpoints by **@danny-avila** in [#6775](https://github.com/danny-avila/Chat/pull/6775)
- 🗃️ feat: Code Interpreter File Persistence between Sessions by **@danny-avila** in [#6790](https://github.com/danny-avila/Chat/pull/6790)
- 🖥️ feat: Code Interpreter API for Non-Agent Endpoints by **@danny-avila** in [#6803](https://github.com/danny-avila/Chat/pull/6803)
- ⚡ feat: Self-hosted Artifacts Static Bundler URL by **@danny-avila** in [#6827](https://github.com/danny-avila/Chat/pull/6827)
- 🐳 feat: Add Jemalloc and UV to Docker Builds by **@danny-avila** in [#6836](https://github.com/danny-avila/Chat/pull/6836)
- 🤖 feat: GPT-4.1 by **@danny-avila** in [#6880](https://github.com/danny-avila/Chat/pull/6880)
- 👋 feat: remove Edge TTS by **@berry-13** in [#6885](https://github.com/danny-avila/Chat/pull/6885)
- feat: nav optimization by **@berry-13** in [#5785](https://github.com/danny-avila/Chat/pull/5785)
- 🗺️ feat: Add Parameter Location Mapping for OpenAPI actions by **@peeeteeer** in [#6858](https://github.com/danny-avila/Chat/pull/6858)
- 🤖 feat: Support `o4-mini` and `o3` Models by **@danny-avila** in [#6928](https://github.com/danny-avila/Chat/pull/6928)
- 🎨 feat: OpenAI Image Tools (GPT-Image-1) by **@danny-avila** in [#7079](https://github.com/danny-avila/Chat/pull/7079)
- 🗓️ feat: Add Special Variables for Prompts & Agents, Prompt UI Improvements by **@danny-avila** in [#7123](https://github.com/danny-avila/Chat/pull/7123)
### 🌍 Internationalization
- 🌍 i18n: Add Thai Language Support and Update Translations by **@rubentalstra** in [#6219](https://github.com/danny-avila/LibreChat/pull/6219)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6220](https://github.com/danny-avila/LibreChat/pull/6220)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6240](https://github.com/danny-avila/LibreChat/pull/6240)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6241](https://github.com/danny-avila/LibreChat/pull/6241)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6277](https://github.com/danny-avila/LibreChat/pull/6277)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6414](https://github.com/danny-avila/LibreChat/pull/6414)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6505](https://github.com/danny-avila/LibreChat/pull/6505)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6530](https://github.com/danny-avila/LibreChat/pull/6530)
- 🌍 i18n: Add Persian Localization Support by **@rubentalstra** in [#6669](https://github.com/danny-avila/LibreChat/pull/6669)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6667](https://github.com/danny-avila/LibreChat/pull/6667)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7126](https://github.com/danny-avila/LibreChat/pull/7126)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7148](https://github.com/danny-avila/LibreChat/pull/7148)
- 🌍 i18n: Add Thai Language Support and Update Translations by **@rubentalstra** in [#6219](https://github.com/danny-avila/Chat/pull/6219)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6220](https://github.com/danny-avila/Chat/pull/6220)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6240](https://github.com/danny-avila/Chat/pull/6240)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6241](https://github.com/danny-avila/Chat/pull/6241)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6277](https://github.com/danny-avila/Chat/pull/6277)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6414](https://github.com/danny-avila/Chat/pull/6414)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6505](https://github.com/danny-avila/Chat/pull/6505)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6530](https://github.com/danny-avila/Chat/pull/6530)
- 🌍 i18n: Add Persian Localization Support by **@rubentalstra** in [#6669](https://github.com/danny-avila/Chat/pull/6669)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6667](https://github.com/danny-avila/Chat/pull/6667)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7126](https://github.com/danny-avila/Chat/pull/7126)
- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7148](https://github.com/danny-avila/Chat/pull/7148)
### 👐 Accessibility
- 🎨 a11y: Update Model Spec Description Text by **@berry-13** in [#6294](https://github.com/danny-avila/LibreChat/pull/6294)
- 🗑️ a11y: Add Accessible Name to Button for File Attachment Removal by **@kangabell** in [#6709](https://github.com/danny-avila/LibreChat/pull/6709)
- ⌨️ a11y: enhance accessibility & visual consistency by **@berry-13** in [#6866](https://github.com/danny-avila/LibreChat/pull/6866)
- 🙌 a11y: Searchbar/Conversations List Focus by **@danny-avila** in [#7096](https://github.com/danny-avila/LibreChat/pull/7096)
- 👐 a11y: Improve Fork and SplitText Accessibility by **@danny-avila** in [#7147](https://github.com/danny-avila/LibreChat/pull/7147)
- 🎨 a11y: Update Model Spec Description Text by **@berry-13** in [#6294](https://github.com/danny-avila/Chat/pull/6294)
- 🗑️ a11y: Add Accessible Name to Button for File Attachment Removal by **@kangabell** in [#6709](https://github.com/danny-avila/Chat/pull/6709)
- ⌨️ a11y: enhance accessibility & visual consistency by **@berry-13** in [#6866](https://github.com/danny-avila/Chat/pull/6866)
- 🙌 a11y: Searchbar/Conversations List Focus by **@danny-avila** in [#7096](https://github.com/danny-avila/Chat/pull/7096)
- 👐 a11y: Improve Fork and SplitText Accessibility by **@danny-avila** in [#7147](https://github.com/danny-avila/Chat/pull/7147)
### 🔧 Fixes
- 🐛 fix: Avatar Type Definitions in Agent/Assistant Schemas by **@danny-avila** in [#6235](https://github.com/danny-avila/LibreChat/pull/6235)
- 🔧 fix: MeiliSearch Field Error and Patch Incorrect Import by #6210 by **@rubentalstra** in [#6245](https://github.com/danny-avila/LibreChat/pull/6245)
- 🔏 fix: Enhance Two-Factor Authentication by **@rubentalstra** in [#6247](https://github.com/danny-avila/LibreChat/pull/6247)
- 🐛 fix: Await saveMessage in abortMiddleware to ensure proper execution by **@sh4shii** in [#6248](https://github.com/danny-avila/LibreChat/pull/6248)
- 🔧 fix: Axios Proxy Usage And Bump `mongoose` by **@danny-avila** in [#6298](https://github.com/danny-avila/LibreChat/pull/6298)
- 🔧 fix: comment out MCP servers to resolve service run issues by **@KunalScriptz** in [#6316](https://github.com/danny-avila/LibreChat/pull/6316)
- 🔧 fix: Update Token Calculations and Mapping, MCP `env` Initialization by **@danny-avila** in [#6406](https://github.com/danny-avila/LibreChat/pull/6406)
- 🐞 fix: Agent "Resend" Message Attachments + Source Icon Styling by **@danny-avila** in [#6408](https://github.com/danny-avila/LibreChat/pull/6408)
- 🐛 fix: Prevent Crash on Duplicate Message ID by **@Odrec** in [#6392](https://github.com/danny-avila/LibreChat/pull/6392)
- 🔐 fix: Invalid Key Length in 2FA Encryption by **@rubentalstra** in [#6432](https://github.com/danny-avila/LibreChat/pull/6432)
- 🏗️ fix: Fix Agents Token Spend Race Conditions, Expand Test Coverage by **@danny-avila** in [#6480](https://github.com/danny-avila/LibreChat/pull/6480)
- 🔃 fix: Draft Clearing, Claude Titles, Remove Default Vision Max Tokens by **@danny-avila** in [#6501](https://github.com/danny-avila/LibreChat/pull/6501)
- 🔧 fix: Update username reference to use user.name in greeting display by **@rubentalstra** in [#6534](https://github.com/danny-avila/LibreChat/pull/6534)
- 🔧 fix: S3 Download Stream with Key Extraction and Blob Storage Encoding for Vision by **@danny-avila** in [#6557](https://github.com/danny-avila/LibreChat/pull/6557)
- 🔧 fix: Mistral type strictness for `usage` & update token values/windows by **@danny-avila** in [#6562](https://github.com/danny-avila/LibreChat/pull/6562)
- 🔧 fix: Consolidate Text Parsing and TTS Edge Initialization by **@danny-avila** in [#6582](https://github.com/danny-avila/LibreChat/pull/6582)
- 🔧 fix: Ensure continuation in image processing on base64 encoding from Blob Storage by **@danny-avila** in [#6619](https://github.com/danny-avila/LibreChat/pull/6619)
- ✉️ fix: Fallback For User Name In Email Templates by **@danny-avila** in [#6620](https://github.com/danny-avila/LibreChat/pull/6620)
- 🔧 fix: Azure Blob Integration and File Source References by **@rubentalstra** in [#6575](https://github.com/danny-avila/LibreChat/pull/6575)
- 🐛 fix: Safeguard against undefined addedEndpoints by **@wipash** in [#6654](https://github.com/danny-avila/LibreChat/pull/6654)
- 🤖 fix: Gemini 2.5 Vision Support by **@danny-avila** in [#6663](https://github.com/danny-avila/LibreChat/pull/6663)
- 🔄 fix: Avatar & Error Handling Enhancements by **@danny-avila** in [#6687](https://github.com/danny-avila/LibreChat/pull/6687)
- 🔧 fix: Chat Middleware, Zod Conversion, Auto-Save and S3 URL Refresh by **@danny-avila** in [#6720](https://github.com/danny-avila/LibreChat/pull/6720)
- 🔧 fix: Agent Capability Checks & DocumentDB Compatibility for Agent Resource Removal by **@danny-avila** in [#6726](https://github.com/danny-avila/LibreChat/pull/6726)
- 🔄 fix: Improve audio MIME type detection and handling by **@berry-13** in [#6707](https://github.com/danny-avila/LibreChat/pull/6707)
- 🪺 fix: Update Role Handling due to New Schema Shape by **@danny-avila** in [#6774](https://github.com/danny-avila/LibreChat/pull/6774)
- 🗨️ fix: Show ModelSpec Greeting by **@berry-13** in [#6770](https://github.com/danny-avila/LibreChat/pull/6770)
- 🔧 fix: Keyv and Proxy Issues, and More Memory Optimizations by **@danny-avila** in [#6867](https://github.com/danny-avila/LibreChat/pull/6867)
- ✨ fix: Implement dynamic text sizing for greeting and name display by **@berry-13** in [#6833](https://github.com/danny-avila/LibreChat/pull/6833)
- 📝 fix: Mistral OCR Image Support and Azure Agent Titles by **@danny-avila** in [#6901](https://github.com/danny-avila/LibreChat/pull/6901)
- 📢 fix: Invalid `engineTTS` and Conversation State on Navigation by **@berry-13** in [#6904](https://github.com/danny-avila/LibreChat/pull/6904)
- 🛠️ fix: Improve Accessibility and Display of Conversation Menu by **@danny-avila** in [#6913](https://github.com/danny-avila/LibreChat/pull/6913)
- 🔧 fix: Agent Resource Form, Convo Menu Style, Ensure Draft Clears on Submission by **@danny-avila** in [#6925](https://github.com/danny-avila/LibreChat/pull/6925)
- 🔀 fix: MCP Improvements, Auto-Save Drafts, Artifact Markup by **@danny-avila** in [#7040](https://github.com/danny-avila/LibreChat/pull/7040)
- 🐋 fix: Improve Deepseek Compatbility by **@danny-avila** in [#7132](https://github.com/danny-avila/LibreChat/pull/7132)
- 🐙 fix: Add Redis Ping Interval to Prevent Connection Drops by **@peeeteeer** in [#7127](https://github.com/danny-avila/LibreChat/pull/7127)
- 🐛 fix: Avatar Type Definitions in Agent/Assistant Schemas by **@danny-avila** in [#6235](https://github.com/danny-avila/Chat/pull/6235)
- 🔧 fix: MeiliSearch Field Error and Patch Incorrect Import by #6210 by **@rubentalstra** in [#6245](https://github.com/danny-avila/Chat/pull/6245)
- 🔏 fix: Enhance Two-Factor Authentication by **@rubentalstra** in [#6247](https://github.com/danny-avila/Chat/pull/6247)
- 🐛 fix: Await saveMessage in abortMiddleware to ensure proper execution by **@sh4shii** in [#6248](https://github.com/danny-avila/Chat/pull/6248)
- 🔧 fix: Axios Proxy Usage And Bump `mongoose` by **@danny-avila** in [#6298](https://github.com/danny-avila/Chat/pull/6298)
- 🔧 fix: comment out MCP servers to resolve service run issues by **@KunalScriptz** in [#6316](https://github.com/danny-avila/Chat/pull/6316)
- 🔧 fix: Update Token Calculations and Mapping, MCP `env` Initialization by **@danny-avila** in [#6406](https://github.com/danny-avila/Chat/pull/6406)
- 🐞 fix: Agent "Resend" Message Attachments + Source Icon Styling by **@danny-avila** in [#6408](https://github.com/danny-avila/Chat/pull/6408)
- 🐛 fix: Prevent Crash on Duplicate Message ID by **@Odrec** in [#6392](https://github.com/danny-avila/Chat/pull/6392)
- 🔐 fix: Invalid Key Length in 2FA Encryption by **@rubentalstra** in [#6432](https://github.com/danny-avila/Chat/pull/6432)
- 🏗️ fix: Fix Agents Token Spend Race Conditions, Expand Test Coverage by **@danny-avila** in [#6480](https://github.com/danny-avila/Chat/pull/6480)
- 🔃 fix: Draft Clearing, Claude Titles, Remove Default Vision Max Tokens by **@danny-avila** in [#6501](https://github.com/danny-avila/Chat/pull/6501)
- 🔧 fix: Update username reference to use user.name in greeting display by **@rubentalstra** in [#6534](https://github.com/danny-avila/Chat/pull/6534)
- 🔧 fix: S3 Download Stream with Key Extraction and Blob Storage Encoding for Vision by **@danny-avila** in [#6557](https://github.com/danny-avila/Chat/pull/6557)
- 🔧 fix: Mistral type strictness for `usage` & update token values/windows by **@danny-avila** in [#6562](https://github.com/danny-avila/Chat/pull/6562)
- 🔧 fix: Consolidate Text Parsing and TTS Edge Initialization by **@danny-avila** in [#6582](https://github.com/danny-avila/Chat/pull/6582)
- 🔧 fix: Ensure continuation in image processing on base64 encoding from Blob Storage by **@danny-avila** in [#6619](https://github.com/danny-avila/Chat/pull/6619)
- ✉️ fix: Fallback For User Name In Email Templates by **@danny-avila** in [#6620](https://github.com/danny-avila/Chat/pull/6620)
- 🔧 fix: Azure Blob Integration and File Source References by **@rubentalstra** in [#6575](https://github.com/danny-avila/Chat/pull/6575)
- 🐛 fix: Safeguard against undefined addedEndpoints by **@wipash** in [#6654](https://github.com/danny-avila/Chat/pull/6654)
- 🤖 fix: Gemini 2.5 Vision Support by **@danny-avila** in [#6663](https://github.com/danny-avila/Chat/pull/6663)
- 🔄 fix: Avatar & Error Handling Enhancements by **@danny-avila** in [#6687](https://github.com/danny-avila/Chat/pull/6687)
- 🔧 fix: Chat Middleware, Zod Conversion, Auto-Save and S3 URL Refresh by **@danny-avila** in [#6720](https://github.com/danny-avila/Chat/pull/6720)
- 🔧 fix: Agent Capability Checks & DocumentDB Compatibility for Agent Resource Removal by **@danny-avila** in [#6726](https://github.com/danny-avila/Chat/pull/6726)
- 🔄 fix: Improve audio MIME type detection and handling by **@berry-13** in [#6707](https://github.com/danny-avila/Chat/pull/6707)
- 🪺 fix: Update Role Handling due to New Schema Shape by **@danny-avila** in [#6774](https://github.com/danny-avila/Chat/pull/6774)
- 🗨️ fix: Show ModelSpec Greeting by **@berry-13** in [#6770](https://github.com/danny-avila/Chat/pull/6770)
- 🔧 fix: Keyv and Proxy Issues, and More Memory Optimizations by **@danny-avila** in [#6867](https://github.com/danny-avila/Chat/pull/6867)
- ✨ fix: Implement dynamic text sizing for greeting and name display by **@berry-13** in [#6833](https://github.com/danny-avila/Chat/pull/6833)
- 📝 fix: Mistral OCR Image Support and Azure Agent Titles by **@danny-avila** in [#6901](https://github.com/danny-avila/Chat/pull/6901)
- 📢 fix: Invalid `engineTTS` and Conversation State on Navigation by **@berry-13** in [#6904](https://github.com/danny-avila/Chat/pull/6904)
- 🛠️ fix: Improve Accessibility and Display of Conversation Menu by **@danny-avila** in [#6913](https://github.com/danny-avila/Chat/pull/6913)
- 🔧 fix: Agent Resource Form, Convo Menu Style, Ensure Draft Clears on Submission by **@danny-avila** in [#6925](https://github.com/danny-avila/Chat/pull/6925)
- 🔀 fix: MCP Improvements, Auto-Save Drafts, Artifact Markup by **@danny-avila** in [#7040](https://github.com/danny-avila/Chat/pull/7040)
- 🐋 fix: Improve Deepseek Compatbility by **@danny-avila** in [#7132](https://github.com/danny-avila/Chat/pull/7132)
- 🐙 fix: Add Redis Ping Interval to Prevent Connection Drops by **@peeeteeer** in [#7127](https://github.com/danny-avila/Chat/pull/7127)
### ⚙️ Other Changes
- 📦 refactor: Move DB Models to `@librechat/data-schemas` by **@rubentalstra** in [#6210](https://github.com/danny-avila/LibreChat/pull/6210)
- 📦 chore: Patch `axios` to address CVE-2025-27152 by **@danny-avila** in [#6222](https://github.com/danny-avila/LibreChat/pull/6222)
- ⚠️ refactor: Use Error Content Part Instead Of Throwing Error for Agents by **@danny-avila** in [#6262](https://github.com/danny-avila/LibreChat/pull/6262)
- 🏃‍♂️ refactor: Improve Agent Run Context & Misc. Changes by **@danny-avila** in [#6448](https://github.com/danny-avila/LibreChat/pull/6448)
- 📝 docs: librechat.example.yaml by **@ineiti** in [#6442](https://github.com/danny-avila/LibreChat/pull/6442)
- 🏃‍♂️ refactor: More Agent Context Improvements during Run by **@danny-avila** in [#6477](https://github.com/danny-avila/LibreChat/pull/6477)
- 🔃 refactor: Allow streaming for `o1` models by **@danny-avila** in [#6509](https://github.com/danny-avila/LibreChat/pull/6509)
- 🔧 chore: `Vite` Plugin Upgrades & Config Optimizations by **@rubentalstra** in [#6547](https://github.com/danny-avila/LibreChat/pull/6547)
- 🔧 refactor: Consolidate Logging, Model Selection & Actions Optimizations, Minor Fixes by **@danny-avila** in [#6553](https://github.com/danny-avila/LibreChat/pull/6553)
- 🎨 style: Address Minor UI Refresh Issues by **@berry-13** in [#6552](https://github.com/danny-avila/LibreChat/pull/6552)
- 🔧 refactor: Enhance Model & Endpoint Configurations with Global Indicators 🌍 by **@berry-13** in [#6578](https://github.com/danny-avila/LibreChat/pull/6578)
- 💬 style: Chat UI, Greeting, and Message adjustments by **@berry-13** in [#6612](https://github.com/danny-avila/LibreChat/pull/6612)
- ⚡ refactor: DocumentDB Compatibility for Balance Updates by **@danny-avila** in [#6673](https://github.com/danny-avila/LibreChat/pull/6673)
- 🧹 chore: Update ESLint rules for React hooks by **@rubentalstra** in [#6685](https://github.com/danny-avila/LibreChat/pull/6685)
- 🪙 chore: Update Gemini Pricing by **@RedwindA** in [#6731](https://github.com/danny-avila/LibreChat/pull/6731)
- 🪺 refactor: Nest Permission fields for Roles by **@rubentalstra** in [#6487](https://github.com/danny-avila/LibreChat/pull/6487)
- 📦 chore: Update `caniuse-lite` dependency to version 1.0.30001706 by **@rubentalstra** in [#6482](https://github.com/danny-avila/LibreChat/pull/6482)
- ⚙️ refactor: OAuth Flow Signal, Type Safety, Tool Progress & Updated Packages by **@danny-avila** in [#6752](https://github.com/danny-avila/LibreChat/pull/6752)
- 📦 chore: bump vite from 6.2.3 to 6.2.5 by **@dependabot[bot]** in [#6745](https://github.com/danny-avila/LibreChat/pull/6745)
- 💾 chore: Enhance Local Storage Handling and Update MCP SDK by **@danny-avila** in [#6809](https://github.com/danny-avila/LibreChat/pull/6809)
- 🤖 refactor: Improve Agents Memory Usage, Bump Keyv, Grok 3 by **@danny-avila** in [#6850](https://github.com/danny-avila/LibreChat/pull/6850)
- 💾 refactor: Enhance Memory In Image Encodings & Client Disposal by **@danny-avila** in [#6852](https://github.com/danny-avila/LibreChat/pull/6852)
- 🔁 refactor: Token Event Handler and Standardize `maxTokens` Key by **@danny-avila** in [#6886](https://github.com/danny-avila/LibreChat/pull/6886)
- 🔍 refactor: Search & Message Retrieval by **@berry-13** in [#6903](https://github.com/danny-avila/LibreChat/pull/6903)
- 🎨 style: standardize dropdown styling & fix z-Index layering by **@berry-13** in [#6939](https://github.com/danny-avila/LibreChat/pull/6939)
- 📙 docs: CONTRIBUTING.md by **@dblock** in [#6831](https://github.com/danny-avila/LibreChat/pull/6831)
- 🧭 refactor: Modernize Nav/Header by **@danny-avila** in [#7094](https://github.com/danny-avila/LibreChat/pull/7094)
- 🪶 refactor: Chat Input Focus for Conversation Navigations & ChatForm Optimizations by **@danny-avila** in [#7100](https://github.com/danny-avila/LibreChat/pull/7100)
- 🔃 refactor: Streamline Navigation, Message Loading UX by **@danny-avila** in [#7118](https://github.com/danny-avila/LibreChat/pull/7118)
- 📜 docs: Unreleased changelog by **@github-actions[bot]** in [#6265](https://github.com/danny-avila/LibreChat/pull/6265)
- 📦 refactor: Move DB Models to `@hanzochat/data-schemas` by **@rubentalstra** in [#6210](https://github.com/danny-avila/Chat/pull/6210)
- 📦 chore: Patch `axios` to address CVE-2025-27152 by **@danny-avila** in [#6222](https://github.com/danny-avila/Chat/pull/6222)
- ⚠️ refactor: Use Error Content Part Instead Of Throwing Error for Agents by **@danny-avila** in [#6262](https://github.com/danny-avila/Chat/pull/6262)
- 🏃‍♂️ refactor: Improve Agent Run Context & Misc. Changes by **@danny-avila** in [#6448](https://github.com/danny-avila/Chat/pull/6448)
- 📝 docs: chat.example.yaml by **@ineiti** in [#6442](https://github.com/danny-avila/Chat/pull/6442)
- 🏃‍♂️ refactor: More Agent Context Improvements during Run by **@danny-avila** in [#6477](https://github.com/danny-avila/Chat/pull/6477)
- 🔃 refactor: Allow streaming for `o1` models by **@danny-avila** in [#6509](https://github.com/danny-avila/Chat/pull/6509)
- 🔧 chore: `Vite` Plugin Upgrades & Config Optimizations by **@rubentalstra** in [#6547](https://github.com/danny-avila/Chat/pull/6547)
- 🔧 refactor: Consolidate Logging, Model Selection & Actions Optimizations, Minor Fixes by **@danny-avila** in [#6553](https://github.com/danny-avila/Chat/pull/6553)
- 🎨 style: Address Minor UI Refresh Issues by **@berry-13** in [#6552](https://github.com/danny-avila/Chat/pull/6552)
- 🔧 refactor: Enhance Model & Endpoint Configurations with Global Indicators 🌍 by **@berry-13** in [#6578](https://github.com/danny-avila/Chat/pull/6578)
- 💬 style: Chat UI, Greeting, and Message adjustments by **@berry-13** in [#6612](https://github.com/danny-avila/Chat/pull/6612)
- ⚡ refactor: DocumentDB Compatibility for Balance Updates by **@danny-avila** in [#6673](https://github.com/danny-avila/Chat/pull/6673)
- 🧹 chore: Update ESLint rules for React hooks by **@rubentalstra** in [#6685](https://github.com/danny-avila/Chat/pull/6685)
- 🪙 chore: Update Gemini Pricing by **@RedwindA** in [#6731](https://github.com/danny-avila/Chat/pull/6731)
- 🪺 refactor: Nest Permission fields for Roles by **@rubentalstra** in [#6487](https://github.com/danny-avila/Chat/pull/6487)
- 📦 chore: Update `caniuse-lite` dependency to version 1.0.30001706 by **@rubentalstra** in [#6482](https://github.com/danny-avila/Chat/pull/6482)
- ⚙️ refactor: OAuth Flow Signal, Type Safety, Tool Progress & Updated Packages by **@danny-avila** in [#6752](https://github.com/danny-avila/Chat/pull/6752)
- 📦 chore: bump vite from 6.2.3 to 6.2.5 by **@dependabot[bot]** in [#6745](https://github.com/danny-avila/Chat/pull/6745)
- 💾 chore: Enhance Local Storage Handling and Update MCP SDK by **@danny-avila** in [#6809](https://github.com/danny-avila/Chat/pull/6809)
- 🤖 refactor: Improve Agents Memory Usage, Bump Keyv, Grok 3 by **@danny-avila** in [#6850](https://github.com/danny-avila/Chat/pull/6850)
- 💾 refactor: Enhance Memory In Image Encodings & Client Disposal by **@danny-avila** in [#6852](https://github.com/danny-avila/Chat/pull/6852)
- 🔁 refactor: Token Event Handler and Standardize `maxTokens` Key by **@danny-avila** in [#6886](https://github.com/danny-avila/Chat/pull/6886)
- 🔍 refactor: Search & Message Retrieval by **@berry-13** in [#6903](https://github.com/danny-avila/Chat/pull/6903)
- 🎨 style: standardize dropdown styling & fix z-Index layering by **@berry-13** in [#6939](https://github.com/danny-avila/Chat/pull/6939)
- 📙 docs: CONTRIBUTING.md by **@dblock** in [#6831](https://github.com/danny-avila/Chat/pull/6831)
- 🧭 refactor: Modernize Nav/Header by **@danny-avila** in [#7094](https://github.com/danny-avila/Chat/pull/7094)
- 🪶 refactor: Chat Input Focus for Conversation Navigations & ChatForm Optimizations by **@danny-avila** in [#7100](https://github.com/danny-avila/Chat/pull/7100)
- 🔃 refactor: Streamline Navigation, Message Loading UX by **@danny-avila** in [#7118](https://github.com/danny-avila/Chat/pull/7118)
- 📜 docs: Unreleased changelog by **@github-actions[bot]** in [#6265](https://github.com/danny-avila/Chat/pull/6265)
[See full release details][release-v0.7.8-rc1]
[release-v0.7.8-rc1]: https://github.com/danny-avila/LibreChat/releases/tag/v0.7.8-rc1
[release-v0.7.8-rc1]: https://github.com/danny-avila/Chat/releases/tag/v0.7.8-rc1
---
+23 -23
View File
@@ -4,7 +4,7 @@ AI chat interface with multi-model support,
MCP integration, agents, and RAG. Live at **hanzo.chat**.
**Repo**: `github.com/hanzoai/chat`
**Upstream**: LibreChat (MIT) — internal package names kept (`@librechat/*`)
**Upstream**: Chat (MIT) — internal package names kept (`@hanzochat/*`)
**Package**: `@hanzochat/chat`
**Runtime**: Node.js 20 (Alpine)
@@ -52,7 +52,7 @@ client/ # React frontend (Vite)
src/routes/ # Client-side routing
src/store/ # State management
packages/
data-provider/ # Shared data layer (librechat-data-provider)
data-provider/ # Shared data layer (@hanzochat/data-provider)
data-schemas/ # Validation schemas
api/ # API client package (@hanzochat/api)
client/ # Shared client components
@@ -62,7 +62,7 @@ packages/
## Configuration
- `librechat.yaml` (or ConfigMap `chat-config` -> `/app/librechat.yaml`)
- `chat.yaml` (or ConfigMap `chat-config` -> `/app/chat.yaml`)
- `hanzo-chat.example.yaml` - Hanzo-specific example config
- `.env` for secrets
@@ -142,7 +142,7 @@ Security model (fail-closed, server-enforced):
## Cloud Agents (canonical /v1/agents)
Chat can RUN a user's canonical Hanzo Cloud agents (cloud `/v1/agents`, the ONE
production agent registry) from the thread — alongside the LibreChat-legacy local
production agent registry) from the thread — alongside the Chat-legacy local
agent builder, which is untouched.
- Two surfaces, ONE run path: the `/agent <name> [prompt]` slash command and the
@@ -197,7 +197,7 @@ agent builder, which is untouched.
`client/src/components/Chat/Input/AgentsCommand.tsx`, and the @mention wiring in
`client/src/hooks/Input/useMentions.ts` + `Mention.tsx`.
- Env: `HANZO_CLOUD_URL` (optional; falls back to the `OPENAI_BASE_URL` host).
- Convergence path (later): chat's LibreChat-legacy `/v1/chat/agents` CRUD should
- Convergence path (later): chat's Chat-legacy `/v1/chat/agents` CRUD should
converge onto cloud `/v1/agents`; this step only ADDS cloud-agent RUN.
## Unified cloud architecture (2026-07) — investigate-before-ripping map
@@ -216,7 +216,7 @@ Verified by full call-graph + route-table trace; do NOT rip blind.
config → LangChain OpenAI client → **`POST https://api.hanzo.ai/v1/chat/completions`**
(SSE stream, resumable via `GenerationJobManager`). Per-user `hk-` key +
per-org Commerce debit; fail-closed 402. THIS is the one inference path.
- **Code interpreter** → `LIBRECHAT_CODE_BASEURL` = cloud `/v1/exec`.
- **Code interpreter** → `CHAT_CODE_BASEURL` = cloud `/v1/exec`.
- **Web search** → `webSearch` block (searxng+firecrawl contracts) = cloud
`/v1/websearch`.
- **Cloud agents** → `POST /v1/chat/agents/cloud/:name/run` server-proxies to cloud
@@ -224,7 +224,7 @@ Verified by full call-graph + route-table trace; do NOT rip blind.
- **Model list**: curated **zen-only** (`fetch:false`) in the loaded config —
NO raw upstream names (brand policy). Authoritative prod list lives in the
`chat-config` ConfigMap (`universe infra/k8s/chat/configmap.yaml`); repo
`librechat.yaml` mirrors it (one way).
`chat.yaml` mirrors it (one way).
### DEAD residue — do NOT treat as a live backend
@@ -237,7 +237,7 @@ Verified by full call-graph + route-table trace; do NOT rip blind.
### The ONE real parallel store (FLAG — needs a Go-backend home)
LibreChat's Express backend owns, in **MongoDB** (`HanzoChat` DB), all of:
Chat's Express backend owns, in **MongoDB** (`HanzoChat` DB), all of:
`convos`, `messages`, `presets`, `prompts`/`promptGroups`, `users`, `balances`/
`transactions`, `files`, `sessions` (refresh-token hashes), plus agents/assistants/
memory/RBAC. Schemas: `packages/data-schemas/src/schema/*`. This is the shadow
@@ -246,9 +246,9 @@ store that is NOT on the Go backend.
- The Go backend (`hanzoai/ai`, mounted at bare `/v1/*` in cloud) DOES have a
persistence home, but under **casibase names** (`/v1/get-chats`, `/v1/get-chat`,
`/v1/add-chat`, `/v1/get-messages`, `/v1/add-message`, `/v1/get-usages`) — a
different schema/shape than LibreChat's Mongo.
different schema/shape than Chat's Mongo.
- The canonical OpenAPI repo has `chat/openapi.yaml` describing the INTENDED
LibreChat-shaped REST surface (`/v1/chat/convos`, `/v1/chat/messages`,
Chat-shaped REST surface (`/v1/chat/convos`, `/v1/chat/messages`,
`/v1/chat/presets`, `/v1/chat/balance`, `/v1/chat/auth/*`) — but the Go binary
**does not implement it yet**, and `ai/openapi.yaml` under-documents the real
casibase routes.
@@ -260,7 +260,7 @@ store that is NOT on the Go backend.
### IAM-native auth (HIP-0111) — federated to hanzo.id, LIVE
- **Prod (backend-proxied)**: LibreChat passport `openid-client` strategy,
- **Prod (backend-proxied)**: Chat passport `openid-client` strategy,
OIDC **discovery** from `${OPENID_ISSUER}` = `https://hanzo.id`
(`/.well-known/openid-configuration`; discovery fetched via in-cluster
`iam.hanzo.svc` to dodge the CF hairpin), client_id **`hanzo-chat`**, callback
@@ -284,7 +284,7 @@ store that is NOT on the Go backend.
`HanzoHeader` for cross-app chrome. Monochrome rebrand already done (grey ramp,
H mark, favicon = hanzo.app set).
- **`@hanzo/gui`** = a **Tamagui** fork (Next.js 15 / React 19, RN-web) — console's
stack. Forcing it into the Vite/React18 LibreChat client = a ground-up rewrite
stack. Forcing it into the Vite/React18 Chat client = a ground-up rewrite
of a live product; NOT done. Unify by widening `@hanzo/ui` adoption + matching
console's monochrome tokens, NOT by swapping component frameworks.
@@ -292,24 +292,24 @@ store that is NOT on the Go backend.
`loadCustomConfig.js` defaults to **`chat.yaml`** (`CONFIG_PATH || <root>/chat.yaml`).
Prod sets `CONFIG_PATH=/app/chat.yaml` (ConfigMap mount). Repo ships
`librechat.yaml` (reference); a deploy that doesn't set `CONFIG_PATH` to it (or
`chat.yaml` (reference); a deploy that doesn't set `CONFIG_PATH` to it (or
provide `chat.yaml`) falls back to the built-in `openAI` endpoint. `OPENAI_BASE_URL`
in `compose.prod.yml` is inert here (built-in openAI reads `OPENAI_REVERSE_PROXY`).
## Internal Package Names
These are kept as-is from upstream (npm deps, not worth renaming):
- `@hanzochat/api`, `@librechat/client`, `@librechat/data-schemas`, `librechat-data-provider`, `@librechat/agents`
- Functions: `extractLibreChatParams`, `importLibreChatConvo`
- Type names: `LibreChatKeys`, `LibreChatParams`
- Config filename: `librechat.yaml` (upstream convention)
- Env var: `LIBRECHAT_LOG_DIR`
- `@hanzochat/api`, `@hanzochat/client`, `@hanzochat/data-schemas`, `@hanzochat/data-provider`, `@hanzochat/agents`
- Functions: `extractChatParams`, `importChatConvo`
- Type names: `ChatKeys`, `ChatParams`
- Config filename: `chat.yaml` (upstream convention)
- Env var: `CHAT_LOG_DIR`
## Branding Cleanup Log
All user-visible `LibreChat` / `librechat.ai` references replaced with Hanzo equivalents:
- All `librechat.ai` URLs -> `hanzo.ai/docs/chat/...`
- `code.librechat.ai` -> `hanzo.ai/docs/chat/code-interpreter/...`
All user-visible `Chat` / `chat.ai` references replaced with Hanzo equivalents:
- All `chat.ai` URLs -> `hanzo.ai/docs/chat/...`
- `code.chat.ai` -> `hanzo.ai/docs/chat/code-interpreter/...`
- package.json repo URLs -> `github.com/hanzoai/chat`
- package.json homepages -> `hanzo.ai/chat`
- package.json descriptions -> "Hanzo Chat"
@@ -317,6 +317,6 @@ All user-visible `LibreChat` / `librechat.ai` references replaced with Hanzo equ
- Docker Compose MongoDB DB name -> `HanzoChat`
- GitHub workflow repo refs -> `hanzoai/chat`
- MCP User-Agent -> `HanzoChat-MCP-Client`
- JSDoc comments: LibreChat -> Hanzo Chat
- Log messages: LibreChat -> Hanzo Chat
- JSDoc comments: Chat -> Hanzo Chat
- Log messages: Chat -> Hanzo Chat
- Helm chart URLs -> hanzo.ai/docs/chat/...
+1 -1
View File
@@ -134,7 +134,7 @@ build-static-local:
@echo "$(GREEN)Building static SPA locally...$(NC)"
@cp .env.static .env
@pnpm run build:data-provider
@pnpm run --filter @librechat/client build
@pnpm run --filter @hanzochat/client build
@cd client && pnpm run build
@cp .env.backup .env 2>/dev/null || true
@echo "$(GREEN)Local build complete: client/dist/$(NC)"
+2 -2
View File
@@ -1,6 +1,6 @@
chat
Copyright (c) 2023 Hanzo AI Inc.
This product includes software from LibreChat (https://github.com/danny-avila/LibreChat), licensed under MIT:
This product includes software from Chat (https://github.com/danny-avila/Chat), licensed under MIT:
Copyright (c) 2023 LibreChat (Danny Avila)
Copyright (c) 2023 Chat (Danny Avila)
+54 -137
View File
@@ -1,168 +1,85 @@
<p align="center"><img src=".github/hero.svg" alt="chat" width="880"></p>
<p align="center"><img src=".github/hero.svg" alt="Hanzo Chat" width="880"></p>
# Hanzo AI Chat
# Hanzo Chat
AI-powered chat platform with enterprise features, using Hanzo's cloud API or local deployment.
The chat surface of the Hanzo AI cloud: multi-model chat with agents, tools, and retrieval, running on Hanzo's backend. Live at [hanzo.chat](https://hanzo.chat).
## Quick Start
Hanzo Chat is a sibling to [hanzo.app](https://hanzo.app) (the app builder) and the Hanzo console (admin). All inference, code execution, and web search route through the unified Hanzo API at `api.hanzo.ai/v1`, and sign-in is federated to Hanzo IAM ([hanzo.id](https://hanzo.id)).
## Features
- **Multi-model chat** — the Zen model family and other frontier models, served through `api.hanzo.ai`.
- **Agents** — build agents in the thread, or run your Hanzo Cloud agents (`/v1/agents`) with an `/agent` command or `@mention`.
- **MCP tools** — connect Model Context Protocol servers for tool use.
- **RAG** — chat over your own files and documents.
- **Web search** — grounded answers via Hanzo web search.
- **Code interpreter** — run code in a sandboxed runtime.
- **Image generation** — generate images inline.
- **Guest chat** — try a free Zen model with no account (optional, off by default).
## Requirements
- Node.js 24 (see `.nvmrc`)
- pnpm 10
- A Hanzo API key — get one at [hanzo.ai/dashboard](https://hanzo.ai/dashboard)
## Quick start (Docker)
```bash
# Clone and setup
git clone https://github.com/hanzoai/chat.git
cd chat
# Copy environment template
cp .env.example .env
# Edit .env and add your Hanzo API key
# Get your key at: https://hanzo.ai/dashboard
nano .env
# Start the platform
cp .env.example .env # set HANZO_API_KEY
make up
```
Access the chat at http://localhost:3081
Open http://localhost:3080. `make up` starts the full stack (app, MongoDB, Meilisearch) from `compose.yml`; `make down` stops it.
## Development
### Basic Development (with hot reload)
```bash
make dev
pnpm install # install workspace dependencies
pnpm build:packages # build the shared workspace packages
pnpm backend:dev # API server on :3080 (nodemon)
pnpm frontend:dev # Vite client dev server (second terminal)
```
### Full Development (with local router)
Tests and checks:
```bash
# Set your LLM API keys
export ANTHROPIC_API_KEY=your-key
export OPENAI_API_KEY=your-key
# Start full dev stack
make dev-full
```
## Architecture
```
┌─────────────────────────┐ ┌─────────────────────────┐
│ Hanzo Chat UI │────▶│ api.hanzo.ai │
│ (Hanzo Chat) │ │ (or local router) │
│ localhost:3081 │ │ │
└─────────────────────────┘ │ • 100+ AI Models │
│ │ • MCP Tools │
│ │ • Code Execution │
▼ └─────────────────────────┘
┌─────────────────────────┐
│ Local Data Storage │
│ • MongoDB (chat history)│
│ • Meilisearch (search) │
└─────────────────────────┘
pnpm test:all # all workspace tests
pnpm e2e # Playwright end-to-end tests
pnpm lint # ESLint
pnpm format # Prettier
```
## Configuration
### Required Environment Variables
Secrets live in `.env`; the model catalog and endpoints live in `chat.yaml` (copy `chat.example.yaml`). Key variables:
```env
# Your Hanzo API key (required)
OPENAI_API_KEY=sk-hanzo-your-key-here
# API endpoint (default: Hanzo cloud)
OPENAI_BASE_URL=https://api.hanzo.ai/v1
# Features
MCP_ENABLED=true
ALLOW_REGISTRATION=true
HANZO_API_KEY= # Hanzo API key — inference, tools, search
MONGO_URI= # MongoDB connection — chat history, users
JWT_SECRET= # session token signing
CREDS_KEY= # credential encryption
CREDS_IV=
```
### Optional Customization
Sign-in is federated to Hanzo IAM over OpenID Connect (`OPENID_ISSUER=https://hanzo.id`, client `hanzo-chat`).
```env
# Branding
APP_TITLE=My AI Assistant
CUSTOM_FOOTER=Powered by Hanzo AI
## Workspace
# Security
JWT_SECRET=your-secret-key
```
api/ Express backend (:3080) — routes, controllers, Mongoose models
client/ React frontend (Vite)
packages/ data-provider · data-schemas · api · client · agents · mcp
```
## Commands
## Documentation
### Basic Usage
```bash
make up # Start services
make down # Stop services
make logs # View logs
make status # Check health
make clean # Remove all data
```
### Development
```bash
make dev # Dev mode with hot reload
make build # Build containers
make test # Run tests
make lint # Check code quality
make format # Format code
```
### Production
```bash
make prod # Deploy with Traefik
make backup # Backup database
```
## Docker Compose Structure
- `compose.yml` - Base configuration for local development
- `compose.dev.yml` - Development overrides (hot reload, local router)
- `compose.prod.yml` - Production overrides (Traefik, security)
## Features
- 🤖 **100+ AI Models** via Hanzo Router
- 💬 **Clean Chat UI** with modern design
- 🔍 **Full-Text Search** with Meilisearch
- 📝 **Persistent Chat History**
- 🛠️ **MCP Tools** for enhanced capabilities
- 🚀 **Code Execution** via secure runtime
- 🔐 **Enterprise Security** with JWT auth
## Troubleshooting
### Chat not loading
```bash
# Check service status
make status
# View logs
make logs-chat
# Verify API key
echo $OPENAI_API_KEY
```
### Database issues
```bash
# Reset database
make db-reset
# Export data
make db-export
# Import data
make db-import FILE=backup.json
```
## Additional Documentation
- [Production Deployment](./docs/production-domains.md)
- [IAM Integration](./docs/iam-integration.md)
- [Platform Overview](./docs/platform-overview.md)
- [Demo User Guide](./docs/demo-user.md)
## Support
- Documentation: https://docs.hanzo.ai
- Docs: https://hanzo.ai/docs/chat
- Deployment, IAM, and platform notes: [`docs/`](./docs)
- Issues: https://github.com/hanzoai/chat/issues
- Discord: https://discord.gg/hanzoai# CI Test
## License
MIT. Forked from LibreChat (MIT). See [LICENSE](./LICENSE).
+61 -203
View File
@@ -1,227 +1,85 @@
<!-- Last synced with README.md: 2026-03-28 (cae3888) -->
<p align="center"><img src=".github/hero.svg" alt="Hanzo Chat" width="880"></p>
<p align="center">
<a href="https://librechat.ai">
<img src="client/public/assets/logo.svg" height="256">
</a>
<h1 align="center">
<a href="https://librechat.ai">LibreChat</a>
</h1>
</p>
# Hanzo Chat
<p align="center">
<a href="README.md">English</a> ·
<strong>中文</strong>
</p>
Hanzo AI 云的对话入口:多模型对话,内置智能体、工具与检索增强,全部运行在 Hanzo 的后端之上。已上线:[hanzo.chat](https://hanzo.chat)。
<p align="center">
<a href="https://discord.librechat.ai">
<img
src="https://img.shields.io/discord/1086345563026489514?label=&logo=discord&style=for-the-badge&logoWidth=20&logoColor=white&labelColor=000000&color=blueviolet">
</a>
<a href="https://www.youtube.com/@LibreChat">
<img
src="https://img.shields.io/badge/YOUTUBE-red.svg?style=for-the-badge&logo=youtube&logoColor=white&labelColor=000000&logoWidth=20">
</a>
<a href="https://docs.librechat.ai">
<img
src="https://img.shields.io/badge/DOCS-blue.svg?style=for-the-badge&logo=read-the-docs&logoColor=white&labelColor=000000&logoWidth=20">
</a>
<a aria-label="Sponsors" href="https://github.com/sponsors/danny-avila">
<img
src="https://img.shields.io/badge/SPONSORS-brightgreen.svg?style=for-the-badge&logo=github-sponsors&logoColor=white&labelColor=000000&logoWidth=20">
</a>
</p>
Hanzo Chat 与 [hanzo.app](https://hanzo.app)(应用构建器)和 Hanzo 控制台(管理端)互为姊妹产品。所有推理、代码执行与网页搜索都经由统一的 Hanzo API(`api.hanzo.ai/v1`)完成,登录则联合到 Hanzo IAM([hanzo.id](https://hanzo.id))。
<p align="center">
<a href="https://railway.com/deploy/librechat-official?referralCode=HI9hWz&utm_medium=integration&utm_source=readme&utm_campaign=librechat">
<img src="https://railway.com/button.svg" alt="Deploy on Railway" height="30">
</a>
<a href="https://zeabur.com/templates/0X2ZY8">
<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30"/>
</a>
<a href="https://template.cloud.sealos.io/deploy?templateName=librechat">
<img src="https://raw.githubusercontent.com/labring-actions/templates/main/Deploy-on-Sealos.svg" alt="Deploy on Sealos" height="30">
</a>
</p>
## 功能
<p align="center">
<a href="https://www.librechat.ai/docs/translation">
<img
src="https://img.shields.io/badge/dynamic/json.svg?style=for-the-badge&color=2096F3&label=locize&query=%24.translatedPercentage&url=https://api.locize.app/badgedata/4cb2598b-ed4d-469c-9b04-2ed531a8cb45&suffix=%+translated"
alt="翻译进度">
</a>
</p>
- **多模型对话** — Zen 系列模型及其他前沿模型,通过 `api.hanzo.ai` 提供服务。
- **智能体** — 在对话中直接构建智能体,或用 `/agent` 命令或 `@提及` 运行你的 Hanzo Cloud 智能体(`/v1/agents`)。
- **MCP 工具** — 接入 Model Context Protocol(模型上下文协议)服务器以调用工具。
- **检索增强(RAG)** — 基于你自己的文件和文档进行对话。
- **网页搜索** — 通过 Hanzo 网页搜索获得有据可循的回答。
- **代码解释器** — 在沙箱运行时中执行代码。
- **图像生成** — 在对话中直接生成图像。
- **访客对话** — 无需注册即可试用免费的 Zen 模型(可选,默认关闭)。
## 环境要求
# ✨ 功能
- Node.js 24(见 `.nvmrc`
- pnpm 10
- Hanzo API 密钥 — 在 [hanzo.ai/dashboard](https://hanzo.ai/dashboard) 获取
- 🖥️ **UI 与体验**:受 ChatGPT 启发,并具备更强的设计与功能。
## 快速开始(Docker
- 🤖 **AI 模型选择**
- Anthropic (Claude), AWS Bedrock, OpenAI, Azure OpenAI, Google, Vertex AI, OpenAI Responses API (包含 Azure)
- [自定义端点 (Custom Endpoints)](https://www.librechat.ai/docs/quick_start/custom_endpoints)LibreChat 支持任何兼容 OpenAI 规范的 API,无需代理。
- 兼容[本地与远程 AI 服务商](https://www.librechat.ai/docs/configuration/librechat_yaml/ai_endpoints)
- Ollama, groq, Cohere, Mistral AI, Apple MLX, koboldcpp, together.ai,
- OpenRouter, Helicone, Perplexity, ShuttleAI, Deepseek, Qwen 等。
```bash
git clone https://github.com/hanzoai/chat.git
cd chat
cp .env.example .env # 填入 HANZO_API_KEY
make up
```
- 🔧 **[代码解释器 (Code Interpreter) API](https://www.librechat.ai/docs/features/code_interpreter)**
- 安全的沙箱执行环境,支持 Python, Node.js (JS/TS), Go, C/C++, Java, PHP, Rust 和 Fortran。
- 无缝文件处理:直接上传、处理并下载文件。
- 隐私无忧:完全隔离且安全的执行环境。
打开 http://localhost:3080。`make up` 会从 `compose.yml` 启动完整技术栈(应用、MongoDB、Meilisearch);`make down` 停止。
- 🔦 **智能体与工具集成**
- **[LibreChat 智能体 (Agents)](https://www.librechat.ai/docs/features/agents)**
- 无代码定制助手:无需编程即可构建专业化的 AI 驱动助手。
- 智能体市场:发现并部署社区构建的智能体。
- 协作共享:与特定用户和群组共享智能体。
- 灵活且可扩展:支持 MCP 服务器、工具、文件搜索、代码执行等。
- 兼容自定义端点、OpenAI, Azure, Anthropic, AWS Bedrock, Google, Vertex AI, Responses API 等。
- [支持模型上下文协议 (MCP)](https://modelcontextprotocol.io/clients#librechat) 用于工具调用。
## 开发
- 🔍 **网页搜索**
- 搜索互联网并检索相关信息以增强 AI 上下文。
- 结合搜索提供商、内容爬虫和结果重排序,确保最佳检索效果。
- **可定制 Jina 重排序**:配置自定义 Jina API URL 用于重排序服务。
- **[了解更多 →](https://www.librechat.ai/docs/features/web_search)**
```bash
pnpm install # 安装工作区依赖
pnpm build:packages # 构建共享的工作区包
pnpm backend:dev # API 服务,端口 :3080nodemon
pnpm frontend:dev # Vite 客户端开发服务器(另开一个终端)
```
- 🪄 **支持代码 Artifacts 的生成式 UI**
- [代码 Artifacts](https://youtu.be/GfTj7O4gmd0?si=WJbdnemZpJzBrJo3) 允许在对话中直接创建 React 组件、HTML 页面和 Mermaid 图表。
测试与检查:
- 🎨 **图像生成与编辑**
- 使用 [GPT-Image-1](https://www.librechat.ai/docs/features/image_gen#1--openai-image-tools-recommended) 进行文生图与图生图。
- 支持 [DALL-E (3/2)](https://www.librechat.ai/docs/features/image_gen#2--dalle-legacy), [Stable Diffusion](https://www.librechat.ai/docs/features/image_gen#3--stable-diffusion-local), [Flux](https://www.librechat.ai/docs/features/image_gen#4--flux) 或任何 [MCP 服务器](https://www.librechat.ai/docs/features/image_gen#5--model-context-protocol-mcp)。
- 根据提示词生成惊艳的视觉效果,或通过指令精修现有图像。
```bash
pnpm test:all # 全部工作区测试
pnpm e2e # Playwright 端到端测试
pnpm lint # ESLint
pnpm format # Prettier
```
- 💾 **预设与上下文管理**
- 创建、保存并分享自定义预设。
- 在对话中随时切换 AI 端点和预设。
- 编辑、重新提交并通过对话分支继续消息。
- 创建并与特定用户和群组共享提示词。
- [消息与对话分叉 (Fork)](https://www.librechat.ai/docs/features/fork) 以实现高级上下文控制。
## 配置
- 💬 **多模态与文件交互**
- 使用 Claude 3, GPT-4.5, GPT-4o, o1, Llama-Vision 和 Gemini 上传并分析图像 📸。
- 支持通过自定义端点、OpenAI, Azure, Anthropic, AWS Bedrock 和 Google 进行文件对话 🗃️。
密钥存放在 `.env` 中;模型目录与端点配置位于 `chat.yaml`(复制 `chat.example.yaml`)。主要变量:
- 🌎 **多语言 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, ไทย, ئۇيغۇرچە
```env
HANZO_API_KEY= # Hanzo API 密钥 — 推理、工具、搜索
MONGO_URI= # MongoDB 连接 — 对话历史、用户
JWT_SECRET= # 会话令牌签名
CREDS_KEY= # 凭据加密
CREDS_IV=
```
- 🧠 **推理 UI**
- 针对 DeepSeek-R1 等思维链/推理 AI 模型的动态推理 UI。
登录通过 OpenID Connect 联合到 Hanzo IAM`OPENID_ISSUER=https://hanzo.id`,客户端 `hanzo-chat`)。
- 🎨 **可定制界面**
- 可定制的下拉菜单和界面,同时适配高级用户和初学者。
## 工作区结构
- 🌊 **[可恢复流 (Resumable Streams)](https://www.librechat.ai/docs/features/resumable_streams)**
- 永不丢失响应:AI 响应在连接中断后自动重连并继续。
- 多标签页与多设备同步:在多个标签页打开同一对话,或在另一设备上继续。
- 生产级可靠性:支持从单机部署到基于 Redis 的水平扩展。
```
api/ Express 后端(:3080)— 路由、控制器、Mongoose 模型
client/ React 前端(Vite
packages/ data-provider · data-schemas · api · client · agents · mcp
```
- 🗣️ **语音与音频**
- 通过语音转文字和文字转语音实现免提对话。
- 自动发送并播放音频。
- 支持 OpenAI, Azure OpenAI 和 Elevenlabs。
## 文档
- 📥 **导入与导出对话**
- 从 LibreChat, ChatGPT, Chatbot UI 导入对话。
- 将对话导出为截图、Markdown、文本、JSON。
- 文档:https://hanzo.ai/docs/chat
- 部署、IAM 与平台说明:[`docs/`](./docs)
- 问题反馈:https://github.com/hanzoai/chat/issues
- 🔍 **搜索与发现**
- 搜索所有消息和对话。
## 许可证
- 👥 **多用户与安全访问**
- 支持 OAuth2, LDAP 和电子邮件登录的多用户安全认证。
- 内置审核系统和 Token 消耗管理工具。
- ⚙️ **配置与部署**
- 支持代理、反向代理、Docker 及多种部署选项。
- 可完全本地运行或部署在云端。
- 📖 **开源与社区**
- 完全开源且在公众监督下开发。
- 社区驱动的开发、支持与反馈。
[查看我们的文档了解更多功能详情](https://docs.librechat.ai/) 📚
## 🪶 LibreChat:全方位的 AI 对话平台
LibreChat 是一个自托管的 AI 对话平台,在一个注重隐私的统一界面中整合了所有主流 AI 服务商。
除了对话功能外,LibreChat 还提供 AI 智能体、模型上下文协议 (MCP) 支持、Artifacts、代码解释器、自定义操作、对话搜索,以及企业级多用户认证。
开源、活跃开发中,专为重视 AI 基础设施自主可控的用户而构建。
---
## 🌐 资源
**GitHub 仓库:**
- **RAG API:** [github.com/danny-avila/rag_api](https://github.com/danny-avila/rag_api)
- **网站:** [github.com/LibreChat-AI/librechat.ai](https://github.com/LibreChat-AI/librechat.ai)
**其他:**
- **官方网站:** [librechat.ai](https://librechat.ai)
- **帮助文档:** [librechat.ai/docs](https://librechat.ai/docs)
- **博客:** [librechat.ai/blog](https://librechat.ai/blog)
---
## 📝 更新日志
访问发布页面和更新日志以了解最新动态:
- [发布页面 (Releases)](https://github.com/danny-avila/LibreChat/releases)
- [更新日志 (Changelog)](https://www.librechat.ai/changelog)
**⚠️ 在更新前请务必查看[更新日志](https://www.librechat.ai/changelog)以了解破坏性更改。**
---
## ⭐ Star 历史
<p align="center">
<a href="https://star-history.com/#danny-avila/LibreChat&Date">
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=danny-avila/LibreChat&type=Date&theme=dark" onerror="this.src='https://api.star-history.com/svg?repos=danny-avila/LibreChat&type=Date'" />
</a>
</p>
<p align="center">
<a href="https://trendshift.io/repositories/4685" target="_blank" style="padding: 10px;">
<img src="https://trendshift.io/api/badge/repositories/4685" alt="danny-avila%2FLibreChat | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/>
</a>
<a href="https://runacap.com/ross-index/q1-24/" target="_blank" rel="noopener" style="margin-left: 20px;">
<img style="width: 260px; height: 56px" src="https://runacap.com/wp-content/uploads/2024/04/ROSS_badge_white_Q1_2024.svg" alt="ROSS Index - 2024年第一季度增长最快的开源初创公司 | Runa Capital" width="260" height="56"/>
</a>
</p>
---
## ✨ 贡献
欢迎任何形式的贡献、建议、错误报告和修复!
对于新功能、组件或扩展,请在发送 PR 前开启 issue 进行讨论。
如果您想帮助我们将 LibreChat 翻译成您的母语,我们非常欢迎!改进翻译不仅能让全球用户更轻松地使用 LibreChat,还能提升整体用户体验。请查看我们的[翻译指南](https://www.librechat.ai/docs/translation)。
---
## 💖 感谢所有贡献者
<a href="https://github.com/danny-avila/LibreChat/graphs/contributors">
<img src="https://contrib.rocks/image?repo=danny-avila/LibreChat" />
</a>
---
## 🎉 特别鸣谢
感谢 [Locize](https://locize.com) 提供的翻译管理工具,支持 LibreChat 的多语言功能。
<p align="center">
<a href="https://locize.com" target="_blank" rel="noopener noreferrer">
<img src="https://github.com/user-attachments/assets/d6b70894-6064-475e-bb65-92a9e23e0077" alt="Locize Logo" height="50">
</a>
</p>
MIT 许可证。基于 LibreChatMIT)分支开发。见 [LICENSE](./LICENSE)。
+3 -3
View File
@@ -1,6 +1,6 @@
const crypto = require('crypto');
const fetch = require('node-fetch');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const {
countTokens,
getBalanceConfig,
@@ -20,7 +20,7 @@ const {
isAgentsEndpoint,
isEphemeralAgentId,
supportsBalanceCheck,
} = require('librechat-data-provider');
} = require('@hanzochat/data-provider');
const {
updateMessage,
getMessages,
@@ -76,7 +76,7 @@ class BaseClient {
this.fetchedConvo;
/** @type {TMessage[]} */
this.currentMessages = [];
/** @type {import('librechat-data-provider').VisionModes | undefined} */
/** @type {import('@hanzochat/data-provider').VisionModes | undefined} */
this.visionMode;
}
+3 -3
View File
@@ -1,9 +1,9 @@
const { z } = require('zod');
const axios = require('axios');
const { Ollama } = require('ollama');
const { sleep } = require('@librechat/agents');
const { logger } = require('@librechat/data-schemas');
const { Constants } = require('librechat-data-provider');
const { sleep } = require('@hanzochat/agents');
const { logger } = require('@hanzochat/data-schemas');
const { Constants } = require('@hanzochat/data-provider');
const { resolveHeaders, deriveBaseURL } = require('@hanzochat/api');
const ollamaPayloadSchema = z.object({
+1 -1
View File
@@ -1,5 +1,5 @@
const { Readable } = require('stream');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
class TextStream extends Readable {
constructor(text, options = {}) {
+1 -1
View File
@@ -1,5 +1,5 @@
const dedent = require('dedent');
const { EModelEndpoint, ArtifactModes } = require('librechat-data-provider');
const { EModelEndpoint, ArtifactModes } = require('@hanzochat/data-provider');
const { generateShadcnPrompt } = require('~/app/clients/prompts/shadcn-docs/generate');
const { components } = require('~/app/clients/prompts/shadcn-docs/components');
@@ -1,5 +1,5 @@
const axios = require('axios');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { isEnabled, generateShortLivedToken } = require('@hanzochat/api');
const footer = `Use the context as your learned knowledge to better answer the user.
@@ -1,5 +1,5 @@
const { ToolMessage } = require('@langchain/core/messages');
const { ContentTypes } = require('librechat-data-provider');
const { ContentTypes } = require('@hanzochat/data-provider');
const { HumanMessage, AIMessage, SystemMessage } = require('@langchain/core/messages');
const { formatAgentMessages } = require('./formatMessages');
+1 -1
View File
@@ -1,5 +1,5 @@
const { ToolMessage } = require('@langchain/core/messages');
const { EModelEndpoint, ContentTypes } = require('librechat-data-provider');
const { EModelEndpoint, ContentTypes } = require('@hanzochat/data-provider');
const { HumanMessage, AIMessage, SystemMessage } = require('@langchain/core/messages');
/**
@@ -1,4 +1,4 @@
const { Constants } = require('librechat-data-provider');
const { Constants } = require('@hanzochat/data-provider');
const { HumanMessage, AIMessage, SystemMessage } = require('@langchain/core/messages');
const { formatMessage, formatLangChainMessages, formatFromLangChain } = require('./formatMessages');
+3 -3
View File
@@ -1,4 +1,4 @@
const { Constants } = require('librechat-data-provider');
const { Constants } = require('@hanzochat/data-provider');
const { initializeFakeClient } = require('./FakeClient');
jest.mock('~/db/connect');
@@ -40,8 +40,8 @@ jest.mock('~/models', () => ({
const { getConvo, saveConvo } = require('~/models');
jest.mock('@librechat/agents', () => {
const actual = jest.requireActual('@librechat/agents');
jest.mock('@hanzochat/agents', () => {
const actual = jest.requireActual('@hanzochat/agents');
return {
...actual,
ChatOpenAI: jest.fn().mockImplementation(() => {
@@ -1,5 +1,5 @@
const { Tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { SearchClient, AzureKeyCredential } = require('@azure/search-documents');
const azureAISearchJsonSchema = {
+2 -2
View File
@@ -3,9 +3,9 @@ const OpenAI = require('openai');
const { v4: uuidv4 } = require('uuid');
const { ProxyAgent, fetch } = require('undici');
const { Tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { getImageBasename, extractBaseURL } = require('@hanzochat/api');
const { FileContext, ContentTypes } = require('librechat-data-provider');
const { FileContext, ContentTypes } = require('@hanzochat/data-provider');
const dalle3JsonSchema = {
type: 'object',
+2 -2
View File
@@ -2,9 +2,9 @@ const axios = require('axios');
const fetch = require('node-fetch');
const { v4: uuidv4 } = require('uuid');
const { Tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { HttpsProxyAgent } = require('https-proxy-agent');
const { FileContext, ContentTypes } = require('librechat-data-provider');
const { FileContext, ContentTypes } = require('@hanzochat/data-provider');
const fluxApiJsonSchema = {
type: 'object',
@@ -5,13 +5,13 @@ const { v4 } = require('uuid');
const { ProxyAgent } = require('undici');
const { GoogleGenAI } = require('@google/genai');
const { tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const {
FileContext,
ContentTypes,
FileSources,
EImageOutputType,
} = require('librechat-data-provider');
} = require('@hanzochat/data-provider');
const {
geminiToolkit,
loadServiceKey,
@@ -4,9 +4,9 @@ const OpenAI = require('openai');
const FormData = require('form-data');
const { ProxyAgent } = require('undici');
const { tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { HttpsProxyAgent } = require('https-proxy-agent');
const { ContentTypes, EImageOutputType } = require('librechat-data-provider');
const { ContentTypes, EImageOutputType } = require('@hanzochat/data-provider');
const { logAxiosError, oaiToolkit, extractBaseURL } = require('@hanzochat/api');
const { getStrategyFunctions } = require('~/server/services/Files/strategies');
const { getFiles } = require('~/models');
@@ -5,8 +5,8 @@ const axios = require('axios');
const sharp = require('sharp');
const { v4: uuidv4 } = require('uuid');
const { Tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
const { FileContext, ContentTypes } = require('librechat-data-provider');
const { logger } = require('@hanzochat/data-schemas');
const { FileContext, ContentTypes } = require('@hanzochat/data-provider');
const { getBasePath } = require('@hanzochat/api');
const paths = require('~/config/paths');
@@ -1,5 +1,5 @@
const { Tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { getEnvironmentVariable } = require('@langchain/core/utils/env');
const traversaalSearchJsonSchema = {
+1 -1
View File
@@ -1,7 +1,7 @@
/* eslint-disable no-useless-escape */
const axios = require('axios');
const { Tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const wolframJsonSchema = {
type: 'object',
@@ -1,9 +1,9 @@
const OpenAI = require('openai');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const DALLE3 = require('../DALLE3');
jest.mock('openai');
jest.mock('@librechat/data-schemas', () => {
jest.mock('@hanzochat/data-schemas', () => {
return {
logger: {
info: jest.fn(),
@@ -12,8 +12,8 @@ const axios = require('axios');
const OpenAI = require('openai');
const undici = require('undici');
const fetch = require('node-fetch');
const { ContentTypes } = require('librechat-data-provider');
const { ToolMessage } = require('@librechat/agents/langchain/messages');
const { ContentTypes } = require('@hanzochat/data-provider');
const { ToolMessage } = require('@hanzochat/agents/langchain/messages');
const StableDiffusionAPI = require('../StableDiffusion');
const FluxAPI = require('../FluxAPI');
const DALLE3 = require('../DALLE3');
@@ -25,7 +25,7 @@ jest.mock('undici', () => ({
ProxyAgent: jest.fn(),
fetch: jest.fn(),
}));
jest.mock('@librechat/data-schemas', () => ({
jest.mock('@hanzochat/data-schemas', () => ({
logger: { info: jest.fn(), warn: jest.fn(), debug: jest.fn(), error: jest.fn() },
}));
jest.mock('path', () => ({
+3 -3
View File
@@ -1,8 +1,8 @@
const axios = require('axios');
const { tool } = require('@langchain/core/tools');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { generateShortLivedToken } = require('@hanzochat/api');
const { Tools, EToolResources } = require('librechat-data-provider');
const { Tools, EToolResources } = require('@hanzochat/data-provider');
const { filterFilesByAgentAccess } = require('~/server/services/Files/permissions');
const { getFiles } = require('~/models');
@@ -97,7 +97,7 @@ const createFileSearchTool = async ({ userId, files, entity_id, fileCitations =
}
/**
* @param {import('librechat-data-provider').TFile} file
* @param {import('@hanzochat/data-provider').TFile} file
* @returns {{ file_id: string, query: string, k: number, entity_id?: string }}
*/
const createQueryBody = (file) => {
+15 -16
View File
@@ -1,10 +1,10 @@
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const {
EnvVar,
Calculator,
createSearchTool,
createCodeExecutionTool,
} = require('@librechat/agents');
} = require('@hanzochat/agents');
const {
checkAccess,
createSafeUser,
@@ -12,8 +12,8 @@ const {
loadWebSearchAuth,
buildImageToolContext,
buildWebSearchContext,
resolveHanzoCloudKey,
isHanzoPerUserKeyEnabled,
resolveTenantBearer,
OPENID_BEARER_SENTINEL,
} = require('@hanzochat/api');
const { getMCPServersRegistry } = require('~/config');
const {
@@ -22,7 +22,7 @@ const {
Permissions,
EToolResources,
PermissionTypes,
} = require('librechat-data-provider');
} = require('@hanzochat/data-provider');
const {
availableTools,
manifestToolMap,
@@ -243,18 +243,17 @@ const loadTools = async ({
customConstructors.dalle = async () => {
const authFields = getAuthFields('dalle');
const authValues = await loadAuthValues({ userId: user, authFields });
const billingUser = options.req?.user;
const isAuthenticatedUser = Boolean(
billingUser && !billingUser.guest && billingUser.email,
);
if (isHanzoPerUserKeyEnabled() && isAuthenticatedUser) {
const perUserKey = await resolveHanzoCloudKey(billingUser);
if (!perUserKey) {
throw new Error(
'Your Hanzo Cloud account is not linked for billing yet. Please sign out and back in, then claim your starter credit at https://billing.hanzo.ai',
);
// Canonical Hanzo Cloud billing (mirrors custom/initialize.ts): when the
// image endpoint is configured to forward the user's IAM bearer
// (DALLE3_API_KEY === the OIDC-token sentinel), resolve the signed-in
// user's own IAM token and forward it so cloud meters THEIR org. Fail
// closed if there is no forwardable bearer — no shared key to spend.
if (authValues.DALLE3_API_KEY === OPENID_BEARER_SENTINEL) {
const bearer = resolveTenantBearer(options.req);
if (!bearer) {
throw new Error('Sign in with Hanzo to generate images — your Hanzo account funds this request.');
}
authValues.DALLE3_API_KEY = perUserKey;
authValues.DALLE3_API_KEY = bearer;
}
return new DALLE3({ ...imageGenOptions, ...authValues, userId: user });
};
@@ -30,7 +30,7 @@ jest.mock('~/server/services/Config', () => ({
}),
}));
const { Calculator } = require('@librechat/agents');
const { Calculator } = require('@hanzochat/agents');
const { User } = require('~/db/models');
const PluginService = require('~/server/services/PluginService');
+2 -2
View File
@@ -1,6 +1,6 @@
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { isEnabled, math } = require('@hanzochat/api');
const { ViolationTypes } = require('librechat-data-provider');
const { ViolationTypes } = require('@hanzochat/data-provider');
const { deleteAllUserSessions } = require('~/models');
const { removePorts } = require('~/server/utils');
const getLogStores = require('./getLogStores');
+1 -1
View File
@@ -1,5 +1,5 @@
const { isEnabled } = require('@hanzochat/api');
const { Time, CacheKeys } = require('librechat-data-provider');
const { Time, CacheKeys } = require('@hanzochat/data-provider');
const getLogStores = require('./getLogStores');
const { USE_REDIS, LIMIT_CONCURRENT_MESSAGES } = process.env ?? {};
+1 -1
View File
@@ -1,5 +1,5 @@
const { Keyv } = require('keyv');
const { Time, CacheKeys, ViolationTypes } = require('librechat-data-provider');
const { Time, CacheKeys, ViolationTypes } = require('@hanzochat/data-provider');
const {
logFile,
keyvMongo,
+1 -1
View File
@@ -1,5 +1,5 @@
const { isEnabled } = require('@hanzochat/api');
const { ViolationTypes } = require('librechat-data-provider');
const { ViolationTypes } = require('@hanzochat/data-provider');
const getLogStores = require('./getLogStores');
const banViolation = require('./banViolation');
+1 -1
View File
@@ -3,7 +3,7 @@ const fs = require('fs');
const ORIGINAL_ENV = process.env;
const mockDataSchemas = () => {
jest.doMock('@librechat/data-schemas', () => ({
jest.doMock('@hanzochat/data-schemas', () => ({
getTenantId: jest.fn(),
getUserId: jest.fn(),
getRequestId: jest.fn(),
+1 -1
View File
@@ -1,5 +1,5 @@
const { EventSource } = require('eventsource');
const { Time } = require('librechat-data-provider');
const { Time } = require('@hanzochat/data-provider');
const {
MCPManager,
FlowStateManager,
+3 -3
View File
@@ -6,13 +6,13 @@ require('winston-daily-rotate-file');
/**
* Determine the log directory.
* Priority:
* 1. LIBRECHAT_LOG_DIR environment variable (allows user override)
* 1. CHAT_LOG_DIR environment variable (allows user override)
* 2. /app/logs if running in Docker (bind-mounted with correct permissions)
* 3. api/logs relative to this file (local development)
*/
const getLogDir = () => {
if (process.env.LIBRECHAT_LOG_DIR) {
return process.env.LIBRECHAT_LOG_DIR;
if (process.env.CHAT_LOG_DIR) {
return process.env.CHAT_LOG_DIR;
}
// Check if running in Docker container (cwd is /app)
+3 -3
View File
@@ -7,13 +7,13 @@ const { redactFormat, redactMessage, debugTraverse, jsonTruncateFormat } = requi
/**
* Determine the log directory.
* Priority:
* 1. LIBRECHAT_LOG_DIR environment variable (allows user override)
* 1. CHAT_LOG_DIR environment variable (allows user override)
* 2. /app/logs if running in Docker (bind-mounted with correct permissions)
* 3. api/logs relative to this file (local development)
*/
const getLogDir = () => {
if (process.env.LIBRECHAT_LOG_DIR) {
return process.env.LIBRECHAT_LOG_DIR;
if (process.env.CHAT_LOG_DIR) {
return process.env.CHAT_LOG_DIR;
}
// Check if running in Docker container (cwd is /app)
+1 -1
View File
@@ -1,6 +1,6 @@
require('dotenv').config();
const { isEnabled } = require('@hanzochat/api');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const mongoose = require('mongoose');
const MONGO_URI = process.env.MONGO_URI;
+1 -1
View File
@@ -1,5 +1,5 @@
const mongoose = require('mongoose');
const { createModels } = require('@librechat/data-schemas');
const { createModels } = require('@hanzochat/data-schemas');
const { connectDb } = require('./connect');
const indexSync = require('./indexSync');
+1 -1
View File
@@ -4,7 +4,7 @@ describe('api/db/index.js', () => {
const callOrder = [];
jest.mock('@librechat/data-schemas', () => ({
jest.mock('@hanzochat/data-schemas', () => ({
createModels: jest.fn((m) => {
callOrder.push('createModels');
m.models.Message = { name: 'Message' };
+2 -2
View File
@@ -1,7 +1,7 @@
const mongoose = require('mongoose');
const { MeiliSearch } = require('meilisearch');
const { logger } = require('@librechat/data-schemas');
const { CacheKeys } = require('librechat-data-provider');
const { logger } = require('@hanzochat/data-schemas');
const { CacheKeys } = require('@hanzochat/data-provider');
const { isEnabled, FlowStateManager } = require('@hanzochat/api');
const { getLogStores } = require('~/cache');
const { batchResetMeiliFlags } = require('./utils');
+1 -1
View File
@@ -32,7 +32,7 @@ const originalMessageModel = mongoose.models.Message;
const originalConversationModel = mongoose.models.Conversation;
// Mock external modules
jest.mock('@librechat/data-schemas', () => ({
jest.mock('@hanzochat/data-schemas', () => ({
logger: mockLogger,
}));
+1 -1
View File
@@ -1,5 +1,5 @@
const mongoose = require('mongoose');
const { createModels } = require('@librechat/data-schemas');
const { createModels } = require('@hanzochat/data-schemas');
const models = createModels(mongoose);
module.exports = { ...models };
+1 -1
View File
@@ -1,4 +1,4 @@
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
+1 -1
View File
@@ -392,7 +392,7 @@ describe('batchResetMeiliFlags', () => {
beforeEach(() => {
// Mock logger.warn to track warning calls
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
warnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {});
});
+5 -5
View File
@@ -1,6 +1,6 @@
const mongoose = require('mongoose');
const crypto = require('node:crypto');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { getCustomEndpointConfig } = require('@hanzochat/api');
const {
Tools,
@@ -10,8 +10,8 @@ const {
isAgentsEndpoint,
isEphemeralAgentId,
encodeEphemeralAgentId,
} = require('librechat-data-provider');
const { mcp_all, mcp_delimiter } = require('librechat-data-provider').Constants;
} = require('@hanzochat/data-provider');
const { mcp_all, mcp_delimiter } = require('@hanzochat/data-provider').Constants;
const {
removeAgentFromAllProjects,
removeAgentIdsFromProject,
@@ -96,7 +96,7 @@ const getAgents = async (searchParameter) => await Agent.find(searchParameter).l
* @param {string} params.spec
* @param {string} params.agent_id
* @param {string} params.endpoint
* @param {import('@librechat/agents').ClientOptions} [params.model_parameters]
* @param {import('@hanzochat/agents').ClientOptions} [params.model_parameters]
* @returns {Promise<Agent|null>} The agent document as a plain object, or null if not found.
*/
const loadEphemeralAgent = async ({ req, spec, endpoint, model_parameters: _m }) => {
@@ -189,7 +189,7 @@ const loadEphemeralAgent = async ({ req, spec, endpoint, model_parameters: _m })
* @param {string} params.spec
* @param {string} params.agent_id
* @param {string} params.endpoint
* @param {import('@librechat/agents').ClientOptions} [params.model_parameters]
* @param {import('@hanzochat/agents').ClientOptions} [params.model_parameters]
* @returns {Promise<Agent|null>} The agent document as a plain object, or null if not found.
*/
const loadAgent = async ({ req, spec, agent_id, endpoint, model_parameters }) => {
+11 -11
View File
@@ -13,9 +13,9 @@ jest.mock('~/server/services/Config', () => ({
const mongoose = require('mongoose');
const { v4: uuidv4 } = require('uuid');
const { agentSchema } = require('@librechat/data-schemas');
const { agentSchema } = require('@hanzochat/data-schemas');
const { MongoMemoryServer } = require('mongodb-memory-server');
const { AccessRoleIds, ResourceType, PrincipalType } = require('librechat-data-provider');
const { AccessRoleIds, ResourceType, PrincipalType } = require('@hanzochat/data-provider');
const {
getAgent,
loadAgent,
@@ -35,7 +35,7 @@ const { getCachedTools, getMCPServerTools } = require('~/server/services/Config'
const { AclEntry, User } = require('~/db/models');
/**
* @type {import('mongoose').Model<import('@librechat/data-schemas').IAgent>}
* @type {import('mongoose').Model<import('@hanzochat/data-schemas').IAgent>}
*/
let Agent;
@@ -1111,7 +1111,7 @@ describe('models/Agent', () => {
const agentId = 'ephemeral_test';
const endpoint = 'openai';
const originalModule = jest.requireActual('librechat-data-provider');
const originalModule = jest.requireActual('@hanzochat/data-provider');
const mockDataProvider = {
...originalModule,
@@ -1121,12 +1121,12 @@ describe('models/Agent', () => {
},
};
jest.doMock('librechat-data-provider', () => mockDataProvider);
jest.doMock('@hanzochat/data-provider', () => mockDataProvider);
expect(agentId).toBeDefined();
expect(endpoint).toBeDefined();
jest.dontMock('librechat-data-provider');
jest.dontMock('@hanzochat/data-provider');
});
test('should handle loadAgent functionality and errors', async () => {
@@ -2391,7 +2391,7 @@ describe('models/Agent', () => {
});
test('should test ephemeral agent loading logic', async () => {
const { EPHEMERAL_AGENT_ID } = require('librechat-data-provider').Constants;
const { EPHEMERAL_AGENT_ID } = require('@hanzochat/data-provider').Constants;
getCachedTools.mockResolvedValue({
tool1_mcp_server1: {},
@@ -2514,7 +2514,7 @@ describe('models/Agent', () => {
});
test('should handle ephemeral agent with no MCP servers', async () => {
const { EPHEMERAL_AGENT_ID } = require('librechat-data-provider').Constants;
const { EPHEMERAL_AGENT_ID } = require('@hanzochat/data-provider').Constants;
getCachedTools.mockResolvedValue({});
@@ -2546,7 +2546,7 @@ describe('models/Agent', () => {
});
test('should handle ephemeral agent with undefined ephemeralAgent in body', async () => {
const { EPHEMERAL_AGENT_ID } = require('librechat-data-provider').Constants;
const { EPHEMERAL_AGENT_ID } = require('@hanzochat/data-provider').Constants;
getCachedTools.mockResolvedValue({});
@@ -2584,7 +2584,7 @@ describe('models/Agent', () => {
});
test('should handle ephemeral agent with extremely large tool list', async () => {
const { EPHEMERAL_AGENT_ID } = require('librechat-data-provider').Constants;
const { EPHEMERAL_AGENT_ID } = require('@hanzochat/data-provider').Constants;
const largeToolList = Array.from({ length: 100 }, (_, i) => `tool_${i}_mcp_server1`);
const availableTools = largeToolList.reduce((acc, tool) => {
@@ -3123,7 +3123,7 @@ describe('models/Agent', () => {
});
test('should handle loadEphemeralAgent with malformed MCP tool names', async () => {
const { EPHEMERAL_AGENT_ID } = require('librechat-data-provider').Constants;
const { EPHEMERAL_AGENT_ID } = require('@hanzochat/data-provider').Constants;
getCachedTools.mockResolvedValue({
malformed_tool_name: {}, // No mcp delimiter
+1 -1
View File
@@ -1,4 +1,4 @@
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { Banner } = require('~/db/models');
/**
+1 -1
View File
@@ -1,4 +1,4 @@
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const options = [
{
+1 -1
View File
@@ -1,4 +1,4 @@
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { createTempChatExpirationDate } = require('@hanzochat/api');
const { getMessages, deleteMessages } = require('./Message');
const { Conversation } = require('~/db/models');
+1 -1
View File
@@ -1,6 +1,6 @@
const mongoose = require('mongoose');
const { v4: uuidv4 } = require('uuid');
const { EModelEndpoint } = require('librechat-data-provider');
const { EModelEndpoint } = require('@hanzochat/data-provider');
const { MongoMemoryServer } = require('mongodb-memory-server');
const {
deleteNullOrEmptyConversations,
+1 -1
View File
@@ -1,4 +1,4 @@
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { ConversationTag, Conversation } = require('~/db/models');
/**
+2 -2
View File
@@ -1,5 +1,5 @@
const { logger } = require('@librechat/data-schemas');
const { EToolResources, FileContext } = require('librechat-data-provider');
const { logger } = require('@hanzochat/data-schemas');
const { EToolResources, FileContext } = require('@hanzochat/data-provider');
const { File } = require('~/db/models');
/**
+2 -2
View File
@@ -1,13 +1,13 @@
const mongoose = require('mongoose');
const { v4: uuidv4 } = require('uuid');
const { MongoMemoryServer } = require('mongodb-memory-server');
const { createModels, createMethods } = require('@librechat/data-schemas');
const { createModels, createMethods } = require('@hanzochat/data-schemas');
const {
SystemRoles,
ResourceType,
AccessRoleIds,
PrincipalType,
} = require('librechat-data-provider');
} = require('@hanzochat/data-provider');
const { grantPermission } = require('~/server/services/PermissionService');
const { createAgent } = require('./Agent');
+1 -1
View File
@@ -1,5 +1,5 @@
const { z } = require('zod');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { createTempChatExpirationDate } = require('@hanzochat/api');
const { Message } = require('~/db/models');
+2 -2
View File
@@ -1,6 +1,6 @@
const mongoose = require('mongoose');
const { v4: uuidv4 } = require('uuid');
const { messageSchema } = require('@librechat/data-schemas');
const { messageSchema } = require('@hanzochat/data-schemas');
const { MongoMemoryServer } = require('mongodb-memory-server');
const {
@@ -16,7 +16,7 @@ const {
jest.mock('~/server/services/Config/app');
/**
* @type {import('mongoose').Model<import('@librechat/data-schemas').IMessage>}
* @type {import('mongoose').Model<import('@hanzochat/data-schemas').IMessage>}
*/
let Message;
+1 -1
View File
@@ -1,4 +1,4 @@
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { Preset } = require('~/db/models');
const getPreset = async (user, presetId) => {
+1 -1
View File
@@ -1,4 +1,4 @@
const { GLOBAL_PROJECT_NAME } = require('librechat-data-provider').Constants;
const { GLOBAL_PROJECT_NAME } = require('@hanzochat/data-provider').Constants;
const { Project } = require('~/db/models');
/**
+2 -2
View File
@@ -1,7 +1,7 @@
const { ObjectId } = require('mongodb');
const { escapeRegExp } = require('@hanzochat/api');
const { logger } = require('@librechat/data-schemas');
const { SystemRoles, ResourceType, SystemCategories } = require('librechat-data-provider');
const { logger } = require('@hanzochat/data-schemas');
const { SystemRoles, ResourceType, SystemCategories } = require('@hanzochat/data-provider');
const {
getSoleOwnedResourceIds,
removeAllPermissions,
+2 -2
View File
@@ -1,6 +1,6 @@
const mongoose = require('mongoose');
const { ObjectId } = require('mongodb');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { MongoMemoryServer } = require('mongodb-memory-server');
const {
SystemRoles,
@@ -8,7 +8,7 @@ const {
AccessRoleIds,
PrincipalType,
PermissionBits,
} = require('librechat-data-provider');
} = require('@hanzochat/data-provider');
// Mock the config/connect module to prevent connection attempts during tests
jest.mock('../../config/connect', () => jest.fn().mockResolvedValue(true));
+2 -2
View File
@@ -1,6 +1,6 @@
const mongoose = require('mongoose');
const { ObjectId } = require('mongodb');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { MongoMemoryServer } = require('mongodb-memory-server');
const {
Constants,
@@ -9,7 +9,7 @@ const {
PrincipalType,
PrincipalModel,
PermissionBits,
} = require('librechat-data-provider');
} = require('@hanzochat/data-provider');
// Mock the config/connect module to prevent connection attempts during tests
jest.mock('../../config/connect', () => jest.fn().mockResolvedValue(true));
+6 -3
View File
@@ -3,8 +3,8 @@ const {
roleDefaults,
permissionsSchema,
removeNullishValues,
} = require('librechat-data-provider');
const { logger } = require('@librechat/data-schemas');
} = require('@hanzochat/data-provider');
const { logger } = require('@hanzochat/data-schemas');
const getLogStores = require('~/cache/getLogStores');
const { Role } = require('~/db/models');
@@ -37,7 +37,10 @@ const getRoleByName = async function (roleName, fieldsToSelect = null) {
// generation for that role died. roleDefaults[roleName] always carries a
// name, so this guard makes the nameless create structurally impossible.
if (!role && roleDefaults[roleName]) {
role = await new Role(roleDefaults[roleName]).save();
// Store-aware: Role resolves to the SQLite DocModel/DualWriteModel under the
// CHAT_STORE_SQLITE flip, where `new Role()` throws "Role is not a constructor".
// `.create()` is the bounded equivalent; it returns the created doc (toObject).
role = await Role.create(roleDefaults[roleName]);
await cache.set(roleName, role);
return role.toObject();
}
+1 -1
View File
@@ -5,7 +5,7 @@ const {
Permissions,
roleDefaults,
PermissionTypes,
} = require('librechat-data-provider');
} = require('@hanzochat/data-provider');
const { getRoleByName, updateAccessPermissions } = require('~/models/Role');
const getLogStores = require('~/cache/getLogStores');
const { initializeRoles } = require('~/models');
+27 -20
View File
@@ -1,4 +1,4 @@
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { getMultiplier, getCacheMultiplier } = require('./tx');
const { Transaction, Balance } = require('~/db/models');
@@ -12,7 +12,7 @@ const cancelRate = 1.15;
* @param {Object} params - The function parameters.
* @param {string|mongoose.Types.ObjectId} params.user - The user ID.
* @param {number} params.incrementValue - The value to increment the balance by (can be negative).
* @param {import('mongoose').UpdateQuery<import('@librechat/data-schemas').IBalance>['$set']} [params.setValues] - Optional additional fields to set.
* @param {import('mongoose').UpdateQuery<import('@hanzochat/data-schemas').IBalance>['$set']} [params.setValues] - Optional additional fields to set.
* @returns {Promise<Object>} Returns the updated balance document (lean).
* @throws {Error} Throws an error if the update fails after multiple retries.
*/
@@ -150,6 +150,28 @@ function calculateTokenValue(txn) {
}
}
/**
* Persists a transaction via the store-aware `Transaction` model (`~/db/models`),
* which resolves to the SQLite DocModel / DualWriteModel under the CHAT_STORE_SQLITE
* flip where the mongoose-document constructor (`new Transaction()`) + `.save()`
* throws "Transaction is not a constructor". The provided calculator mutates the
* working object with the derived fields (rate/tokenValue/rateDetail) BEFORE the
* insert, then the non-schema calculator inputs (endpointTokenConfig/inputTokenCount)
* and rateDetail are stripped so the persisted document matches the mongoose path
* (mongoose drops non-schema paths on save; the document store does not).
* @param {object} txData
* @param {(txn: object) => void} calculate
* @returns {Promise<object>} the created transaction document
*/
async function persistTransaction(txData, calculate) {
const txn = { ...txData };
calculate(txn);
delete txn.endpointTokenConfig;
delete txn.inputTokenCount;
delete txn.rateDetail;
return Transaction.create(txn);
}
/**
* New static method to create an auto-refill transaction that does NOT trigger a balance update.
* @param {object} txData - Transaction data.
@@ -163,11 +185,7 @@ async function createAutoRefillTransaction(txData) {
if (txData.rawAmount != null && isNaN(txData.rawAmount)) {
return;
}
const transaction = new Transaction(txData);
transaction.endpointTokenConfig = txData.endpointTokenConfig;
transaction.inputTokenCount = txData.inputTokenCount;
calculateTokenValue(transaction);
await transaction.save();
const transaction = await persistTransaction(txData, calculateTokenValue);
const balanceResponse = await updateBalance({
user: transaction.user,
@@ -198,12 +216,7 @@ async function createTransaction(_txData) {
return;
}
const transaction = new Transaction(txData);
transaction.endpointTokenConfig = txData.endpointTokenConfig;
transaction.inputTokenCount = txData.inputTokenCount;
calculateTokenValue(transaction);
await transaction.save();
const transaction = await persistTransaction(txData, calculateTokenValue);
if (!balance?.enabled) {
return;
}
@@ -240,13 +253,7 @@ async function createStructuredTransaction(_txData) {
return;
}
const transaction = new Transaction(txData);
transaction.endpointTokenConfig = txData.endpointTokenConfig;
transaction.inputTokenCount = txData.inputTokenCount;
calculateStructuredTokenValue(transaction);
await transaction.save();
const transaction = await persistTransaction(txData, calculateStructuredTokenValue);
if (!balance?.enabled) {
return;
+8 -23
View File
@@ -1,6 +1,5 @@
const { logger } = require('@librechat/data-schemas');
const { ViolationTypes } = require('librechat-data-provider');
const { billingSubject } = require('@hanzochat/api');
const { logger } = require('@hanzochat/data-schemas');
const { ViolationTypes } = require('@hanzochat/data-provider');
const { createAutoRefillTransaction } = require('./Transaction');
const { logViolation } = require('~/cache');
const { getMultiplier } = require('./tx');
@@ -95,26 +94,12 @@ const checkBalanceRecord = async function ({
const multiplier = getMultiplier({ valueKey, tokenType, model, endpoint, endpointTokenConfig });
const tokenCost = amount * multiplier;
// Guests (anonymous preview) are NOT balance-gated here. Their spend is bounded
// two ways, neither of which is an authed user's org balance: (1) the per-IP
// guest message limiter (GUEST_MESSAGE_MAX, default 3) and (2) the separate,
// small-capped, NON-exempt guest key (HANZO_API_KEY) whose own org's Commerce
// balance the cloud gateway debits and 402s when empty. Running them through the
// Commerce/local gate (startBalance:0) would block the free tier entirely.
if (req?.user?.guest === true) {
return { canSpend: true, balance: 0, tokenCost };
}
const commerceClient = getCommerceClient();
const billingOrg = (req?.user?.organization ?? '').toString().trim();
// Per-user billing subject — prefer the one resolveHanzoCloudKey stamped from
// the authoritative IAM identity; otherwise derive it from organization + email
// (name == email for individual signups). This keys the gate on the SAME
// account the gateway debits (per-user for the shared "hanzo" catch-all), not
// the shared org balance.
const subject =
(req?.user?.billingSubject ?? '').toString().trim() ||
billingSubject(billingOrg, req?.user?.email);
// Tenant billing key = the org (the token `owner`). cloud is the authoritative
// meter+gate and bills per-org (see AUTH_BILLING_CONTRACT.md); this optional
// local pre-flight keys on the SAME org so it never diverges from cloud. This
// gate is OFF in production (balance.enabled=false) — cloud is the ONE gate.
const subject = (req?.user?.organization ?? '').toString().trim();
// Commerce-first authoritative gate (per-subject, fail closed).
if (commerceClient && subject) {
@@ -215,7 +200,7 @@ const checkBalanceRecord = async function ({
addIntervalToDate(lastRefillDate, record.refillIntervalValue, record.refillIntervalUnit)
) {
try {
/** @type {{ rate: number, user: string, balance: number, transaction: import('@librechat/data-schemas').ITransaction}} */
/** @type {{ rate: number, user: string, balance: number, transaction: import('@hanzochat/data-schemas').ITransaction}} */
const result = await createAutoRefillTransaction({
user: user,
tokenType: 'credits',
+2 -2
View File
@@ -9,7 +9,7 @@ const mockClient = {
isModelAllowed: jest.fn(),
};
jest.mock('@librechat/data-schemas', () => ({
jest.mock('@hanzochat/data-schemas', () => ({
logger: { info: jest.fn(), warn: jest.fn(), error: jest.fn(), debug: jest.fn() },
}));
jest.mock('~/server/services/CommerceClient', () => ({
@@ -19,7 +19,7 @@ jest.mock('~/cache', () => ({ logViolation: jest.fn().mockResolvedValue(undefine
jest.mock('./tx', () => ({ getMultiplier: () => 1 }));
jest.mock('./Transaction', () => ({ createAutoRefillTransaction: jest.fn() }));
jest.mock('~/db/models', () => ({ Balance: { findOne: jest.fn() } }));
jest.mock('librechat-data-provider', () => ({
jest.mock('@hanzochat/data-provider', () => ({
ViolationTypes: { TOKEN_BALANCE: 'token_balance' },
}));
+1 -1
View File
@@ -1,5 +1,5 @@
const mongoose = require('mongoose');
const { buildTree } = require('librechat-data-provider');
const { buildTree } = require('@hanzochat/data-provider');
const { MongoMemoryServer } = require('mongodb-memory-server');
const { getMessages, bulkSaveMessages } = require('./Message');
const { Message } = require('~/db/models');
+1 -1
View File
@@ -1,5 +1,5 @@
const mongoose = require('mongoose');
const { createMethods } = require('@librechat/data-schemas');
const { createMethods } = require('@hanzochat/data-schemas');
const methods = createMethods(mongoose);
const { comparePassword } = require('./userMethods');
const {
+1 -1
View File
@@ -1,4 +1,4 @@
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { updateInterfacePermissions: updateInterfacePerms } = require('@hanzochat/api');
const { getRoleByName, updateAccessPermissions } = require('./Role');
+1 -1
View File
@@ -1,5 +1,5 @@
const mongoose = require('mongoose');
const { logger, hashToken, getRandomValues } = require('@librechat/data-schemas');
const { logger, hashToken, getRandomValues } = require('@hanzochat/data-schemas');
const { createToken, findToken } = require('~/models');
/**
+6 -6
View File
@@ -1,4 +1,4 @@
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { getCustomEndpointConfig } = require('@hanzochat/api');
const {
Tools,
@@ -7,7 +7,7 @@ const {
isEphemeralAgentId,
appendAgentIdSuffix,
encodeEphemeralAgentId,
} = require('librechat-data-provider');
} = require('@hanzochat/data-provider');
const { getMCPServerTools } = require('~/server/services/Config');
const { mcp_all, mcp_delimiter } = Constants;
@@ -21,7 +21,7 @@ const ADDED_AGENT_ID = 'added_agent';
* Get an agent document based on the provided ID.
* @param {Object} searchParameter - The search parameters to find the agent.
* @param {string} searchParameter.id - The ID of the agent.
* @returns {Promise<import('librechat-data-provider').Agent|null>}
* @returns {Promise<import('@hanzochat/data-provider').Agent|null>}
*/
let getAgent;
@@ -39,9 +39,9 @@ const setGetAgent = (fn) => {
*
* @param {Object} params
* @param {import('express').Request} params.req
* @param {import('librechat-data-provider').TConversation} params.conversation - The added conversation
* @param {import('librechat-data-provider').Agent} [params.primaryAgent] - The primary agent (used to duplicate tools when both are ephemeral)
* @returns {Promise<import('librechat-data-provider').Agent|null>} The agent config as a plain object, or null if invalid.
* @param {import('@hanzochat/data-provider').TConversation} params.conversation - The added conversation
* @param {import('@hanzochat/data-provider').Agent} [params.primaryAgent] - The primary agent (used to duplicate tools when both are ephemeral)
* @returns {Promise<import('@hanzochat/data-provider').Agent|null>} The agent config as a plain object, or null if invalid.
*/
const loadAddedAgent = async ({ req, conversation, primaryAgent }) => {
if (!conversation) {
+1 -1
View File
@@ -1,4 +1,4 @@
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { createTransaction, createStructuredTransaction } = require('./Transaction');
/**
* Creates up to two transactions to record the spending of tokens.
+1 -1
View File
@@ -1,6 +1,6 @@
/** Note: No hard-coded values should be used in this file. */
const { maxTokensMap } = require('@hanzochat/api');
const { EModelEndpoint } = require('librechat-data-provider');
const { EModelEndpoint } = require('@hanzochat/data-provider');
const {
defaultRate,
tokenValues,
+3 -3
View File
@@ -44,9 +44,9 @@
"@google/genai": "^1.19.0",
"@keyv/redis": "^4.3.3",
"@langchain/core": "^0.3.80",
"@librechat/agents": "^3.1.51",
"@hanzochat/agents": "^3.2.63",
"@hanzochat/api": "workspace:*",
"@librechat/data-schemas": "workspace:*",
"@hanzochat/data-schemas": "workspace:*",
"@microsoft/microsoft-graph-client": "^3.0.7",
"@modelcontextprotocol/sdk": "1.22.0",
"@node-saml/passport-saml": "^5.1.0",
@@ -78,7 +78,7 @@
"keyv": "^5.3.2",
"keyv-file": "^5.1.2",
"klona": "^2.0.6",
"librechat-data-provider": "workspace:*",
"@hanzochat/data-provider": "workspace:*",
"lodash": "^4.17.23",
"mathjs": "^15.1.0",
"meilisearch": "^0.38.0",
+1 -1
View File
@@ -1,4 +1,4 @@
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
/** WeakMap to hold temporary data associated with requests */
const requestDataMap = new WeakMap();
+1 -1
View File
@@ -1,7 +1,7 @@
const cookies = require('cookie');
const jwt = require('jsonwebtoken');
const openIdClient = require('openid-client');
const { logger } = require('@librechat/data-schemas');
const { logger } = require('@hanzochat/data-schemas');
const { isEnabled, findOpenIDUser } = require('@hanzochat/api');
const {
requestPasswordReset,
@@ -1,4 +1,4 @@
jest.mock('@librechat/data-schemas', () => ({
jest.mock('@hanzochat/data-schemas', () => ({
logger: { error: jest.fn(), debug: jest.fn(), warn: jest.fn() },
}));
jest.mock('~/server/services/GraphTokenService', () => ({

Some files were not shown because too many files have changed in this diff Show More