Commit Graph
3981 Commits
Author SHA1 Message Date
erio 0e6d5817ba test(signature): add Redis pool unit tests with miniredis
Covers the Lua script logic (ZADD + lazy TTL expiry + capacity trim)
via in-process miniredis — no Docker required, runs under `go test
-tags unit`.

10 cases: basic add/topN, fewer-than-N, empty bucket, capacity
trim to newest 3, lazy expiry on Add, TopN-does-not-filter-by-TTL
(design rule: stale sigs survive until next Add), duplicate score
update, empty-input noop, zero-N guard, Size accuracy.

Also keeps the integration test file (build tag `integration`) for
environments with Docker + testcontainers.
2026-04-19 17:10:24 +08:00
erio 35bdd1400e chore: bump version to 0.1.114.10 2026-04-19 15:18:14 +08:00
erio 91ead361b7 test(signature): add unit tests + fix gjson ForEach indexing bug
Covers:
  - ReplaceThinkingSignaturesInBody / InClaudeRequest:
    M>N cycling, empty pool, no thinking blocks, empty signature
    preservation, string-content messages, nil pool guard
  - Strip / Pool rectifier strategies:
    Strip stage-1 unconditional, stage-2 gated on tool error;
    Pool empty→proceed=false (rule A), no replacements→abort,
    one-shot semantics (stage-2 always declines), pool error
    treated as empty
  - BucketFor: oauth/setup-token share, apikey per-id, unknown empty
  - Harvester SSE: content_block_start extraction, per-response
    dedupe, Skip callback, bucket/capacity guards
  - Harvester non-streaming: buffers until Close then parses once

Along the way caught and fixed a path-construction bug in
ReplaceThinkingSignaturesInBody: it used gjson.ForEach's key.Raw
which is empty for array indices, producing malformed sjson paths
like "messages..content..signature". Switched to manual index
counters so paths are always well-formed integers.
2026-04-19 14:00:01 +08:00
erio c8e4753ada feat(signature): frontend UI + i18n for signature pool size
Adds a Signature Pool Size numeric input to the Rectifier settings
section with a hint explaining the 0-vs-positive semantics. When
pool size > 0, the Thinking Signature and API Key Signature toggle
hints dynamically switch from "strip signatures and retry" to
"replace with pooled signatures (pass through when pool is empty)",
matching the runtime strategy swap.

Includes both zh and en locale keys (poolSize, poolSizeHint,
poolSizeUnit, thinkingSignatureHintPool, apikeySignatureHintPool)
plus the signature_pool_size field in the TypeScript RectifierSettings
interface and save payload.
2026-04-19 13:54:24 +08:00
erio 78de54b693 feat(signature): activate PoolRectifier + harvester hookup
Introduces the pool-replace retry strategy end-to-end. When
RectifierSettings has SignaturePoolSize>0 and the account's per-type
sub-switch is on, the factory returns a PoolClaudeRectifier /
PoolAntigravityRectifier that fetches the freshest signatures from the
Redis pool and cycles them through the request's thinking blocks
(M>N cycling). Rule A: an empty pool transparently passes the
original upstream error back — no fallback to strip.

Key pieces:
  - PoolClaudeRectifier / PoolAntigravityRectifier in internal/service/signature
  - signatureRectifierFactory in the service package selects strategy per
    request via shouldUsePool(ctx, account) which implements the agreed
    decision table (Enabled + SignaturePoolSize>0 + per-type sub-switch)
  - WrapResponseBody helper on the factory is invoked at each Claude-native
    DoWithTLS entry point (main Forward, Anthropic passthrough, Bedrock) to
    run the Harvester over the upstream body; no-op when pool disabled
  - ctxkey.IsSignatureRectifyRetry is set on all retry contexts (Claude
    two-stage, count_tokens, Antigravity signature retries) so the harvester
    skips ingesting signatures from retry responses and does not pollute the
    pool with values we ourselves injected
  - NewGatewayService / NewAntigravityGatewayService now accept
    signature.SignaturePool; wire_gen.go updated accordingly

