2598 Commits
Author SHA1 Message Date
Seefs 603a5ceb55 feat(dashboard): show cache usage alongside total tokens (#7773)
* feat(dashboard): show cache usage alongside total tokens

* fix(dashboard): align cache stat descriptions and fix typecheck
2026-10-20 15:42:31 +08:00
邹云森森andCaIon 32e587406c feat(email): 添加 resend 邮件服务商接口 (#7880)
* feat(email): add Resend delivery provider

Add Resend as an alternative to SMTP with separately editable sender address
and name, credential-safe configuration status, and validation before activation.
Preserve the target repository's email notification and balance reminder settings.
Only Resend changes are transplanted from hicoderai/new-api; no landing,
legal-document, model-group, pricing, branding, or dependency changes are included.

Validation: Go common/controller email regression tests; 19 frontend email and
notification tests; frontend production build; tsgo typecheck; touched-file lint.
Frontend validation uses the target lockfile's Axios 1.18.1 with local cached tools.

Security references: OWASP Authentication, Session Management, and Forgot Password
Cheat Sheets. Tests cover credential non-disclosure, invalid activation, sanitized
transport errors, and no automatic retry or fallback; token and session flows
are unchanged.
https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html

* fix(email): log Resend rejections and translate Resend errors

Resend's reason for a rejected or failed send now goes to the server log.
People who asked for the email still see the short error.
Resend and email method errors are translatable messages in all seven
languages.
The email settings hints are shorter and reuse the existing key hint.
The new frontend files carry the license header, the sender name check
passes lint, and test fixtures use neutral names.

* fix(email): check Resend settings while Resend is active

Saving the sender address or API key now checks the whole Resend setup
while Resend sends the mail. A typo the form accepts, such as
[email protected], is rejected instead of breaking every send.
Resend's error text and network errors are quoted in the server log.

---------

Co-authored-by: CaIon <[email protected]>
2026-10-19 20:22:37 +08:00
Calcium-Ion 6cf0d90b5f feat(deepseek): support V4.1 Flash, the Search and Fetch APIs, and @ reasoning modifiers (#7867)
* fix(model): find channels for @-modified model names without the memory cache

With the channel memory cache off (no Redis and MEMORY_CACHE_ENABLED
unset), channel selection matched abilities by the exact requested name,
so a name such as deepseek-flash@effort:max found no channel. Retry with
the routing name when the exact name has no channel, as the cached path
already does.

* feat(deepseek): support V4.1 Flash, the Search and Fetch APIs, and @ reasoning modifiers

Follows the DeepSeek API update of 2026-10-17.

- Default models: deepseek-flash and deepseek-v4-pro, each with -none,
  -low and -max, plus deepseek-search and deepseek-fetch. The retired
  deepseek-chat, deepseek-reasoner and deepseek-v4-flash names are no
  longer listed.
- Thinking: the -none/-low/-max tails also work on deepseek-flash.
  @thinking and @effort now reach DeepSeek's own fields on Chat
  (thinking, reasoning_effort), Anthropic (thinking, output_config.effort)
  and Responses (reasoning.effort), and take precedence over the tails.
- Built-in prices: peak/off-peak expressions for deepseek-flash (also the
  deepseek-v4-flash aliases) and deepseek-v4-pro, and $0.75 per 1,000
  requests for deepseek-search and deepseek-fetch.
- Server-side search: searches and page fetches DeepSeek runs for Claude
  Code and Codex bill at $0.75 per 1,000 under deepseek_search and
  deepseek_fetch, whatever model name the client sent. Searches whose
  results already arrived stay billed when a stream ends before its final
  usage; this also applies to Alibaba's Anthropic endpoint.
- New endpoints POST /v1/tools/search and /v1/tools/fetch relay the
  DeepSeek APIs like /v1/alpha/search. A call is charged only when
  DeepSeek returns results or page content.

* fix(deepseek): price /v1/tools/search and /v1/tools/fetch by endpoint

A model named in the request body chose the billing model, so a body such
as {"query":"...","model":"deepseek-flash"} was priced as a chat model with
no tokens and the search or fetch cost nothing. Both endpoints now always
use their routing name, deepseek-search or deepseek-fetch. DeepSeek ignores
the body field, so the body is still forwarded unchanged.

* fix(deepseek): bill Claude-format page fetches at the DeepSeek Fetch price

usage.server_tool_use.web_fetch_requests always went to the web_fetch
tool price, which has no built-in value, while only searches used the
DeepSeek key. A channel can now name its own fetch key next to the search
key, and DeepSeek channels bill reported fetches under deepseek_fetch. A
channel retry clears it like the search key.
2026-10-17 23:24:33 +08:00
Calcium-Ion 6461f084ff feat(notify): turn each kind of notification on or off (#7868)
SMTP settings now only decide whether mail can be sent. Verification
codes, password reset, email binding and account security mails depend
on SMTP alone, and no notification switch affects them.

Admins get a site-wide low balance reminder switch, on by default, next
to the SMTP settings. The default threshold is entered as an amount.

Users choose which notifications they receive. Low balance reminders are
on for everyone by default. Admins can also turn channel notifications
and upstream model update notices on or off. The root user receives
channel notifications until it turns them off. Other admins receive them
after they turn them on.

A low balance reminder is sent once, when a charge takes the balance
below the threshold. A top-up above the threshold arms it again.

Channel notifications reach every admin who has them on, in that admin's
language. Email binding codes and account security mails follow the
account's language.

Saving notification settings no longer clears the language, the sidebar
layout or the billing preference.

Existing settings keep their behavior, and old clients that do not send
the new fields keep the stored switches. Webhook, Bark and Gotify
payloads are unchanged.
2026-10-17 22:17:59 +08:00
CaIon 5c31c1fa9e feat(channels): improve priority dragging and add onboarding guides
Keep table drop targets stable, use compact previews, and prevent
accidental mode switches and leftover row transforms.

Add first-use guides for card and table views with automatic playback,
pause and replay controls, and translations for all seven locales.
2026-10-17 21:52:39 +08:00
CaIon 5e9595190e feat(channels): add drag mode for channel priority ordering
Support priority ordering in table and card views with automatic priority calculation, same-tier and between-tier drop targets, live previews, conflict detection, and undo.

Keep drag mode off by default and place its toggle before Batch Operations. Consolidate sorting controls and preserve the shared visibility and view controls.
2026-10-17 20:19:41 +08:00
CaIon 5a59d69153 fix(pricing): hide task prices left at zero when another price is set
A task plugin can offer two billing modes, such as per task for some
models and per second for others. An administrator who prices a model
per second leaves its per-task price at zero. The model card, the list
view and the detail page then showed that unused zero price next to
the real one, and it read as a free item.

A usage price that is zero in every tier is now hidden when another
usage price is set. A model priced at zero throughout still shows its
zero prices. The card also drops the tier name when a model has a
single tier, since that name was always the editor's default "base".
2026-10-17 20:19:40 +08:00
RedwindA b0a98ac49c fix(web): apply theme class before first paint so Dark Reader detects dark mode (#7571)
Dark Reader checks html.dark / color-scheme only once during page load,
but the theme class was added by ThemeProvider after React mounted.
Resolve the saved theme in an inline head script and declare
color-scheme: dark on .dark.
2026-10-17 14:03:48 +08:00
xuu1998andxuu1998 f92c576506 fix(web): cover Cloudflare clef and NVIDIA model families in provider rules (#7840)
The usage-log model badge resolves its icon from MODEL_PROVIDER_RULES by model
name only. clef (Cloudflare) and the NVIDIA families without an nvidia prefix
(nv-embed, nv-rerank, riva-, parakeet-, canary-, nemoretriever) matched no rule
and fell back to the plain dot.

Co-authored-by: xuu1998 <[email protected]>
2026-10-17 13:07:41 +08:00
CaIon d032204733 feat(aws): add Mantle Messages API support
Add an opt-in Mantle interface with top-level cache_control, API key and
SigV4 authentication, and native Claude response handling. Keep InvokeModel
as the default for existing AWS channels.

Strip hop-by-hop headers before signing and map the dated Haiku alias to
Mantle's model ID. Cover signatures over HTTP/1.1 and HTTP/2, cache usage,
legacy routing, and channel configuration with regression tests.

Refs #4791
2026-10-16 14:45:17 +08:00
RedwindA 6549a17eda fix(dashboard): draw flow chart labels in the theme text color (#7821)
The Sankey labels were hard-coded to #475569, which is nearly invisible
on the dark theme. Read the app's --foreground token at runtime instead,
so labels and the active node outline match the surrounding text in
light/dark mode and every theme preset, and follow preset switches live.
2026-10-15 18:00:20 +08:00
Seefs c9179d38b7 fix(playground): support the reasoning field in chat responses (#7831) 2026-10-15 17:59:00 +08:00
CaIon 908f94d65d fix(web): hide pages the user cannot open from console search
The search box in the console header listed every page, so regular
users saw the admin pages and only found out on click that they had
no access. It now offers the same pages as the sidebar, which already
filters them by role and by the sidebar settings.

The layout now renders the search dialog instead of the search
provider, which removes the import cycle between the two.
2026-10-15 17:57:30 +08:00
CaIon f196934ce6 feat(plugins): bind Hailuo, Vidu and Veo plugins to New API channels
The hailuo, vidu, google and vertex-ai plugins now declare the new_api
upstream, so a New API (type 60) channel can bind them, and each serves its
vendor wire format on prefixed native routes that a downstream gateway
running the same plugin addresses:

- hailuo: /hailuo/v1/video_generation and /hailuo/v2/video_generation, with
  path-parameter queries. Through a gateway, v1 videos download from the
  upstream's task artifact, since only the upstream can resolve the MiniMax
  file link.
- vidu: /vidu/ent/v2/{text2video,img2video,start-end2video,reference2video}
  and the creations query; the route names the action, so one or two
  reference images stay a reference request.
- google: models/{model}:predictLongRunning and operation polling. The
  operation is renamed after the gateway task, and videos download from the
  upstream's task artifact, since the Gemini file needs the upstream key.
- vertex-ai: one dynamic route for predictLongRunning and
  fetchPredictOperation. Operation names echo the caller's project and
  location and never carry the upstream project.

Status presenters render a task that is not polled yet as pending and a
task the poller failed while its snapshot was still running as failed, so
the downstream gateway reads the upstream state and settles on the same
usage.

Versions: hailuo 1.3.0; vidu, google and vertex-ai 1.1.0.
2026-10-14 23:20:54 +08:00
feitianbubu c9f7b62081 fix(web): sort pricing models by input, output and per-request price (#7813) 2026-10-14 21:36:19 +08:00
枭钰钰 a0bf6a8a41 fix(vertex): pass project_id in API Key mode to avoid 404 on official endpoints (#6330)
* fix(vertex): pass project_id in API Key mode to avoid 404 on official endpoints

Closes #6250.

Vertex AI channel in vertex_key_type=api_key mode: getRequestUrl passed
an empty string as project_id to BuildGoogleModelURL /
BuildAnthropicModelURL, leaving the URL without the
/projects/{PROJECT_ID}/locations/{REGION}/ segment, so the official
Vertex endpoints returned 404.

In JSON mode project_id is parsed from the service account, so the bug
is specific to api_key mode.

Fix:
- Add VertexProjectID to ChannelOtherSettings (used only in api_key
  mode; JSON mode keeps parsing from the service account, unchanged).
- In adaptor.go's api_key branch, use ChannelOtherSettings.VertexProjectID
  instead of the hardcoded empty string. When unset, fall back to the
  legacy behavior so existing api_key channels are not broken.
- Frontend (web/default): show a Project ID input when
  VertexKeyType=api_key, persist settings.vertex_project_id, and require
  it via superRefine so users cannot save an api_key channel that would
  still 404. Clear the residual field in non-api_key mode or non-Vertex
  channels.
- Add translations across 7 locales (en/zh/zh-TW/ja/fr/ru/vi).

Regression tests (relay/channel/vertex/adaptor_api_key_url_test.go)
cover 4 scenarios:
- api_key mode + configured project_id -> URL contains /projects/.../locations/
- api_key mode + stream suffix -> key joined with &, path contains project_id
- api_key mode + Claude mode -> anthropic publisher path
- api_key mode + empty project_id -> falls back to legacy behavior

* fix(vertex): make the API Key project ID optional

Express-mode API keys are documented without a project, so a required
project ID would block saving those channels. Without one the URL keeps
the express-mode form as before; with one, the URL carries the project
and the deployment region, so a global region is honored.

- Drop the required check and describe the field in plain words.
- Treat vertex_project_id as a sensitive field, like the key format.
- Assert exact request URLs in a table test, including the global case.
- Cover saving, clearing and loading the project ID in the channel form.
2026-10-14 20:16:30 +08:00
CaIon 7de10f2c60 feat(token): let an API key read its owner's quota
Add a per-token "Allow querying user quota" switch. When it is on,
GET /api/usage/token/ also returns user_quota with the account's
remaining and used quota in quota units. New tokens start with the
switch on: the console form, POST /api/token/ without the field, and the
default token created at registration. Tokens created before this change
stay off. PUT /api/token/ keeps the stored value when the field is
absent, and the token audit lists the field when it changes.

The key usage and key log queries now check the key's IP whitelist the
same way API requests do, and answer 403 from an address outside it.
Expired and exhausted keys can still use both queries.

New API channels now query their balance from the upstream's
/api/usage/token/ with the channel key. When the upstream also returns
the owner's quota, the smaller of the key's and the owner's remaining
quota is the balance, and an unlimited key uses the owner's quota alone.
Without it, a limited key reports its own remaining quota, and an
unlimited key asks for the switch to be turned on upstream. Quota
converts to USD with the upstream's quota_per_unit from /api/status.

Refs #3122
Refs #6828
2026-10-13 21:39:52 +08:00
Seefs b4be2a7847 feat(ui): show newly added pricing groups at the top (#7782) 2026-10-13 12:35:05 +08:00
Calcium-Ion 633b4c5b19 feat(logs): export usage logs from the common log page (#7774)
* feat(logs): export usage logs from the common log page

The common log page has an Export button next to Search. It asks for a
time range and downloads the logs of the current view, with the page's
other filters, as a CSV file in the viewer's language. Users export their
own logs; administrators in the all-users view export every user's logs.

Each user runs one export at a time. A second export started while one is
running, from another tab or another node, is refused until the first one
ends. With Redis the reservation holds across nodes and lapses within a
minute if its node stops.

Log Maintenance settings add two options: whether users may export (on by
default) and the longest log time range in days (30 by default). Regular
users can search their logs within that range at once, and every export
is limited to it. A user's log search had no limit before.

* fix(web): split the log maintenance page into four modules

The log maintenance page ran its settings, the history log cleanup and
the server log files together. It now shows four modules, each under its
own heading and separated by a divider, like the performance page: log
recording, log search and export, history log cleanup, and server log
management. The history log cleanup no longer sits in a box of its own.

The history log cleanup and the server log files are now components of
their own; what they do is unchanged.

* feat(logs): default the log time range to 365 days and apply it to the dashboard

The longest log time range now defaults to 365 days instead of 30.
Regular users had no limit on log searches before this setting, and 30
days could not cover a 31-day month.

A user's own usage and traffic charts on the dashboard were held to a
fixed one-month range. They now follow the same setting, so the
dashboard, the log search and the export share one limit, and a range
that is too long gets the same "cannot exceed N days" message.
2026-10-12 21:33:53 +08:00
dependabot[bot] 9f048107ed chore(deps): bump ai from 7.0.31 to 7.0.136 in /web (#7767)
Bumps [ai](https://github.com/vercel/ai/tree/HEAD/packages/ai) from 7.0.31 to 7.0.136.
- [Release notes](https://github.com/vercel/ai/releases)
- [Changelog](https://github.com/vercel/ai/blob/main/packages/ai/CHANGELOG.md)
- [Commits](https://github.com/vercel/ai/commits/[email protected]/packages/ai)

---
updated-dependencies:
- dependency-name: ai
  dependency-version: 7.0.136
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-12 20:44:18 +08:00
dependabot[bot] bf4e1ebfb8 build(deps-dev): bump oxfmt from 0.57.0 to 0.72.0 in /web (#7766)
Bumps [oxfmt](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt) from 0.57.0 to 0.72.0.
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/npm/oxfmt/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/oxfmt_v0.72.0/npm/oxfmt)

---
updated-dependencies:
- dependency-name: oxfmt
  dependency-version: 0.72.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-12 20:38:31 +08:00
dependabot[bot] 82306b3c35 build(deps-dev): bump @tanstack/router-plugin in /web (#7768)
Bumps [@tanstack/router-plugin](https://github.com/TanStack/router/tree/HEAD/packages/router-plugin) from 1.168.23 to 1.168.42.
- [Release notes](https://github.com/TanStack/router/releases)
- [Changelog](https://github.com/TanStack/router/blob/main/packages/router-plugin/CHANGELOG.md)
- [Commits](https://github.com/TanStack/router/commits/@tanstack/[email protected]/packages/router-plugin)

---
updated-dependencies:
- dependency-name: "@tanstack/router-plugin"
  dependency-version: 1.168.42
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-12 20:38:05 +08:00
dependabot[bot] c730add954 build(deps): bump stream-markdown-parser from 1.1.3 to 1.2.18 in /web (#7769)
Bumps [stream-markdown-parser](https://github.com/Simon-He95/markstream-vue/tree/HEAD/packages/markdown-parser) from 1.1.3 to 1.2.18.
- [Release notes](https://github.com/Simon-He95/markstream-vue/releases)
- [Changelog](https://github.com/Simon-He95/markstream-vue/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Simon-He95/markstream-vue/commits/[email protected]/packages/markdown-parser)

---
updated-dependencies:
- dependency-name: stream-markdown-parser
  dependency-version: 1.2.18
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-12 20:37:50 +08:00
dependabot[bot] b8c2dc1266 build(deps): bump yace from 1.1.0 to 1.1.1 in /web (#7770)
Bumps [yace](https://github.com/petersolopov/yace) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/petersolopov/yace/releases)
- [Changelog](https://github.com/petersolopov/yace/blob/master/CHANGELOG.md)
- [Commits](https://github.com/petersolopov/yace/compare/v1.1.0...v1.1.1)

---
updated-dependencies:
- dependency-name: yace
  dependency-version: 1.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-12 20:37:26 +08:00
Seefs 2b85a8fc67 fix(auth): show legal consent warning on password login (#7771) 2026-10-12 18:25:54 +08:00
Seefs 2ed058795b fix(ci): stabilize checks and configure Bun dependency updates (#7765) 2026-10-12 18:19:04 +08:00
Richard Wang 4bfcf28121 chore(web): bump @lobehub/icons to 5.22.0 (#7764) 2026-10-12 17:55:13 +08:00
Seefs fc08b76f0f fix(web): preserve recorded billing tiers in usage log previews (#7763) 2026-10-12 17:47:28 +08:00
Seefs bbf60b49f6 fix(logs): vertically align billing source icons on mobile (#7760) 2026-10-12 16:54:24 +08:00
Richard Wang 3827fd8307 fix(web): show cache read price on expression model cards (#7757) 2026-10-12 16:35:23 +08:00
Richard Wang 42e694eb60 fix(web): map Meta Muse Spark models to Meta icon (#7734) 2026-10-11 19:26:38 +08:00
CaIon 93ea55f3c3 feat(web): pick dates and times down to the second
The date-time picker (expiry times, announcements, log cleanup and the
dashboard filters) and the date range filter on the log pages stopped
at the minute: editing a time reset its seconds to zero, and confirming
a range again dropped the seconds it had. Both now show and keep
seconds, and the range filter button shows them too.

Emptying the time field, for example with Backspace on one part of it,
sent out an invalid date. The field now goes back to its current time
instead.
2026-10-11 14:19:10 +08:00
CaIon 96275d224b fix(web): show date pickers and relative times in the interface language
The date pickers looked up their calendar language under "zh", but the
interface uses zhCN and zhTW, so both Chinese interfaces showed English
month and weekday names. They now use the Simplified and Traditional
Chinese calendars.

Relative times on the passkey card, the login session list and the
announcement list were always English, so a Chinese page showed
"2 hours ago". They now follow the interface language through the
shared formatter; English announcements read "5 minutes ago" instead of
"5m ago".

Refs #7654
2026-10-11 13:57:00 +08:00
CaIon 06dd76a9d3 fix(playground): keep each account's Playground data separate
Playground settings and messages were stored under fixed browser keys,
so after switching accounts in the same browser the next user saw the
previous user's chat history.

They are now stored per user ID. Data saved under the old shared keys
has no known owner and is deleted, so existing Playground history is
cleared once.

Fixes #7713
2026-10-11 13:57:00 +08:00
CaIon 1fb6d07fee perf(web): stop loading the whole Simple Icons pack on four pages
The wallet, security and OAuth callback pages and the user binding
dialog imported a few brand icons from react-icons/si, which pulled the
whole pack (about 3,400 icons, 5 MB, 2 MB gzipped) into their routes.
They now use the brand icons already in the project, plus a new Alipay
icon, and Linux DO bindings show the Linux DO logo instead of the Linux
penguin. The wallet page now loads 535 KB of scripts instead of 5.6 MB.

Payment method icons configured by name still load their pack on
demand.

Refs #7634
2026-10-11 13:56:59 +08:00
CaIon b892a6b2e4 fix(web): reload once when a page's code fails to load
When a route's script or style chunk failed to load, for example on a
flaky connection, the console showed the error page with a large "500"
although no request had returned 500, and it stayed there until the
user reloaded by hand.

The error page now reloads the page once after a chunk load failure; if
the chunk still fails, the error page stays. It no longer shows a status
code for errors that have none. Rate-limit errors still show 429.

Fixes #7634
2026-10-11 13:56:59 +08:00
CaIon 0f38a14a5e fix(web): restore browser sans-serif for Auto font 2026-10-10 20:29:04 +08:00
CaIon 1ca79d57e7 fix(pricing): show recent status samples without gaps
Fetch the latest 24 nonempty hourly samples within 7 days for the visible models, while preserving the 24-hour summary metrics. Render only available samples without gray placeholders.

Validated perf-metrics tests with SQLite 3.50.4, MySQL 5.7.44, and PostgreSQL 9.6.24; Go build; 36 frontend tests; frontend typecheck, lint, formatting, and production build.
2026-10-10 19:24:32 +08:00
CaIon c6741c36a9 feat(channel): add a per-channel switch to skip upstream TLS verification
Until now only the global TLS_INSECURE_SKIP_VERIFY setting could accept a
self-signed upstream certificate. A channel's tls_insecure_skip_verify
setting now does it for that channel's API requests: relay and the Responses
and Realtime WebSocket dialer, task submission and polling, plugin fetches,
balance, usage and model-list queries, and media proxied through the channel
proxy. Credential exchanges (Codex OAuth refresh, Vertex and plugin
service-account tokens) keep verifying certificates. The channel drawer shows
the switch under the HTTP protocol settings.
2026-10-10 14:02:06 +08:00
CaIon 3f02244783 feat(web): show the request domains a plugin declares when uploading it 2026-10-10 13:39:55 +08:00
Calcium-Ion 128089fa7d feat(i18n): translate web console messages in the frontend and AI-client errors in the backend (#7712)
* feat(i18n): translate web console messages in the frontend and AI-client errors in the backend

Chinese text was hard-coded in API messages, stored log content, emails and
server logs, so it reached users of every interface language.

Text is now translated where its reader is:

- Web console APIs return the English source text as message_key with
  message_params, and the web console translates it in all seven languages.
  message keeps the English text for other API callers.
- Errors returned to AI clients, emails and notifications are translated by
  the backend (en, zh-CN, zh-TW) in the language saved on the account, then
  the language of the request's Accept-Language header.
- A reader who states no language still gets the original Chinese text of the
  messages that were Chinese before, word for word. Gateways that use another
  new-api as their upstream match that text, for example to disable a channel
  whose account ran out of quota.
- Usage, top-up and system log content is stored as content_parts (key and
  params) and rendered in the viewer's language. Task fail reasons and refund
  reasons written by the gateway are English keys. Rows written earlier are
  shown as before.
- Server logs are English.

Also:

- The saved interface language zhTW, and zh-HK, zh-MO and zh-Hant, now select
  Traditional Chinese in the backend instead of Simplified Chinese.
- The built-in group descriptions are translated; descriptions an
  administrator saved are shown as written.
- .agents/rules/i18n.md records the conventions. Tests in i18n/ fail on a
  non-English string literal, on a web console message without translations
  and on a locale key without a constant.

* feat(i18n): print server logs in Chinese when LOG_LANGUAGE asks for it

Server log lines are English by default. With LOG_LANGUAGE=zh-CN, the lines
that were Chinese before print their original Chinese text.

common.LogText formats a log line from its English format, which is also the
key of its Chinese text in common/log_text.zh-CN.json. A line without an entry
prints in English. go vet checks the arguments of every call, and
TestServerLogTexts checks that each entry still matches a call and has the
same verbs as its key.

* docs: list LOG_LANGUAGE in the docker compose example

* feat(i18n): translate every server log line and add DEFAULT_LANGUAGE

Every line the gateway prints goes through common.LogText. With
DEFAULT_LANGUAGE=zh-CN, the lines that have a Chinese text in
common/log_text.zh-CN.json print it and the others print English.

DEFAULT_LANGUAGE replaces LOG_LANGUAGE. It also sets the language of
backend messages, emails and notifications for a reader who states no
language: en, zh-CN or zh-TW, English when unset. The list of messages
that stayed Chinese for such readers is removed.

Review fixes:
- Channel test logs store the token name "Model test"; the console shows
  it in the interface language.
- Task fail reasons written by the gateway are fixed sentences the console
  translates. The HTTP status and the upstream detail go to the server log
  when a polled task is failed.
- The auto-disable reason of a slow channel is written in DEFAULT_LANGUAGE.

Also:
- Drop the repository-scanning lint tests, the i18n rules file and its
  read gate. AGENTS.md keeps the conventions in four lines.
- Payment error toasts keep their previous text and only add the
  translated message key.
- Remove guards and helpers that no caller needs.

* fix(i18n): address review on language lookup, notices and error responses

- Relay requests no longer load the user a second time to find the
  language: the settings TokenAuth put into the request are used as they
  are, also when they hold no language.
- Upstream model update notices are rendered in each recipient's saved
  language, DEFAULT_LANGUAGE when none.
- /api/usage/token and /api/log/token answer with backend-translated
  messages again, with the keys and status codes used before this PR.
  Their callers are scripts, not the web console.
- Webhook notifications fill {{value}} placeholders like the other
  senders. Running the rendered text through Sprintf appended
  %!(EXTRA ...) and broke %2F in links.
- RootUserLanguage reads the setting of the root user it already loaded.
- zh_TW and zh_HK are read as Traditional Chinese.
- Add common.ApiErrorStatus for error responses under another HTTP status
  and use it where those responses were built by hand.
- Remove middleware.GetLanguage, which had no callers.
2026-10-09 21:39:51 +08:00
CaIon d10f11ffff feat(gemini): bill Google Search grounding per query or per grounded prompt
Gemini responses grounded with Google Search billed one google_search
call per response on every model. Google bills Gemini 3 per unique
search query ($14 per 1,000) and Gemini 2.5 and older per grounded
prompt ($35 per 1,000). A counter now collects the unique non-empty web
and image search queries across every stream frame and candidate of a
response. It records google_search as that query count and
google_search_grounded_prompt as one grounded prompt, and the tool price
table decides which of the two costs money for a model. The native
generateContent handler now counts grounding too. Google reports no
billed count in the response, so the unique query set is the best
available evidence, not a guaranteed match.

Built-in prices make queries free and grounded prompts $35 per 1,000 on
gemini-2.5*, gemini-2.0*, and gemini-1.5*; other models keep
google_search at $14 per query. Because built-in model-prefix prices
take precedence over a tool default, an operator's own google_search
price no longer applies to those older models; to keep search free
there, set google_search_grounded_prompt:gemini-2.5* (and the 2.0* and
1.5* keys) to 0.
2026-10-08 18:04:55 +08:00
Seefs 04c597bf6c feat: support regex conditions in advanced parameter overrides (#7698) 2026-10-08 16:07:03 +08:00
CaIon 45094bdf41 fix(web): 修复 OriginOS 6 设备渠道页白屏
用户环境:vivo X300 Ultra,OriginOS 6(Android 16),Chrome 和
Via 浏览器均出现渠道页白屏,控制台无错误。

保留页面模糊过渡,将入场终点由 blur(0px) 改为 blur(0.01px),
动画结束后设置 filter: none,避免零半径 blur 在受影响设备上
触发白屏。补充页面入场和路由切换的回归测试。

验证:受影响测试 2 项通过,typecheck、相关文件 lint 和格式检查
通过;测试实例已由用户在故障设备上复测,确认页面正常。
2026-10-07 23:40:17 +08:00
Calcium-Ion acb347a197 feat(relay): web search encoding per channel and vendor-reported search billing (#7690)
* feat(channels): convert Claude and Gemini requests to OpenAI Responses on Advanced Custom channels

Advanced Custom channels could convert Claude Messages and Gemini
generateContent requests only to OpenAI Chat. Two converters,
claude_messages_to_openai_responses and
gemini_generate_content_to_openai_responses, now send them to an OpenAI
Responses upstream and convert the response back to the client's
format. The channel form offers them with the /v1/responses upstream
path and bearer auth by default, and the backend validates their
incoming paths like the existing converters. The IDs keep the values the
relay already uses for these conversions.

* feat(moonshot): forward Responses requests to Kimi unchanged

Moonshot channels answered /v1/responses with "not implemented". They
now forward the request body unchanged to {base}/v1/responses. Kimi
serves only kimi-k3 on that endpoint today; other models are rejected
by the upstream, not by the gateway.

* fix(relay): bill completed image_generation calls when a Responses request ends failed or incomplete

An image_generation call that completed before a response.failed,
response.incomplete, or cancelled terminal had already reached the
client, but the stream accumulator reset the image count to zero on
those terminals, and the non-stream handler skipped image outputs when
the response status was failed, cancelled, or incomplete. Completed
images now bill on every terminal, as they already did when the client
disconnected; image items that never completed still bill nothing.

* feat(relay): let channels encode hosted web search and bill vendor-reported search counts

Converted requests always encoded hosted web search the OpenAI way:
web_search_options on Chat and the native hosted tool on Responses,
Claude, and Gemini. Zhipu and DashScope compatible-mode Chat take their
own search fields, and the search counts that Azure, xAI, and DashScope
report in usage were ignored; only web_search_call items were billed.

relaykit gains a WebSearchEncoder option that a channel adaptor sets in
Init; without one, the default encodings stay. Zhipu sends the GLM
web_search tool with search_std, and Gemini clients on Zhipu now convert
through Chat. DashScope compatible-mode Chat sends enable_search and
search_options with the default turbo strategy and maps allowed domains
to assigned_site_list (up to 25 sites). DashScope Anthropic-compatible
requests that carry a web search tool get the cc_entrypoint system
marker DashScope requires before it searches. Zhipu and DashScope Chat
responses report no search usage, so a forced search bills one call,
which is dropped again if the request retries on another channel; a
Zhipu non-stream response that returns search results also bills one.
Several hosted web search definitions on a Chat target now drop the
extras with a diagnostic instead of failing the request.

Vendor-reported counts replace the counted web_search_call items under
the vendor's price key: Azure tool_usage.web_search.num_requests as
bing_web_search, xAI server_side_tool_usage_details (web_search_calls as
web_search, priced for grok models, plus x_search posts and profiles),
and DashScope usage.x_tools on Responses and server_tool_use on its
Anthropic endpoint as search_strategy_agent. Claude server_tool_use also
counts web_fetch, code_execution, and tool_search, which bill only once
an operator prices them. Tool names with a built-in price and these
Claude server tools are reserved, so a client function of the same name
is never billed under them. The Claude and Gemini search counts move
from gin context keys into the per-request tool counts, and every tool
count is capped at 10,000 at settlement.

Built-in prices are seeded for these keys in each vendor's list currency
(Zhipu and DashScope in CNY, Azure and xAI in USD) and listed on the
tool price settings page. DashScope Responses requests move to
/compatible-mode/v1/responses, because the old /api/v2/apps/protocols
endpoint is no longer maintained.
2026-10-07 23:29:24 +08:00
CaIon c6bac617d4 fix(relaykit): make the thinking suffix adapter toggle cover effort suffixes
With the Claude or Gemini thinking suffix adapter switched off, the parser
still stripped effort tails such as -high from claude-* and gemini-* model
names and injected thinking parameters upstream; only the -thinking family
honoured the toggle. Gate every alias on the same flag so a disabled adapter
sends the model name verbatim, rename the parameter to allowSuffixAlias,
correct the ThinkingAdapterEnabled doc comments, and spell out the covered
suffixes and the off behaviour in both settings cards.
2026-10-07 21:26:34 +08:00
CaIon 956019109b feat(web): filter the user list by group
The users table already sent a group parameter to /api/user/search, but
the toolbar had no control for it. Add a single-select User Group filter
next to Status and Role, with options from /api/group/. The group search
parameter is now an array, like status and role.

The options are built on every render: DataTableFacetedFilter is
memoized and reads the selection from the column, so a memoized options
array would keep the chip from showing the selected group.
2026-10-07 21:26:34 +08:00
jimmyleocn 973cf8ef46 fix(keys): prevent rounded clipping of group names (#7645) 2026-10-06 09:01:03 +08:00
Seefs 56758edf95 fix(web): correct translations, chart ordering, and font loading (#7628) 2026-09-30 22:54:58 +08:00
Jay Chen 5fe8917be7 fix(i18n): keep cached zhTW/zhCN interface codes stable across page loads (#7139) 2026-09-30 22:42:15 +08:00