Round-trip Grok encrypted reasoning via Anthropic thinking.signature and
seed cache identity from Claude Code session IDs, so multi-turn /v1/messages
traffic can hit xAI prompt cache more like CLIProxyAPI. Also strip invalid
encrypted_content and retry on the Messages path when decrypt fails after
account switches.
deploy/.env.example documents POSTGRES_MAX_CONNECTIONS,
POSTGRES_SHARED_BUFFERS, POSTGRES_EFFECTIVE_CACHE_SIZE and
POSTGRES_MAINTENANCE_WORK_MEM, with notes on how to size them — but no
compose file ever passes them to the postgres container. A user who sets
them in .env gets nothing, silently.
Wire them into the postgres command in deploy/docker-compose.yml. The
fallbacks are the postgres:18 stock defaults (100 / 128MB / 4GB / 64MB),
so a deploy that does not set the variables behaves exactly as before.
Checked with postgres:18-alpine: with the variables unset, SHOW gives
the stock values; with them set (1024 / 1GB / 6GB / 128MB), SHOW gives
the set values.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PR #4506 fixed this in deploy/docker-compose.yml, but the same broken
form is still in docker-compose.dev.yml and docker-compose.local.yml.
The redis command is one quoted script given to the inner sh -c, and
compose keeps the newlines inside the quoted string, so redis-server on
the first line runs as a complete command with no flags at all. The
--save / --appendonly / --appendfsync lines are silently never applied,
and ${REDIS_PASSWORD:+--requirepass ...} is dead too — redis takes no
password even when REDIS_PASSWORD is set.
The fix is the same trailing `\` line continuations as #4506, with the
same comment, so the three compose files read the same way.
Checked with both files on redis:8-alpine, REDIS_PASSWORD set. Before:
PING with no auth said PONG, appendonly was "no", save was the stock
"3600 1 300 100 60 10000". After: no-auth PING gets NOAUTH, appendonly
is "yes", save is "60 1". With REDIS_PASSWORD unset the server still
starts open, as before.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Use *string for AnthropicResponse.StopReason so streaming message_start
serializes as JSON null instead of "", matching official Anthropic wire
format and avoiding strict client turn-finalization failures.
BatchImageGuideView carried ~140 user-visible strings outside vue-i18n:
hardcoded Chinese template strings (buttons, placeholders, tooltips,
table headers, dialog titles, empty states) plus an inline bilingual
batchImageText dictionary switched by isZhLocale() in script.
Move all of it into a new batchImage locale namespace (180 leaf keys
per language, zh/en verified key-aligned) registered in both locale
indexes, reusing 15 existing common.*/pagination.* keys. Column and
filter-option definitions become computed so language switches take
effect immediately; countable strings use vue-i18n plurals and the
three rich-text counters keep their inner bold spans via <i18n-t>
slots.
The Codex agent-instruction prompt template intentionally stays as a
plain string: it is an AI-facing prompt document containing literal
JSON braces that conflict with vue-i18n message syntax.
Verified: eslint, vue-tsc --noEmit, vite build, and the full vitest
suite (175 files / 1219 tests) all pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move the last few user-visible hardcoded strings into vue-i18n:
- AppHeader: "Toggle Menu" / "User Menu" aria-labels (screen readers
read these in English regardless of locale)
- CustomPageView: "Page not found" error body
- PaymentProviderDialog: display-name example placeholder hardcoded in
Chinese for all locales
Adds common.toggleMenu / common.userMenu / common.pageNotFound and
admin.settings.payment.customMethodDisplayNamePlaceholder to both zh
and en locales, and registers the new placeholder key in the
PaymentProviderDialog spec's i18n mock.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The project's dark theme uses the custom slate-based dark-* palette
(928 dark:bg-dark-* / 787 dark:border-dark-* occurrences), but a
minority of surfaces still used Tailwind's neutral gray-* scale, which
has a visibly cooler hue next to slate cards — most noticeably the
filter dropdown panels on the usage, accounts and subscriptions pages.
Migrate the remaining dark:bg-gray-* / dark:border-gray-* (and their
hover: variants) to the dark-* palette across 12 components: filter
dropdowns, chart card borders, menu separators, and small badges.
49 class-string substitutions, no structural or logic changes;
dark:text-gray-* is untouched since gray text on dark surfaces is the
established project-wide convention.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The user info banner in UserBalanceModal renders on a dark:bg-dark-700
container, but the email line kept text-gray-900 and the balance line
text-gray-500 with no dark variants, leaving near-black text on a dark
background. Align both with the pattern already used elsewhere in the
same dialog (text-gray-900 dark:text-gray-100 / text-gray-500
dark:text-gray-400).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The available channels table rendered inside its own
"card overflow-hidden" wrapper instead of the ".table-wrapper" element
that TablePageLayout's scroll chain expects. The outer
.table-scroll-container is overflow-hidden, so when the channel list
grew taller than the viewport the content was simply clipped with no
scrollbar. The extra card wrapper also drew a double border inside the
layout's own card chrome.
Mount the table on .table-wrapper like every other table page so it
receives overflow-y-auto from the layout, and add a source-convention
regression test in the style of TablePageLayout.spec.ts.
Fixes#4555
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Custom API key upstreams that only implement the standard OpenAI
GET /v1/models endpoint return {"object":"list","data":[{"id":...}]},
which the Codex models manifest validation rejected with
"missing top-level models array", surfacing HTTP 502 to Codex clients.
Convert the standard list envelope into the Codex manifest shape
({"models":[{"slug":...}]}) for API key upstreams before validation.
Bodies that already carry a top-level models field, are not the
standard list shape, or yield no usable model IDs are left unchanged
so existing validation and account failover behavior is preserved.
Fixes#4490
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reconcile with #4539 (grok media account model mapping), now on main:
- handler/grok_media.go non-failover error path keeps both changes —
#4539's grokMediaScheduleModel(account, routingModel, nil) schedule
attribution and this branch's IsResponseCommitted guard
- auto-merged sections verified: routing/classify use #4539's routingModel,
video lookup owner-binding and no-failover semantics intact, ForwardGrokMedia
keeps mapping block (skipped for lookup endpoints via RequiresRequestBody),
empty-image failover, and video-status URL rewrite in order
Resolve conflicts with main:
- service/grok_media.go: keep both post-response blocks — #4497's empty
image-output failover (main) runs first for image endpoints, then this
branch's video-status content-URL rewrite; the endpoint conditions are
mutually exclusive
- handler/openai_gateway_credential_failover_loop_test.go: mark the stub
OAuth accounts media-eligible via the grok_media_eligible extra override,
because this branch moved grok media failover coverage to the generation
endpoint, which is now gated by #4540's paid-eligibility probe on main
Resolve conflicts with main:
- handler/grok_media_test.go: keep both new tests (schedule model test from
this branch, eligibility gating tests from #4540)
- service/openai_gateway_grok_test.go: keep both new tests; update the image
cases of the mapping table test to return a non-empty image payload because
#4497 (already on main) now converts empty image responses into an upstream
failover error