Antigravity responses are raw Gemini format so WrapResponseBody is not
called there — harvesting stays Claude-only as designed. Antigravity can
still *read* from the shared OAuth pool on retry; whether cross-ecosystem
signatures verify upstream is the question the future PoC will answer.
2026-04-19 13:51:22 +08:00
erio 2df77c1604 feat(signature): add SignaturePool infrastructure (not yet wired)
Introduces the building blocks for the thinking-signature pool feature
without activating any runtime behavior. Nothing uses these new types
yet — Phase 3 will wire them into the retry loops.

New package internal/service/signature adds:
  - SignaturePool interface + Bucket helpers (oauth shared / apikey per-account)
  - ReplaceThinkingSignaturesInBody / ReplaceThinkingSignaturesInClaudeRequest
    pure functions that cycle through pool entries for M>N replacements
  - Harvester io.ReadCloser decorator for SSE + non-streaming JSON that
    extracts content_block.signature fields best-effort into the pool
  - 1h soft TTL constant for lazy expiry

New repository adapter internal/repository/signature_pool_cache.go
implements Redis ZSET storage with a single Lua script handling atomic
add + lazy expiry cleanup + capacity trim. Registered via
ProvideSignaturePool in the wire ProviderSet.

Settings extension: RectifierSettings gains a SignaturePoolSize int
field (0 = pool disabled / sticks with strip behavior; >0 = pool replace
is active). Threaded through service view, DTO, and handler GET/PUT
paths with bounds validation (max 1000).

ctxkey.IsSignatureRectifyRetry added so the harvester can later skip
ingesting signatures from retry requests we ourselves injected.
2026-04-19 12:43:11 +08:00
erio 84adf1d6de refactor(signature): extract retry body-transform into Rectifier strategy
Introduce internal/service/signature package with ClaudeRectifier and
AntigravityRectifier interfaces plus StripClaudeRectifier /
StripAntigravityRectifier implementations that wrap the legacy
FilterThinkingBlocksForRetry / FilterSignatureSensitiveBlocksForRetry
and stripThinkingFromClaudeRequest / stripSignatureSensitiveBlocksFromClaudeRequest
functions. Refactor the Claude and Antigravity retry loops
(including count_tokens) to delegate body transformation to the
rectifier, keeping all surrounding scaffolding (ops events, logging,
time budget checks, HTTP plumbing) unchanged.

Also extracts the looksLikeToolSignatureError predicate previously
inlined as an anonymous closure.

Zero behavior change: all existing unit tests pass unchanged. This
prepares for Phase 2 where a Pool strategy will be plugged in via
the same interface.
2026-04-19 12:33:56 +08:00
erio 2a7272429f fix(payment): size popup to fit alipay checkout without any scrolling
Alipay's page is ~1200x900; a fixed 1250x780 still clipped vertically.
Replace the constant with getPaymentPopupFeatures(): it prefers
1250x900, clamps to window.screen.avail*, and centers the popup so
it never overflows the visible work area on smaller laptops.

Drop POPUP_WINDOW_FEATURES and STRIPE_POPUP_WINDOW_FEATURES in favor of
the helper — all five call sites migrated.

Bump version to 0.1.114.9
2026-04-19 01:32:17 +08:00
erio 4b948e3917 fix(payment): widen popup to 1250x780 so alipay checkout fits without scrolling
Alipay's standard checkout (QR + account login panel) needs ~1200px
width. The default popup was 1000x750, forcing a horizontal scrollbar.
Unify to the wider size that the Stripe-alipay popup already used.

Bump version to 0.1.114.8
2026-04-19 01:24:53 +08:00
erio b3897940b8 fix(payment): stripe publishableKey must not be masked in admin GET
Previous pattern-based isSensitiveConfigField treated any key containing
"key" as a secret and stripped it from the admin response, which wrongly
hid Stripe's publishableKey (a public value). Edit dialogs then failed
its required-field validation because the backend no longer returned it.

