Compare commits

...
Author SHA1 Message Date
zeekay ecdc089858 fix(csp): allow hanzo.app so the Contribute widget loads
hanzo.chat embeds hanzo.app/edit.js on every page, but script-src listed
only analytics + cloudflareinsights, so the widget was CSP-blocked. Add
hanzo.app to script-src (load) and connect-src (its /v1 calls).
2026-07-25 10:53:05 -07:00
Hanzo Dev 8a604750b1 fix(csp): allow hanzo.app in script-src so the Contribute widget loads
hanzo.chat embeds hanzo.app/edit.js but script-src only listed analytics
and cloudflareinsights, so the widget was CSP-blocked on every page.
2026-07-25 10:52:24 -07:00
Hanzo Dev d53b82d5ce ci: neutralize deploy-dev.yml to sync-notice — native deploy is .hanzo/workflows/deploy.yml (GitHub is a mirror) 2026-07-24 15:15:38 -07:00
Hanzo Dev 64f31f2f55 ci: neutralize tag-images.yml to sync-notice — native deploy is .hanzo/workflows/deploy.yml (GitHub is a mirror) 2026-07-24 15:15:37 -07:00
Hanzo Dev 33c26607a6 ci: neutralize dev-staging-images.yml to sync-notice — native deploy is .hanzo/workflows/deploy.yml (GitHub is a mirror) 2026-07-24 15:15:36 -07:00
Hanzo Dev d1d7d4df25 ci: neutralize dev-images.yml to sync-notice — native deploy is .hanzo/workflows/deploy.yml (GitHub is a mirror) 2026-07-24 15:15:34 -07:00
Hanzo Dev 8ce12d8d8b ci: neutralize dev-branch-images.yml to sync-notice — native deploy is .hanzo/workflows/deploy.yml (GitHub is a mirror) 2026-07-24 15:15:34 -07:00
Hanzo Dev ec9c105a6e ci: neutralize main-image-workflow.yml to sync-notice — native deploy is .hanzo/workflows/deploy.yml (GitHub is a mirror) 2026-07-24 15:15:32 -07:00
Hanzo Dev 4d2580772f ci: neutralize docker-publish.yml to sync-notice — native deploy is .hanzo/workflows/deploy.yml (GitHub is a mirror) 2026-07-24 15:15:31 -07:00
Hanzo Dev 7594b95570 ci: native Hanzo deploy pipeline (act_runner + BuildKit -> ghcr.io/hanzoai/chat -> operator patch app/chat)
Push to main -> in-cluster act_runner -> BuildKit builds ./Dockerfile ->
ghcr.io/hanzoai/chat:<sha> -> kubectl patch app/chat -> operator reconcile -> hanzocd.
GitHub Actions image build/deploy workflows reduced to sync-only.
2026-07-24 15:15:00 -07:00
Hanzo AI e0fd669598 feat(guest): forward-port anonymous guest-composer chat (@hanzo/iam-compatible)
Re-adds the deliberately-deleted (98d0dad34b) guest stack so an anonymous
visitor lands on the chat composer and can chat ONE free model without login,
then hits the SSO wall after a small per-IP quota. Adapted to today's code
(@hanzochat/* package names, @hanzo/iam bridge, tenant-bearer billing) — NOT a
blind revert: hanzoCloudKey.ts stays deleted.

Server (fail-closed, server-enforced):
- services/guestConfig.js: env-gated config + ephemeral guest principal/user +
  guest-scoped endpoints/models builders (default model zen5-flash).
- controllers/auth/GuestController.js + POST /v1/chat/auth/guest: short-lived
  guest JWT ({guest:true}, per-token random id) signed with JWT_SECRET; the
  route is per-IP mint-limited (guestTokenLimiter).
- middleware/requireGuestOrJwtAuth.js: accepts guest tokens ONLY where mounted;
  the jwt strategy still rejects guests everywhere else (clean 401, no CastError).
- middleware/enforceGuestScope.js: pins endpoint+model, strips
  agents/tools/files/spec/preset; 403 on any other endpoint/model.
- limiters/{guestLimiters,guestMessageLimiter}.js + utils/guestClientIp.js:
  per-REAL-IP quota (CF-Connecting-IP, not the token) via in-memory limiterCache
  (correct at replicas:1/Recreate; Redis NOT required). Exhaust -> 402 GUEST_LIMIT.
- endpoints/custom/initialize.ts: guest principal -> shared capped GUEST_API_KEY
  (KMS chat-guest-key), skipping per-user hk-/bearer billing; fail-closed if
  unset. Authenticated bearer path unchanged.
- agents/index.js: guest-capable completion chain; guest-safe /chat/active poll;
  AND the guest SSE read-back — GET /chat/stream/:streamId registered with
  requireGuestOrJwtAuth ABOVE the strict guard (else a guest 401s reading its own
  stream -> empty reply). Per-job ownership check (job.metadata.userId===req.user.id)
  is unchanged: foreign=403, missing=404, no cross-principal stream leak.
- Wire models/endpoints + guest-safe bootstrap (user/convos/favorites) to
  requireGuestOrJwtAuth; emit allowGuestChat/guestMessageMax in /v1/config;
  balance-gate bypass for guests (no org, bounded by limiter + capped key).

Tests: restored 6 guest specs (40/40 green) + 3 guest-billing cases in
initialize.spec. Client already guest-ready (zero client changes).

Docs: LLM.md guest section corrected to in-memory-at-replicas:1 + GUEST_API_KEY.

Do NOT deploy: red security review next, then cto native build + CR image pin.
2026-07-24 13:13:41 -07:00
Hanzo AI cebbd5a42c hanzo-edit: mount the contribute widget (repo=hanzoai/chat) 2026-07-24 01:31:06 -07:00
zeekayandClaude Opus 4.8 8c6478cb82 feat(auth): @hanzo/iam session-bridge — one-way SPA login without breaking OBO
Complete the chat auth migration onto @hanzo/iam. The @hanzo/iam SPA runs
Authorization-Code + PKCE entirely in the browser; a new backend session-bridge
turns the IAM-verified identity into the EXISTING Chat session, so live login,
reload-persistence, the 401->refresh interceptor, and the cloud-agent
on-behalf-of (OBO) flow all keep working. This is a session-bridge, not a blind
swap — the change is purely additive server-side.

Backend
- POST /oauth/iam/session (api/server/controllers/auth/iamSession.js): accepts
  { accessToken, idToken } from the SPA, JWKS-validates the id_token against
  hanzo.id (issuer + RS256 signature + expiry), reconciles the Mongo User by
  `sub` (openidId, provider=openid — create/migrate via findOpenIDUser), issues
  the existing Chat session via setAuthTokens (refresh cookie + Mongo Session +
  token_provider=chat + Chat JWT), and persists the id_token server-side
  (persistOpenIDTokensToSession) so resolveTenantBearer can forward it on OBO
  cloud calls. IAM tokens never become the app bearer; the id_token stays
  server-side.
- api/server/services/iamToken.js: standalone JWKS verifier (the openIdJwtStrategy
  JWKS path promoted to a callable), self-contained via OIDC discovery of
  OPENID_ISSUER — no dependency on the Passport OpenID login strategy.

Frontend
- OAuthCallback: after IAM.handleCallback(), POST the token to /oauth/iam/session
  and set the returned Chat JWT (dispatchTokenUpdatedEvent). Fixes the prior
  broken callback that set the IAM token as the app bearer (rejected by
  requireJwtAuth) and created no server session.
- data-provider: add the iamSession() endpoint + export.

Deps
- @hanzo/iam ^0.13.1 -> ^0.13.8 (relocked).

Verification
- New unit tests green (iamSession 10, iamToken 5); AuthService + CloudAgentsClient
  green (52 in-scope tests pass). Typecheck introduces 0 new errors.
- verifyIamToken integration-tested against LIVE hanzo.id JWKS: forged (real kid,
  wrong key), unknown-kid, and empty tokens all rejected.

The server-initiated /oauth/openid routes are kept as a dormant, unreferenced
fallback (the SPA uses only the bridge); their deletion is deferred until the
full browser login flow is verified live end-to-end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 22:51:11 -07:00
zeekay a31f4517c1 merge: adopt @hanzogui/shell in hanzo.chat (Meet Hanzo launcher + brand tokens + landing header/footer) 2026-07-23 21:12:53 -07:00
zeekay 8b4edcf187 chore(chat): bump @hanzogui/shell ^7.4.2 (mobile account fix) 2026-07-23 21:07:31 -07:00
zeekay de5ae59f68 feat(landing): adopt shared HanzoHeader + PreFooterCTA + HanzoFooter on marketing page 2026-07-23 20:45:00 -07:00
zeekay a65406448c feat(nav): add Meet Hanzo cross-app launcher to chat header 2026-07-23 20:43:29 -07:00
zeekay 908cd5493e style(brand): import @hanzo/brand tokens + surface shared hanzo scale in tailwind 2026-07-23 20:42:52 -07:00
zeekay 364da76d9e chore(deps): add @hanzogui/shell@^7.4.1 + @hanzo/brand@^1.4.1 to frontend 2026-07-23 20:42:17 -07:00
Hanzo AI eb15c057c8 analytics: canonical hz.js tag (hanzo.chat)
Replace the script.js/data-website-id snippet in client/index.html <head> with the ONE canonical <script async src=https://analytics.hanzo.ai/hz.js data-site="hanzo.chat">. Drops the %VITE_ANALYTICS_SITE_ID% build placeholder (site key is now the static domain).
2026-07-23 01:35:17 -07:00
hanzo-dev 16e3b451db fix(build): sync pnpm-lock.yaml with client/package.json (@hanzo/logo)
The 'logo single-source' change added @hanzo/logo@^1.0.13 to client/package.json
but did not update pnpm-lock.yaml, so the Docker build's `pnpm install --frozen-lockfile`
has failed (ERR_PNPM_OUTDATED_LOCKFILE) on every build since — chat main was
unbuildable. Regenerated with the pinned pnpm@10.27.0 (--lockfile-only). @hanzo/logo
is a public npm package; peer-dep warnings are pre-existing (legacy-peer-deps).
2026-07-22 19:16:17 -07:00
hanzo-dev 79e884dd44 chore(brand): scrub LibreChat brand mentions → Hanzo
Removes the upstream LibreChat brand from user-visible + internal surfaces (pt-PT
locale string, MCP trust-warning template, code comments, the libreChatProxy var,
the LIBRE_CHAT_DOCKER_TAG build-script env, README prose). App UI was already
Hanzo-branded; this cleans the residue.

KEPT deliberately:
- LICENSE / packages/data-schemas/LICENSE 'Copyright (c) … LibreChat' — the MIT
  license REQUIRES the attribution; stripping it would violate the license.
- All LibreOffice references (packages/api/src/files/documents/libreoffice.ts) —
  that is the office-suite doc-preview dependency, NOT LibreChat; removing it
  breaks document preview.
2026-07-22 19:09:55 -07:00
Hanzo Dev 256595062b merge(feat/logo-single-source): consolidate onto main 2026-07-21 17:08:35 -07:00
Hanzo Dev f0312ac278 merge(feat/enso-default-model): consolidate onto main 2026-07-21 17:08:17 -07:00
hanzo-dev b4988a2a6c fix: router-routes-caller — /v1/get-routing-defaults -> /v1/router/defaults 2026-07-21 16:49:51 -07:00
Hanzo Dev ab14835c8b chat: default model → enso (Enso Pro, gated)
Prepend enso to the Hanzo endpoint (customOrder 0) models.default list, so a new
conversation opens on enso — the balanced orchestrated tier that routes each
request to the best-fit model. zen5-* stay selectable; titleModel/summaryModel
stay zen5-flash (fast, in-catalog title generation, unrelated to the composer).

GATED — config mirror only, do not deploy: the authoritative prod list is the
universe chat-config ConfigMap (repo chat.yaml mirrors it), so go-live is a
ConfigMap update gated on enso being served on api.hanzo.ai (ENSO_URL in ai) and
enso-ultra preservation validation.
2026-07-21 14:47:35 -07:00
Hanzo Dev fff0b4cf27 refactor(logo): HanzoLogoIcon consumes @hanzo/logo (single source)
Stop re-typing the block-H path data — import MARK_PATHS/MARK_VIEWBOX from
@hanzo/logo (the one geometry home). Byte-identical mark, same props
(className/size/currentColor), 4 consumers unaffected. Needs @hanzo/logo
published install to update the pnpm lock (deploy-gate); scoped tsc of the
changed file green against real react + logo types.
2026-07-21 09:35:43 -07:00
Hanzo Dev b81e6561fb auth: make @hanzo/iam the single login path
Re-implement the IAM-only rip on the re-rooted @hanzochat main (the old
auth/iam-only branch shares no history and is unmergeable). @hanzo/iam owns
every credential step via redirect-PKCE to hanzo.id; /auth/callback completes
the token exchange.

Client
- iam.ts: one always-configured IAM SDK singleton (env + prod defaults
  hanzo.id / hanzo-chat), redirect_uri ${origin}/auth/callback
- Login.tsx: single "Log in with Hanzo" -> signinRedirect(); auto-redirect
  on mount unless ?redirect=false
- drop LoginForm, Registration, ResetPassword, RequestPasswordReset,
  SocialButton, SocialLoginRender (+ specs); drop the register / forgot /
  reset routes
- GuestLimitDialog / LandingPage / login.ts: login = IAM signinRedirect

Server
- delete local + third-party passport strategies (local, apple, discord,
  facebook, github, google, ldap, saml) and their specs
- keep jwt / openid / openidJwt strategies for IAM JWT validation
- socialLogins: Hanzo IAM (OpenID Connect) only
- auth routes: drop /login, /register, /requestPasswordReset, /resetPassword
- remove dead requireLocalAuth / requireLdapAuth middleware + LoginController

Build
- give the @hanzochat/api rollup enough heap (cross-env
  NODE_OPTIONS=--max-old-space-size=8192) to fix the OOM exit 134
- raise the Dockerfile heap default to 8192 to match
2026-07-21 08:54:29 -07:00
Hanzo AI ac47ffd674 chore(chat): migrate @hanzo/capture -> @hanzo/event (superset API) 2026-07-20 23:27:22 -07:00
Hanzo Dev ce490f1462 Merge feat/community-git: showcase only apps built on git.hanzo.ai
The community showcase lists ONLY apps whose repository lives on
git.hanzo.ai. git.ts is the one place that decides "lives on Hanzo Git"
for both sides: isHanzoGitUrl (client link guard) and HANZO_GIT_URL_PATTERN
(server store query, matched case-insensitively). The marketplace query
pins source=git.hanzo.ai; the plain builder list is unaffected.
2026-07-20 17:29:56 -07:00
Hanzo Dev 05258972f0 chat: align showcase server filter with the client git.hanzo.ai predicate
The showcase decides "lives on git.hanzo.ai" in one place: git.ts. It now
also exports HANZO_GIT_URL_PATTERN (built from HANZO_GIT_HOST) for the
server store query; the agents list handler matches it case-insensitively.
Server and client (isHanzoGitUrl) now accept the same URLs -- http(s),
exact host, any case -- so a repo stored with non-canonical scheme or
casing is no longer silently dropped from the showcase.
2026-07-20 17:27:37 -07:00
Hanzo Dev a6b2b407f6 chat: community showcase lists only apps built on git.hanzo.ai
The agent/app marketplace is the community showcase. Apps are built in
hanzo.app, which clones each into git.hanzo.ai; scope the showcase to those
apps and surface only their git.hanzo.ai repositories.

- data-provider/git: one source of truth for the host (git.hanzo.ai), the
  canonical base URL, and isHanzoGitUrl() — shared by the marketplace query,
  the server-side list filter, and the client link guard.
- Agent record gains `repository` (its git.hanzo.ai clone); the list projection
  returns it.
- getMarketplaceAgents pins source=git.hanzo.ai; getListAgentsHandler restricts
  the result to agents whose `repository` is on git.hanzo.ai only when that
  source is set, so the builder's plain agent list is unaffected.
- Agent detail renders a repository link guarded by isHanzoGitUrl, so only
  git.hanzo.ai URLs are ever shown.

Producer follow-on (cross-repo): hanzo.app sets `repository` to the git.hanzo.ai
clone URL when it builds an app.
2026-07-20 17:04:36 -07:00
zeekayandClaude bfa2e347fb chore(models): prune unserved zen ids from picker mirror (matches chat-config CM)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-19 05:48:30 -07:00
zeekayandClaude ca5768bdeb feat(models): add Anthropic + OpenAI picker families (reference mirror of chat-config CM)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-18 23:59:27 -07:00
zeekayandClaude 61b1bfe506 feat(golive): unify build-app handoff to hanzo.app, refresh landing, surface SSE 5xx, fix dead help links
Directive 1 — every "build an app" affordance now DIRECTS to hanzo.app
(chat stays chat; hanzo.app is the builder sibling). One destination via
openAppBuilder/buildAppUrl:
- Composer BuildAppButton opens hanzo.app seeded from the composer text
  (was: toggled an inline placeholder pane).
- Landing starter row gains a "Build an app" link-out chip (plain-model
  default only; agent/assistant starters untouched).
- /build deep-link redirects to hanzo.app; the /build command + message
  action already handed off.
- Retired the inline build shell (BuildPreviewPane, buildMode atom,
  ChatView split) — it only ever placeheld before the same handoff.

Directive 2 + landing refresh — LandingPage (logged-out front door):
- Pricing block -> unified plans $20/$100/$200/mo, "shared AI usage across
  every Hanzo app (chat, app builder, API) + pay-as-you-go", each linking
  to hanzo.ai/pricing; kept the real $5 free-credit line.
- Zen showcase -> current house families (enso, enso-flash, zen5,
  zen5-coder, zen3-omni); dropped param/context chips (no repo source to
  cite). Demo terminal zen4-coder -> zen5-coder.
- Third-party wall -> models served today (Claude Opus 4.8, Claude 5
  Sonnet, GPT-5.2, DeepSeek V4, Qwen3.5, Llama 4).

Directive 3 — dead help/docs links repointed to the live docs
(docs.hanzo.ai/chat; hanzo.ai/docs/chat is 404): landing nav/hero/CTA/
footer, AdminSettingsDialog, ApiKeyDialog, Usage routing link, and the
helpAndFaqURL server default.

Directive 4 — useResumableSSE surfaces a completions 5xx that carries a
structured error body immediately instead of spinning through 5 reconnect
attempts (~30s) then a generic message. sse.js always sets responseCode on
an HTTP failure, so a 5xx-with-body previously missed the surface branch;
bodyless/non-JSON failures still reconnect (transient-drop resilience). +2 tests.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-18 23:59:26 -07:00
zeekayandClaude Opus 4.8 95b00c9e9a chat: hero-style landing empty state (heading + enlarged composer + prefill chips)
Match the hanzo.ai hero on the new-conversation empty state, reusing the
ONE existing composer + submit path (presentation only, no second input).

- Landing: default heading is now "What can I help with?" (localized
  com_ui_landing_title); admin customWelcome still wins. Short headings
  scale up to a hero size (text-3xl -> md:text-5xl), responsive.
- ChatForm: derive one isLanding memo; reuse it to center (existing logic,
  de-duplicated) AND enlarge the composer on the empty state only (taller
  initial height + text-base md:text-lg -> bigger placeholder/pill).
- ConversationStarters: reshape the existing chips into a centered
  monochrome pill row; clicking now PREFILLS the composer via the canonical
  submitPrompt (arms activePrompt, honors autoSendPrompts) instead of a
  second send path. Honest generic defaults: Summarize / Write code /
  Explain / Brainstorm. Agent/assistant author starters still flow through.

Monochrome throughout via existing brand tokens (text/surface/border-*);
SendButton is already the circular arrow-up. No new deps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 09:51:34 -07:00
c0e4df7788 feat(images): Fix action + previous-image picker for generated images (#73)
Add a 'Fix' affordance on AI-generated images (hover pill on the message
thumbnail and an action in the fullscreen dialog) that attaches the image
to the composer as a reference file and seeds a 'Fix this image: ' prompt,
so the user only describes the fix.

Three attach paths, all reusing the existing file pipeline:
- Fix: attaches the generated image by reference (file_id, no re-upload) via
  useAttachImage, injecting a completed ExtendedFile (attached: true) into the
  composer file map — the send path forwards it verbatim as multimodal content.
- Drag-and-drop: plain image/file drops with no tool-resource choice now attach
  straight into the current conversation instead of a single-option dead-end modal.
- Attach a previous image: new attach-menu item opens a picker of the current
  conversation's images (collectConversationImages) and attaches by reference.

Pure helper collectConversationImages/resolveImageUrl unit-tested (10 cases).
i18n keys added to the default locale. No backend changes.

Co-authored-by: Hanzo AI <ai@hanzo.ai>
2026-07-16 12:59:21 -07:00
b2aab4aff9 refactor: repoint reward-signal client onto the shared @hanzo/ai SDK (#72)
The content-free router-training feedback transport now lives once, in
`@hanzo/ai` `sendFeedback` (v0.2.1). `client/src/utils/rewardSignal.ts` becomes
a THIN adapter: it keeps the exact public `sendRewardSignal(requestId, signal,
rating?)` the message-actions and model-selector surfaces wire to, plus the
local `VITE_HANZO_FEEDBACK` opt-out, and delegates emission to the SDK with the
chat-specific `baseUrl` (VITE_HANZO_API_URL, cross-origin gateway) and the
end-user bearer lifted from axios defaults. The SDK owns the whitelisted
`{request_id, signal, rating?}` body, the dedupe, and the fire-and-forget
transport — so no prompt/response/code can transit, exactly as before.

The existing spec passes untouched (behavior preserved end-to-end through the
SDK; jsdom has no sendBeacon, so the SDK falls back to the same keepalive fetch
with the same headers).

Tests: client/src/utils/rewardSignal.spec.ts — 13/13 passed.

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

Co-authored-by: hanzo-dev <dev@hanzo.ai>
2026-07-16 11:15:34 -07:00
zandGitHub 3f4addac86 feat(analytics): instrument chat with @hanzo/capture (#70)
Client-side product analytics via @hanzo/capture: pageviews, identify(user.id), and CHAT_STARTED/CHAT_MESSAGE_SENT with endpoint+model identifiers only — no message content or PII. Lockfile + tsconfig subpath mapping included.
2026-07-16 10:14:12 -07:00
f2778c26cb feat(reward-signals): emit content-free router-training feedback to gateway (v0.9.23) (#71)
Emit CONTENT-FREE reward signals to `POST {VITE_HANZO_API_URL}/v1/feedback` so
the router training loop gets production feedback. Payload carries ONLY
{request_id, signal, rating?} — never any prompt/response/tag/text/filename/code.
Fire-and-forget: never blocks UX, silent no-op on any failure.

The routing ledger keys each RoutingEvent on the upstream gateway response id
(chatcmpl-…/msg_…). The chat client previously held only locally-minted UUIDs,
so capture the REAL id server-side and thread it to the client as one field:

- server: ModelEndHandler captures `data.output.response_metadata.id ?? .id`
  (the last CHAT_MODEL_END = the visible answer) into a shared `runMetadata`;
  AgentClient surfaces it via `this.metadata` → `feedbackRequestId` on the saved
  response message, riding the SSE final event + DB save to the client.
- schema: add optional `feedbackRequestId` to tMessageSchema (zod), IMessage
  (mongo type) and the mongoose message schema (persists; SQLite keeps it in its
  JSON doc blob automatically). Absent id ⇒ signal no-ops (never fabricated).

Client signals wired via new `client/src/utils/rewardSignal.ts`:
- thumbs up/down → up/down; regenerate → regenerate; copy → up (weak positive);
  model-switch right after a response → switch (last assistant msg's id).
Dedicated cross-origin credentialed fetch (keepalive) to the Hanzo API — NOT the
same-origin chat backend — forwarding the end-user bearer for per-org attribution.
Honors local opt-out `VITE_HANZO_FEEDBACK=0|false|off` (server-side org/user
training opt-in is the preferred enforcement). 13 unit tests, all green.

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

Co-authored-by: hanzo-dev <dev@hanzo.ai>
2026-07-16 09:19:20 -07:00
Hanzo AI 12bc51f901 fix: CloudUsage.js imports @hanzochat/data-schemas (not @librechat) — 0.9.22
The controller required '@librechat/data-schemas', but this fork renames all
internal packages to @hanzochat/* (214 other files use @hanzochat/data-schemas;
that upstream name resolves to nothing). Runtime-only failure (require-time), so
the green build hid it and 0.9.21 CrashLooped at CloudUsage.js:1 MODULE_NOT_FOUND
— AFTER clearing the 0.9.21 zod/v4 fix. One-line name fix; all other requires in
the usage backend already resolve.
2026-07-16 01:25:03 -07:00
Hanzo AI dff9f48c17 fix: force zod@3.24.4→3.25.76 so openai@6 resolves zod/v4 (0.9.21)
Any fresh chat image built after 74954c3f8 (@hanzo/iam 0.13 lockfile regen,
which pulled openai@6.46.0) crashes on boot:

  ERR_PACKAGE_PATH_NOT_EXPORTED: subpath './v4' is not defined by
  "exports" in openai/node_modules/zod/package.json

openai@6.46.0 declares zod '^3.25 || ^4.0' and imports zod/v4, but pnpm
mis-resolved its peer to the older zod@3.24.4 (which has no ./v4 subpath).
The deployed 0.9.19 image predates openai@6 entering the tree, so it never
hit this — 0.9.20 was simply the first rebuild to trip the latent landmine.

Fix: a single pnpm override zod@3.24.4 → 3.25.76 (already in-tree, a
backward-compatible superset that ships the zod/v4 compat subpath),
collapsing to ONE zod. Verified: no other package floated; openai@6.46.0
now pairs zod@3.25.76. Unblocks the whole chat build pipeline, not just
the 0.9.20 usage panel.
2026-07-16 01:04:18 -07:00
Hanzo AI 7a24877eaf usage: canonical cloud usage in Settings (proxy /v1/get-cloud-usages, @hanzo/usage)
Adds the shared Hanzo usage read to LibreChat's Usage tab, beside (not
replacing) the Mongo token-credit view. Backend CloudUsage.js proxies
cloud's GET /v1/get-cloud-usages on-behalf-of (hanzo.id bearer resolved
server-side, never to browser); client renders the CloudUsageOverview
with @hanzo/usage's headless normalizeCloudUsage over native Tailwind.
Honest when unavailable: 200 {enabled:false} hides the section.
2026-07-15 18:26:33 -07:00
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
2012 changed files with 11434 additions and 91383 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
+9 -44
View File
@@ -1,49 +1,14 @@
name: Update Test Server
# Neutralized — the native deploy pipeline is .hanzo/workflows/deploy.yml
# (Hanzo Git push -> in-cluster act_runner -> BuildKit builds ./Dockerfile ->
# ghcr.io/hanzoai/chat:<sha> -> kubectl patch app/chat -> operator reconcile).
# GitHub is a mirror; this workflow no longer builds, publishes, or deploys.
name: "Update Test Server (sync notice)"
on:
workflow_run:
workflows: ["Docker Dev Branch Images Build"]
types:
- completed
workflow_dispatch:
permissions:
contents: read
jobs:
deploy:
runs-on: hanzo-deploy-linux-amd64
if: |
github.repository == 'danny-avila/LibreChat' &&
(github.event_name == 'workflow_dispatch' ||
(github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'dev'))
notice:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install SSH Key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.DO_SSH_PRIVATE_KEY }}
known_hosts: ${{ secrets.DO_KNOWN_HOSTS }}
- name: Run update script on DigitalOcean Droplet
env:
DO_HOST: ${{ secrets.DO_HOST }}
DO_USER: ${{ secrets.DO_USER }}
run: |
ssh ${DO_USER}@${DO_HOST} << EOF
sudo -i -u danny bash << 'EEOF'
cd ~/LibreChat && \
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 npm run update:deployed && \
git checkout dev && \
git pull origin dev && \
git checkout do-deploy && \
git rebase dev && \
sudo npm run start:deployed && \
echo "Update completed. Application should be running now."
EEOF
EOF
- name: Sync notice
run: echo "native pipeline is .hanzo/workflows/deploy.yml; GitHub is a mirror"
+9 -90
View File
@@ -1,95 +1,14 @@
name: Docker Dev Branch Images Build
# Neutralized — the native deploy pipeline is .hanzo/workflows/deploy.yml
# (Hanzo Git push -> in-cluster act_runner -> BuildKit builds ./Dockerfile ->
# ghcr.io/hanzoai/chat:<sha> -> kubectl patch app/chat -> operator reconcile).
# GitHub is a mirror; this workflow no longer builds, publishes, or deploys.
name: "Docker Dev Branch Images Build (sync notice)"
on:
workflow_dispatch:
push:
branches:
- dev
paths:
- 'api/**'
- 'client/**'
- 'packages/**'
- 'package.json'
- 'package-lock.json'
- 'Dockerfile'
- 'Dockerfile.multi'
permissions:
contents: read
packages: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: hanzo-build-linux-amd64
timeout-minutes: 130
strategy:
matrix:
include:
- target: api-build
file: Dockerfile.multi
image_name: lc-dev-api
- target: node
file: Dockerfile
image_name: lc-dev
notice:
runs-on: ubuntu-latest
steps:
# Check out the repository
- name: Checkout
uses: actions/checkout@v4
# Set up QEMU
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
# Set up Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Prepare the environment
- name: Prepare environment
run: |
cp .env.example .env
- name: Compute build metadata
run: |
echo "BUILD_COMMIT=${{ github.sha }}" >> $GITHUB_ENV
echo "BUILD_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
# Build and push Docker images for each target
- name: Build and push Docker images
uses: docker/build-push-action@v5
with:
context: .
file: ${{ matrix.file }}
push: true
tags: |
ghcr.io/${{ github.repository_owner }}/${{ matrix.image_name }}:${{ github.sha }}
ghcr.io/${{ github.repository_owner }}/${{ matrix.image_name }}:latest
${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.image_name }}:${{ github.sha }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.image_name }}:latest
platforms: linux/amd64,linux/arm64
target: ${{ matrix.target }}
build-args: |
BUILD_COMMIT=${{ env.BUILD_COMMIT }}
BUILD_BRANCH=${{ env.BUILD_BRANCH }}
BUILD_DATE=${{ env.BUILD_DATE }}
- name: Sync notice
run: echo "native pipeline is .hanzo/workflows/deploy.yml; GitHub is a mirror"
+9 -86
View File
@@ -1,91 +1,14 @@
name: Docker Dev Images Build
# Neutralized — the native deploy pipeline is .hanzo/workflows/deploy.yml
# (Hanzo Git push -> in-cluster act_runner -> BuildKit builds ./Dockerfile ->
# ghcr.io/hanzoai/chat:<sha> -> kubectl patch app/chat -> operator reconcile).
# GitHub is a mirror; this workflow no longer builds, publishes, or deploys.
name: "Docker Dev Images Build (sync notice)"
on:
workflow_dispatch:
push:
branches:
- main
paths:
- 'api/**'
- 'client/**'
- 'packages/**'
- 'package.json'
- 'package-lock.json'
- 'Dockerfile'
- 'Dockerfile.multi'
permissions:
contents: read
packages: write
jobs:
build:
runs-on: hanzo-build-linux-amd64
timeout-minutes: 130
strategy:
matrix:
include:
- target: api-build
file: Dockerfile.multi
image_name: librechat-dev-api
- target: node
file: Dockerfile
image_name: librechat-dev
notice:
runs-on: ubuntu-latest
steps:
# Check out the repository
- name: Checkout
uses: actions/checkout@v4
# Set up QEMU
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
# Set up Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Prepare the environment
- name: Prepare environment
run: |
cp .env.example .env
- name: Compute build metadata
run: |
echo "BUILD_COMMIT=${{ github.sha }}" >> $GITHUB_ENV
echo "BUILD_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
# Build and push Docker images for each target
- name: Build and push Docker images
uses: docker/build-push-action@v5
with:
context: .
file: ${{ matrix.file }}
push: true
tags: |
ghcr.io/${{ github.repository_owner }}/${{ matrix.image_name }}:${{ github.sha }}
ghcr.io/${{ github.repository_owner }}/${{ matrix.image_name }}:latest
${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.image_name }}:${{ github.sha }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.image_name }}:latest
platforms: linux/amd64,linux/arm64
target: ${{ matrix.target }}
build-args: |
BUILD_COMMIT=${{ env.BUILD_COMMIT }}
BUILD_BRANCH=${{ env.BUILD_BRANCH }}
BUILD_DATE=${{ env.BUILD_DATE }}
- name: Sync notice
run: echo "native pipeline is .hanzo/workflows/deploy.yml; GitHub is a mirror"
+9 -74
View File
@@ -1,79 +1,14 @@
name: Docker Dev Staging Images Build
# Neutralized — the native deploy pipeline is .hanzo/workflows/deploy.yml
# (Hanzo Git push -> in-cluster act_runner -> BuildKit builds ./Dockerfile ->
# ghcr.io/hanzoai/chat:<sha> -> kubectl patch app/chat -> operator reconcile).
# GitHub is a mirror; this workflow no longer builds, publishes, or deploys.
name: "Docker Dev Staging Images Build (sync notice)"
on:
workflow_dispatch:
permissions:
contents: read
packages: write
jobs:
build:
runs-on: hanzo-build-linux-amd64
strategy:
matrix:
include:
- target: api-build
file: Dockerfile.multi
image_name: lc-dev-staging-api
- target: node
file: Dockerfile
image_name: lc-dev-staging
notice:
runs-on: ubuntu-latest
steps:
# Check out the repository
- name: Checkout
uses: actions/checkout@v4
# Set up QEMU
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
# Set up Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Prepare the environment
- name: Prepare environment
run: |
cp .env.example .env
- name: Compute build metadata
run: |
echo "BUILD_COMMIT=${{ github.sha }}" >> $GITHUB_ENV
echo "BUILD_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
# Build and push Docker images for each target
- name: Build and push Docker images
uses: docker/build-push-action@v5
with:
context: .
file: ${{ matrix.file }}
push: true
tags: |
ghcr.io/${{ github.repository_owner }}/${{ matrix.image_name }}:${{ github.sha }}
ghcr.io/${{ github.repository_owner }}/${{ matrix.image_name }}:latest
${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.image_name }}:${{ github.sha }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.image_name }}:latest
platforms: linux/amd64,linux/arm64
target: ${{ matrix.target }}
build-args: |
BUILD_COMMIT=${{ env.BUILD_COMMIT }}
BUILD_BRANCH=${{ env.BUILD_BRANCH }}
BUILD_DATE=${{ env.BUILD_DATE }}
- name: Sync notice
run: echo "native pipeline is .hanzo/workflows/deploy.yml; GitHub is a mirror"
+9 -66
View File
@@ -1,71 +1,14 @@
# Hanzo Chat — image build (amd64, self-hosted hanzoai pool).
#
# chat is a PUBLIC repo, so it cannot reuse the private hanzoai/.github
# reusable workflow (GitHub blocks public->private workflow reuse).
# This is therefore self-contained.
#
# Builds ghcr.io/hanzoai/chat on:
# - push to main -> immutable sha-<sha7> tag
# - v* tags -> semver image tag (e.g. v0.7.10 -> 0.7.10)
# No :latest, no floating tags (semver-only policy). amd64 only
# (DOKS is all amd64 — no arm64 runners).
name: Docker Release
# Neutralized — the native deploy pipeline is .hanzo/workflows/deploy.yml
# (Hanzo Git push -> in-cluster act_runner -> BuildKit builds ./Dockerfile ->
# ghcr.io/hanzoai/chat:<sha> -> kubectl patch app/chat -> operator reconcile).
# GitHub is a mirror; this workflow no longer builds, publishes, or deploys.
name: "Docker Release (sync notice)"
on:
push:
branches: [main]
tags: ["v*"]
workflow_dispatch:
concurrency:
group: docker-release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
packages: write
jobs:
build-amd64:
# hanzoai org pool — ARC routes by runnerScaleSetName, not labels.
runs-on: hanzo-build-linux-amd64
notice:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: docker-container
driver-opts: network=host
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/hanzoai/chat
tags: |
type=semver,pattern={{version}}
type=sha,prefix=sha-,format=short
flavor: |
latest=false
- name: Build and push amd64 image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=ghcr.io/hanzoai/chat-cache:amd64
cache-to: type=registry,ref=ghcr.io/hanzoai/chat-cache:amd64,mode=max
provenance: false
- name: Sync notice
run: echo "native pipeline is .hanzo/workflows/deploy.yml; GitHub is a mirror"
+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 }}
+9 -87
View File
@@ -1,92 +1,14 @@
name: Docker Compose Build Latest Main Image Tag (Manual Dispatch)
# Neutralized — the native deploy pipeline is .hanzo/workflows/deploy.yml
# (Hanzo Git push -> in-cluster act_runner -> BuildKit builds ./Dockerfile ->
# ghcr.io/hanzoai/chat:<sha> -> kubectl patch app/chat -> operator reconcile).
# GitHub is a mirror; this workflow no longer builds, publishes, or deploys.
name: "Docker Compose Build Latest Main Image Tag (Manual Dispatch) (sync notice)"
on:
workflow_dispatch:
permissions:
contents: read
packages: write
jobs:
build:
runs-on: hanzo-build-linux-amd64
strategy:
matrix:
include:
- target: api-build
file: Dockerfile.multi
image_name: librechat-api
- target: node
file: Dockerfile
image_name: librechat
notice:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
- name: Fetch tags and set the latest tag
run: |
set -euo pipefail
git fetch --tags --force
LATEST_TAG=$(git tag --list 'v[0-9]*' --sort=-v:refname | grep -E '^v[0-9]+[.][0-9]+[.][0-9]+$' | head -n 1)
if [ -z "$LATEST_TAG" ]; then
echo "::error::No stable v<semver> tag found"
exit 1
fi
printf 'LATEST_TAG=%s\n' "$LATEST_TAG" >> "$GITHUB_ENV"
- name: Compute build metadata
run: |
printf 'BUILD_COMMIT=%s\n' "$(git rev-parse HEAD)" >> "$GITHUB_ENV"
printf 'BUILD_BRANCH=main\n' >> "$GITHUB_ENV"
printf 'BUILD_DATE=%s\n' "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> "$GITHUB_ENV"
# Set up QEMU
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
# Set up Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Prepare the environment
- name: Prepare environment
run: |
cp .env.example .env
# Build and push Docker images for each target
- name: Build and push Docker images
uses: docker/build-push-action@v5
with:
context: .
file: ${{ matrix.file }}
push: true
tags: |
ghcr.io/${{ github.repository_owner }}/${{ matrix.image_name }}:${{ env.LATEST_TAG }}
ghcr.io/${{ github.repository_owner }}/${{ matrix.image_name }}:latest
${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.image_name }}:${{ env.LATEST_TAG }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.image_name }}:latest
platforms: linux/amd64,linux/arm64
target: ${{ matrix.target }}
build-args: |
BUILD_COMMIT=${{ env.BUILD_COMMIT }}
BUILD_BRANCH=${{ env.BUILD_BRANCH }}
BUILD_DATE=${{ env.BUILD_DATE }}
- name: Sync notice
run: echo "native pipeline is .hanzo/workflows/deploy.yml; GitHub is a mirror"
+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
+10 -112
View File
@@ -1,116 +1,14 @@
name: Docker Images Build on Tag
# Neutralized — the native deploy pipeline is .hanzo/workflows/deploy.yml
# (Hanzo Git push -> in-cluster act_runner -> BuildKit builds ./Dockerfile ->
# ghcr.io/hanzoai/chat:<sha> -> kubectl patch app/chat -> operator reconcile).
# GitHub is a mirror; this workflow no longer builds, publishes, or deploys.
name: "Docker Images Build on Tag (sync notice)"
on:
push:
tags:
- 'v*'
permissions:
contents: read
packages: write
workflow_dispatch:
jobs:
build:
runs-on: hanzo-build-linux-amd64
strategy:
matrix:
include:
- target: api-build
file: Dockerfile.multi
image_name: librechat-api
- target: node
file: Dockerfile
image_name: librechat
notice:
runs-on: ubuntu-latest
steps:
# Check out the repository
- name: Checkout
uses: actions/checkout@v4
- name: Validate release tag
id: release-tag
env:
REF_NAME: ${{ github.ref_name }}
run: |
set -euo pipefail
TAG_REGEX='^v[0-9]+[.][0-9]+[.][0-9]+(-rc[0-9]+)?$'
STABLE_TAG_REGEX='^v[0-9]+[.][0-9]+[.][0-9]+$'
if [[ ! "$REF_NAME" =~ $TAG_REGEX ]]; then
echo "::error::Docker release tags must use v<semver> or v<semver>-rcN, for example v0.8.5 or v0.8.5-rc1"
exit 1
fi
printf 'image_tag=%s\n' "$REF_NAME" >> "$GITHUB_OUTPUT"
if [[ "$REF_NAME" =~ $STABLE_TAG_REGEX ]]; then
echo "is_stable=true" >> "$GITHUB_OUTPUT"
else
echo "is_stable=false" >> "$GITHUB_OUTPUT"
fi
# Set up QEMU
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
# Set up Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Prepare the environment
- name: Prepare environment
run: |
cp .env.example .env
- name: Compute build metadata
run: |
echo "BUILD_COMMIT=${{ github.sha }}" >> $GITHUB_ENV
echo "BUILD_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
- name: Resolve image tags
id: image-tags
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
IMAGE_NAME: ${{ matrix.image_name }}
IMAGE_TAG: ${{ steps.release-tag.outputs.image_tag }}
IS_STABLE: ${{ steps.release-tag.outputs.is_stable }}
run: |
set -euo pipefail
{
echo 'tags<<EOF'
printf 'ghcr.io/%s/%s:%s\n' "$GITHUB_REPOSITORY_OWNER" "$IMAGE_NAME" "$IMAGE_TAG"
printf '%s/%s:%s\n' "$DOCKERHUB_USERNAME" "$IMAGE_NAME" "$IMAGE_TAG"
if [ "$IS_STABLE" = "true" ]; then
printf 'ghcr.io/%s/%s:latest\n' "$GITHUB_REPOSITORY_OWNER" "$IMAGE_NAME"
printf '%s/%s:latest\n' "$DOCKERHUB_USERNAME" "$IMAGE_NAME"
fi
echo 'EOF'
} >> "$GITHUB_OUTPUT"
# Build and push Docker images for each target
- name: Build and push Docker images
uses: docker/build-push-action@v5
with:
context: .
file: ${{ matrix.file }}
push: true
tags: ${{ steps.image-tags.outputs.tags }}
platforms: linux/amd64,linux/arm64
target: ${{ matrix.target }}
build-args: |
BUILD_COMMIT=${{ env.BUILD_COMMIT }}
BUILD_BRANCH=${{ env.BUILD_BRANCH }}
BUILD_DATE=${{ env.BUILD_DATE }}
- name: Sync notice
run: echo "native pipeline is .hanzo/workflows/deploy.yml; GitHub is a mirror"
+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/
+24
View File
@@ -0,0 +1,24 @@
name: deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: hanzo-linux-amd64
steps:
- uses: actions/checkout@v4
- name: Build + push image
run: |
SHA="${GITHUB_SHA::8}"
buildctl-daemonless.sh build --frontend=dockerfile.v0 \
--opt context="${{ github.server_url }}/${{ github.repository }}.git#${GITHUB_SHA}" \
--opt filename=Dockerfile --opt platform=linux/amd64 \
--secret id=GIT_AUTH_TOKEN,env=GIT_AUTH_TOKEN \
--output "type=image,name=ghcr.io/hanzoai/chat:${SHA},push=true" --progress=plain
env:
GIT_AUTH_TOKEN: ${{ secrets.GIT_CLONE_TOKEN }}
- name: Deploy — declare tag to operator
run: |
for app in chat; do
kubectl -n hanzo patch app "$app" --type=merge -p "{\"spec\":{\"image\":{\"repository\":\"ghcr.io/hanzoai/chat\",\"tag\":\"${GITHUB_SHA::8}\"}}}"
done
+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
---
+1 -1
View File
@@ -20,7 +20,7 @@ COPY --from=ghcr.io/astral-sh/uv:0.9.5-python3.12-alpine /usr/local/bin/uv /usr/
RUN uv --version
# Set configurable max-old-space-size with default
ARG NODE_MAX_OLD_SPACE_SIZE=6144
ARG NODE_MAX_OLD_SPACE_SIZE=8192
RUN mkdir -p /app && chown node:node /app
WORKDIR /app
+1 -1
View File
@@ -2,7 +2,7 @@
# v0.8.3-rc1
# Set configurable max-old-space-size with default
ARG NODE_MAX_OLD_SPACE_SIZE=6144
ARG NODE_MAX_OLD_SPACE_SIZE=8192
# Base for all builds
FROM ghcr.io/hanzoai/nodejs:v24.18.0 AS base-min
+45 -36
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
@@ -95,7 +95,7 @@ CREDS_KEY= CREDS_IV= # Credential encryption
Off by default (`ALLOW_GUEST_CHAT=false`). When enabled, the landing IS the chat
composer (ChatGPT-style): an unauthenticated visitor renders the real chat view —
composer, starter cards, model picker — WITHOUT logging in, scoped to the free
Zen model (`GUEST_MODEL`, default `zen3-nano`) via the `Hanzo` custom endpoint
Zen model (`GUEST_MODEL`, default `zen5-flash`) via the `Hanzo` custom endpoint
(`api.hanzo.ai`). Prod uses `GUEST_MESSAGE_MAX=2`. Exhausting the quota returns
`402 {type:'GUEST_LIMIT'}` and the client opens the existing OpenID/hanzo.id login.
@@ -117,32 +117,41 @@ Security model (fail-closed, server-enforced):
per-token random id) signed with `JWT_SECRET`. Rate-limited per IP
(`guestTokenLimiter`, `GUEST_TOKEN_MAX`/`GUEST_TOKEN_WINDOW`) so tokens can't be
spam-minted.
- `requireGuestOrJwtAuth` (chat-completion route ONLY) accepts guest tokens;
the standard `jwt` strategy rejects them everywhere else (no DB user), so
every other route stays closed. `enforceGuestScope` pins endpoint+model and
strips agents/tools/files/spec/preset. Guests always use the shared, capped
`HANZO_API_KEY` (per-user `hk-` billing is skipped for `guest` principals).
- `requireGuestOrJwtAuth` (chat-completion + guest-safe bootstrap routes: models,
endpoints, user, convos, favorites, agents `/chat/active`) accepts guest tokens;
the standard `jwt` strategy rejects them everywhere else (no DB user), so every
other route stays closed. `enforceGuestScope` pins endpoint+model and strips
agents/tools/files/spec/preset. Guests use the shared, capped guest gateway key
`GUEST_API_KEY` (the KMS `chat-guest-key`; `HANZO_API_KEY` is the dev fallback),
resolved in `packages/api/src/endpoints/custom/initialize.ts` — the guest key's
OWN org is metered+capped at the gateway, and per-user `hk-` billing is skipped
for `guest` principals (they carry no forwardable bearer and no `X-Org-Id`).
- `guestMessageLimiter` enforces the quota against the REAL client IP
(`utils/guestClientIp` → Cloudflare `CF-Connecting-IP`, falls back to `req.ip`),
NOT the token — clearing cookies / incognito / minting a fresh token does NOT
reset it. Backed by the shared Redis `limiterCache` so it holds across replicas.
`USE_REDIS=true` is MANDATORY (a memory store would let a visitor round-robin
pods to multiply their quota).
reset it. The store is `limiterCache`, which returns `undefined` when `USE_REDIS`
is off → `express-rate-limit` uses its in-process MemoryStore. That store is
authoritative at the live deploy's `replicas: 1` + `Recreate` (never two live
pods to round-robin); Redis is NOT required (it was killed platform-wide). The
only reset is a pod restart — operational, not attacker-triggerable. If guest
chat ever scales past one replica, set `USE_REDIS=true` so the count holds
across pods.
- Key files: `api/server/services/guestConfig.js`,
`api/server/controllers/auth/GuestController.js`,
`api/server/middleware/{requireGuestOrJwtAuth,enforceGuestScope}.js`,
`api/server/middleware/limiters/{guestLimiters,guestMessageLimiter}.js`,
`api/server/utils/guestClientIp.js`,
router wiring in `api/server/routes/agents/index.js`.
- Env: `ALLOW_GUEST_CHAT`, `GUEST_MESSAGE_MAX`, `GUEST_ENDPOINT`, `GUEST_MODEL`,
`GUEST_TOKEN_EXPIRY`, `GUEST_TOKEN_MAX`, `GUEST_TOKEN_WINDOW`. Requires
`HANZO_API_KEY` (the free publishable gateway key) and `USE_REDIS` for the
shared per-IP quota across replicas.
- Env: `ALLOW_GUEST_CHAT`, `GUEST_MODEL` (prod `zen5-flash`), `GUEST_ENDPOINT`
(`Hanzo`), `GUEST_MESSAGE_MAX` (prod `2`), `GUEST_TOKEN_EXPIRY`, `GUEST_TOKEN_MAX`,
`GUEST_TOKEN_WINDOW`. Requires the shared, capped guest key `GUEST_API_KEY`
(KMS `chat-guest-key`; falls back to `HANZO_API_KEY`). No Redis dependency at
`replicas: 1` — the in-process MemoryStore is the quota's single source of truth.
## 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 +206,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 +225,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 +233,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 +246,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 +255,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 +269,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 +293,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 +301,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 +326,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. MIT licensed. See [LICENSE](./LICENSE) for the full attribution.
+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 许可证。MIT 许可证。见 [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) => {
+3 -3
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,
@@ -22,7 +22,7 @@ const {
Permissions,
EToolResources,
PermissionTypes,
} = require('librechat-data-provider');
} = require('@hanzochat/data-provider');
const {
availableTools,
manifestToolMap,
@@ -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));
+2 -2
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');
+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');
+2 -2
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.
*/
+13 -3
View File
@@ -1,5 +1,5 @@
const { logger } = require('@librechat/data-schemas');
const { ViolationTypes } = require('librechat-data-provider');
const { logger } = require('@hanzochat/data-schemas');
const { ViolationTypes } = require('@hanzochat/data-provider');
const { createAutoRefillTransaction } = require('./Transaction');
const { logViolation } = require('~/cache');
const { getMultiplier } = require('./tx');
@@ -94,6 +94,16 @@ const checkBalanceRecord = async function ({
const multiplier = getMultiplier({ valueKey, tokenType, model, endpoint, endpointTokenConfig });
const tokenCost = amount * multiplier;
// Guests (anonymous preview) are NOT balance-gated here — they have no org and no
// DB balance record, so both the Commerce and legacy local gates would false-block
// them. Guest spend is bounded two other ways: (1) the per-IP guest message
// limiter (GUEST_MESSAGE_MAX) and (2) the shared, small-capped guest key
// (GUEST_API_KEY) whose OWN org's balance the cloud gateway debits and 402s when
// empty. Never an authed user's org balance.
if (req?.user?.guest === true) {
return { canSpend: true, balance: 0, tokenCost };
}
const commerceClient = getCommerceClient();
// 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
@@ -200,7 +210,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) {

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