Replace the pattern matcher with an explicit per-provider registry that
mirrors the frontend PROVIDER_CONFIG_FIELDS, so only true secrets are
masked/preserved:

- alipay: privateKey, publicKey, alipayPublicKey
- wxpay: privateKey, apiV3Key, publicKey
- stripe: secretKey, webhookSecret (publishableKey stays plaintext)
- easypay: pkey

Bump version to 0.1.114.7
2026-04-19 01:10:43 +08:00
erio 2599b9f278 feat(payment): hide secret config fields in admin GET and preserve on empty PUT
- Strip sensitive provider config keys (privateKey/publicKey/apiV3Key/
  secretKey/webhookSecret/pkey/password/etc.) from admin list/detail API
- mergeConfig: empty value for a sensitive key preserves the stored secret
- Admin dialog: sensitive inputs add autocomplete="new-password" +
  data-*ignore + spellcheck="false" to block browser password managers;
  edit-mode placeholder shows "leave empty to keep"; skip required
  validation for sensitive fields when editing

Bump version to 0.1.114.6
2026-04-19 01:01:19 +08:00
erio dcf56340be chore: bump version to 0.1.114.5 2026-04-18 23:46:28 +08:00
erio 5877cc9e6f fix(payment): frontend declares is_mobile in create-order request
Backend's UA heuristic (mobile|android|iphone|ipad|ipod) misidentifies
iPadOS 13+ (reports as Mac) and certain embedded browsers that strip
the "Mobile" keyword, so H5 users got a PC alipay.trade.page.pay URL.
Frontend then tried to window.location.href into it — navigation went
somewhere unexpected or the popup fallback left the user staring at
the paying-state frontend.

Let the frontend — which has navigator.userAgentData.mobile and better
context — declare is_mobile on the create-order request. Backend uses
the explicit value when present, falling back to UA detection only
when the client didn't send one (preserves old clients / direct API
callers).
2026-04-18 23:46:17 +08:00
erio ad754c905c chore: bump version to 0.1.114.4 2026-04-18 23:28:29 +08:00
erio 5347fd1eac fix(payment): switch PC alipay to redirect-only (trade.page.pay)
The previous change used TradePreCreate (FACE_TO_FACE_PAYMENT), which
requires the merchant to sign "当面付". Our merchant only has "电脑网站
支付" so Alipay returns ACQ.ACCESS_FORBIDDEN.

Go back to TradePagePay but fix the original bug differently: fill
only PayURL (do NOT fill QRCode). Frontend:
  - PC hits the pay_url branch → openWindow() popup (redirect to
    Alipay's own page, which shows login/QR natively)
  - H5 hits the mobile+pay_url branch → window.location.href jump

This matches Alipay's "电脑网站支付" UX and works with the current
merchant signing scope. No client-side QR encoding of the gateway URL
(which was never a valid scannable payload).
2026-04-18 23:28:21 +08:00
erio 73c87fe3c5 chore: bump version to 0.1.114.3 2026-04-18 23:16:09 +08:00
erio 60bc062b6c fix(payment): use alipay.trade.precreate for PC QR code
PC branch was calling TradePagePay (alipay.trade.page.pay), which
returns a gateway redirect URL — not a scannable QR payload. That URL
was being written to both PayURL and QRCode, so the frontend's qr_code
branch encoded the URL string into an image. Users scanning it got a
generic HTTP link instead of an Alipay order.

Switch PC to TradePreCreate (FACE_TO_FACE_PAYMENT): returns a native
qr_code string (qr.alipay.com/...) that renders as a real order QR.
Only fill QRCode, leave PayURL empty so the frontend takes the QR
popup branch. Surface empty-qr_code responses with sub_code/sub_msg
for debugging. H5 path (TradeWapPay) unchanged.
2026-04-18 23:15:58 +08:00
erio 5794dc96ef fix(lint): resolve 6 golangci-lint issues blocking CI
- safe/safe.go: pass context.Background() to slog.LogAttrs (SA1012)
- capture_fingerprint/main.go: wrap ln/raw Close() defers to swallow err
- capture_fingerprint/peek_conn.go, serve_h2.go: ignore bytes.Buffer.Write
  return (cannot fail)
- verify_fingerprint/main.go: wrap resp.Body.Close() defer

These were pre-existing issues on release/custom-0.1.114 also failing
on v0.1.114.1. Fixing now so the current CI run turns green.
2026-04-18 21:55:29 +08:00
erio cfd9566905 feat(tls-fingerprint): show binding count + fix randomized fingerprint visibility (v0.1.114.2)
## Bug fixes
- EditAccountModal: auto-generated profiles (__auto__:acc-*) are no longer
  hidden from the dropdown; accounts bound to their own auto profile can
  now see and keep the selection.
- AccountResponse DTO: emit tls_fingerprint_randomized so the "randomized"
  badge and reshuffle affordance render on subsequent edits.

## Feature
- TLS fingerprint profile list returns bound_account_count per profile,
  aggregated via a single grouped SQL query over accounts.extra.
- Dropdowns and admin management table surface the binding count so admins
  can judge whether a fingerprint is shared before editing or deleting it.

## Performance
- Migration 108 adds a partial + expression index on
  (extra->>'tls_fingerprint_profile_id') WHERE extra ? '...',
  enabling Index Only Scan + HashAggregate for the new query.
- Extraction uses (extra->>'...')::bigint so PostgreSQL performs the cast;
  Go-side parsing and NullString plumbing are removed.

## Backend
- AccountRepository: add CountByTLSFingerprintProfile; implement in ent
  repo via grouped raw SQL.
- TLSFingerprintProfileService: add ProfileWithBinding + ListWithBindingCount.
- Admin handler List now returns the enriched payload.
- AccountResponse DTO: add tls_fingerprint_randomized (optional, omitempty).
- Update all five AccountRepository test stubs for the new interface method
  (account_service_delete_test, gateway_multiplatform_test,
  gemini_multiplatform_test, ratelimit_session_window_test,
  server/api_contract_test).

## Frontend
- TLSFingerprintProfile type: add optional bound_account_count.
- EditAccountModal + CreateAccountModal: show " (N)" suffix on options
  with active bindings; drop the auto-profile filter.
- TLSFingerprintProfilesModal admin table: new "使用中 / In use" column
  with amber-highlighted count.
- i18n zh/en: add columns.boundAccounts.

## Compatibility
- New fields are all optional (omitempty) — existing OAuth accounts and
  older frontends behave as before.
- No data migration required; empty extra entries are ignored by index
  and aggregation alike.
2026-04-18 21:45:17 +08:00
erio 6d0e056244 feat(fingerprint): Claude Code CLI fingerprint mimicry suite (v0.1.114.1)
Squash-merge feat/fingerprint-mimic into release/custom-0.1.114.

Functional changes:
- TLS ClientHello mimicry: 17→52 cipher suites, ALPN http/1.1 only,
  X25519MLKEM768, JA3/JA4 aligned with official CLI baseline
- HTTP layer: User-Agent 2.1.112 sdk-cli, 3 new anthropic-beta tokens,
  Stainless headers synced
- Sidecar traffic: usage poll + count_tokens injection + startup probe
  (three fire-and-forget channels with jittered intervals)
- Sticky session UUID: per-account metadata.user_id reuse via Redis,
  WithSessionHash propagation through gateway
- 429 no-switch: short-circuit on rate-limit instead of cross-account fail-over
- Admin UI: per-account "randomize fingerprint" button + ConfirmDialog +
  i18n (zh/en, 9 keys)

Engineering hardening (fixes carried over from hai/snapshot):
- safe.Go/Run package: panic-recovering goroutine helper with slog
- redis.Nil treated as cache miss in identity cache
- SOCKS5 dialer uses ContextDialer for ctx cancellation propagation
- Token refresh selects on stopCh during retry backoff
- Sidecar goroutines wrapped with safe.Go
- .gitattributes forces LF for *.json (fixes baseline file CRLF drift)

Refactoring (zero behavior change):
- sidecar probe constants centralized
- identity_service rewrite preflight extracted into helpers
- capture_fingerprint tool split from 894 lines into 9 files
- gofmt sweep on all hai-imported files

Tests:
- safe package: panic recovery + attrs propagation
- tlsfingerprint: SOCKS5 dialer + capture parity
- identity sticky-session preflight paths (early-return + cache-hit)
- sidecar probe: jittered interval + dry-run
- token refresh: graceful shutdown during backoff

Beta-validated on 0.1.112.21 (commit 565da163 of feat/fingerprint-mimic).
Bumps VERSION 0.1.112.20 -> 0.1.114.1.
2026-04-18 16:07:36 +08:00
erio e89f879533 chore(payment): mark legacy AES ciphertext fallback as deprecated
明文 JSON 已经是新写入的默认格式;保留 AES 密文读取仅为兼容迁移期间的旧
记录,一旦所有部署通过管理后台重存过一次即可删除。标记为 deprecated 并加
TODO,几个版本后统一清理掉:payment.Encrypt / payment.Decrypt、两处
decryptConfig 的 AES 分支、PaymentConfigService.encryptionKey 和
DefaultLoadBalancer.encryptionKey 字段。
2026-04-17 23:32:51 +08:00
erio 2e360412ce Merge tag 'v0.1.114' into release/custom-0.1.114
支持 opus-4.7 模型,修复 outbox watermark 上下文过期导致 CPU 飙升的问题。

- 支持 opus-4.7 模型
- OpenAI API Key 账号调用 Claude Code 时注入 prompt_cache_key,提升缓存命中率

- 重构上游响应体读取逻辑,合并 9 处重复代码为统一的 ReadUpstreamResponseBody 函数

- 修复 outbox watermark 上下文过期导致水位线无法推进、相同事件反复处理引发 CPU 飙升的问题
- 修复 watermark 写入重试时复用已过期上下文的问题
- 新增同批次内 group rebuild 去重,减少约 80% 的冗余 rebuild 调用
- 上游返回 KYC 身份验证要求时停止账号调度
- 修复 OpenAI WS 标志在调度器缓存中丢失的问题
- 修复账号 UI 中连接池 WS 模式选项未显示的问题

# Conflicts:
#	backend/cmd/server/VERSION
#	backend/internal/server/api_contract_test.go
#	backend/internal/service/auth_service_register_test.go
#	backend/internal/service/billing_service_unified_test.go
#	backend/internal/service/channel.go
#	backend/internal/service/domain_constants.go
#	backend/internal/service/email_service.go
#	backend/internal/service/payment_config_providers.go
#	backend/internal/service/payment_refund.go
#	backend/internal/service/settings_view.go
#	frontend/src/api/payment.ts
#	frontend/src/components/account/AccountUsageCell.vue
#	frontend/src/utils/__tests__/usageLoadQueue.spec.ts
2026-04-17 20:36:18 +08:00
erio 801b53d6af refactor(gateway): extract upstream response read limit default to shared const
config.DefaultUpstreamResponseReadMaxBytes 同时被 viper 默认值和 service
层兜底引用,消除两份 128*1024*1024 魔法值的同步负担。
2026-04-17 19:38:07 +08:00
erio d359c1f762 chore: bump version to 0.1.112.20 2026-04-17 19:34:52 +08:00
erio 5ffa106e8b chore(gateway): bump upstream response read limit 64MB -> 128MB
单张 4K PNG base64 最坏情况可达 67MB,image_generation_call 返回多张
候选图时 64MB 仍可能被撑爆。128MB 能覆盖 2-3 张 4K PNG 的常见场景,且相对
请求体上限 256MB 仍有缓冲。
2026-04-17 19:34:52 +08:00
erio 7acec3a60d chore: bump version to 0.1.112.19 2026-04-17 18:32:53 +08:00
erio c3fcc37cc4 feat(gateway): raise upstream non-streaming response read limit to 64MB
非流式上游响应读取上限从 8MB 提到 64MB,避免图片生成(image_generation_call
返回 base64 内联图)单张高清图就把响应撑爆、被 `ReadUpstreamResponseBody`
直接拒绝成 upstream_error 502。

64MB ≈ 48MB 原始字节(base64 膨胀 33%),足够一次响应带多张高清图。依然通过
gateway.upstream_response_read_max_bytes 可配置。
2026-04-17 18:32:33 +08:00
erio 93efc12812 fix(billing): reject rate_multiplier <= 0 on save; clamp negatives to 0 in compute
分组倍率和用户专属倍率在保存时没有校验,0 会触发计费层的 `<=0 → 1.0`
防御条款,结果订阅/余额分组按标准价扣费;完全是沉默地绕过了业务规则。

- 保存校验(admin_service):CreateGroup / UpdateGroup / BatchSetGroupRateMultipliers /
  UpdateUser.SyncUserGroupRates 全部要求 > 0
- 计算层(billing_service):三处 `<=0 → 1.0` 改为 `<0 → 0`;负数按 0 结算,
  避免配置异常被静默按 1x 收费
- 前端:分组倍率 / 用户专属倍率输入 min 统一到 0.001
- 删除未使用的 IsFreeSubscription 方法

测试:新增 billing_service_rate_multiplier_test.go 端到端验证;更新原有锁定
旧 `<=0 → 1.0` 行为的测试。
2026-04-17 18:32:12 +08:00
erio 017f517a38 chore: bump version to 0.1.112.18 2026-04-17 17:01:26 +08:00
erio 718c886245 fix(admin): prevent browser password manager from autofilling account API key
Chrome's password manager matched the apikey-type account's Base URL + API Key
inputs as a login form and autofilled the last saved password by domain, so
editing a Gemini account could overwrite its apikey with a Claude key that
shared the same Base URL. Add autocomplete="new-password" plus data-*-ignore
attributes for 1Password / LastPass / Bitwarden to opt the field out of every
major password manager's autofill.
2026-04-17 17:01:01 +08:00
erio b9d5613169 fix(usage): subscription billing honours group rate multiplier
Subscription-mode billing was consuming quota at TotalCost (raw) instead of
ActualCost (TotalCost * RateMultiplier), so per-group rate multipliers —
including free subscriptions (multiplier = 0) — were silently ignored.
Switch the three subscription cost writes in buildUsageBillingCommand,
finalizePostUsageBilling, and the legacy postUsageBilling fallback to
ActualCost, and add a table-driven test covering 2x / 0.5x / free multipliers
plus a balance-mode regression check.
2026-04-17 17:00:45 +08:00
erio 40df055888 fix(payment): store provider config as plaintext JSON with legacy ciphertext fallback
Without TOTP_ENCRYPTION_KEY, saved payment configs were lost on restart because
the AES round-trip failed silently. Write new records as plaintext JSON; read
path tries JSON first, falls back to legacy AES decrypt when a key is present,
and treats unreadable values as empty so admins can re-enter them via the UI.
2026-04-17 17:00:29 +08:00
Wesley Liddick 358ff6a608 Merge pull request #1683 from FjlI5/dev-main
fix:修复上游账号为OpenAI API key时Claude Code调用缓存率低的问题
2026-04-17 10:28:12 +08:00
Wesley Liddick 41fbdba104 Merge pull request #1687 from touwaeriol/refactor/upstream-response-limit-dedup
refactor: extract ReadUpstreamResponseBody to deduplicate response read + too-large handling
2026-04-17 10:19:14 +08:00
Wesley Liddick c22d11cedd Merge pull request #1702 from StarryKira/fix/outbox-watermark-context-dedup-1691
fix: fix outbox watermark context expiry and add in-batch group rebuild dedup
2026-04-17 10:18:56 +08:00
shaw 5d586a9f3a fix: 上游返回 KYC 身份验证要求时停止账号调度 2026-04-17 10:17:50 +08:00
shaw a789c8c4c7 feat: 支持opus-4.7 2026-04-17 09:37:25 +08:00
ElysiaandClaude Sonnet 4.6 697c41a3f6 fix: create fresh context per watermark write retry attempt
Each retry in the SetOutboxWatermark loop now gets its own 5s context.
Previously a shared context could already be expired when the second or
third attempt ran, making the retries pointless.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 20:41:40 +08:00
ElysiaandClaude Sonnet 4.6 e44baa1094 fix: fix outbox watermark context expiry and add in-batch group rebuild dedup
Fixes #1691

- pollOutbox() reused a 10s context for SetOutboxWatermark after event
  processing could take much longer, causing "outbox watermark write
  failed: context deadline exceeded". The watermark never advanced so
  the same 200 events were reprocessed every poll cycle, spiking CPU.
  Now uses an independent 5s context with up to 3 retries (200ms apart).

- When multiple Codex accounts sharing the same 21-22 groups are all
  rate-limited in quick succession, each account_changed event triggered
  redundant bucket rebuild attempts for the same groups. Introduce
  batchSeenKey{groupID, platform} and thread a seen map through the
  handler chain; rebuildBucketsForPlatform skips (group, platform) pairs
  already rebuilt within the same poll batch (~80% fewer rebuild calls in
  the 5-accounts-same-groups scenario).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 19:09:40 +08:00
Wesley Liddick e6e73b4f52 Merge pull request #1690 from KnowSky404/fix/ws-codex-scheduler-cache-1662
fix: preserve openai ws flags in scheduler cache
2026-04-16 17:21:32 +08:00
shaw 7ea8e7e667 chore: update sponsors 2026-04-16 17:19:32 +08:00
shaw a55ead5ea8 chore: remove empty dir Antigravity-Manager 2026-04-16 16:42:40 +08:00
KnowSky404 836092a666 fix: restore ctx pool ws mode option in account ui 2026-04-16 02:13:04 +00:00
KnowSky404 3944b3d216 fix: preserve openai ws flags in scheduler cache 2026-04-16 02:01:50 +00:00
erio 10699eeb34 refactor: extract ReadUpstreamResponseBody to deduplicate upstream response read + too-large error handling
Consolidates 9 call sites of resolveUpstreamResponseReadLimit + readUpstreamResponseBodyLimited + ErrUpstreamResponseBodyTooLarge error handling into a single ReadUpstreamResponseBody function with TooLargeWriter callback for API-format-specific error responses (Anthropic, OpenAI, countTokens).
2026-04-16 01:53:22 +08:00
erio 645271d07a chore: bump version to 0.1.112.17 2026-04-16 01:34:03 +08:00
erio c48f44d109 refactor: extract ReadUpstreamResponseBody to deduplicate upstream response read + too-large error handling
Consolidates 9 call sites of resolveUpstreamResponseReadLimit + readUpstreamResponseBodyLimited + ErrUpstreamResponseBodyTooLarge error handling into a single ReadUpstreamResponseBody function with TooLargeWriter callback for API-format-specific error responses.
2026-04-16 01:33:47 +08:00
fjl5 6c89d8d35c add prompt_cache_key injection for messages→responses 2026-04-15 23:56:56 +08:00
github-actions[bot] be7551b9f4 chore: sync VERSION to 0.1.113 [skip ci] 2026-04-15 09:34:24 +00:00
Wesley Liddick 70d0569f08 Merge pull request #1668 from tyqy12/main
修复 OpenAI 账号限流回流误判:7d 窗口可用时不因 5h 窗口为 0 回写 429
2026-04-15 16:48:48 +08:00