- 14 Apr, 2026 16 commits
-
-
erio authored
Allow channels to configure independent model pricing for account statistics cost calculation, decoupled from user billing. Backend: - Migration 101: channels.apply_pricing_to_account_stats toggle, channel_account_stats_pricing_rules/model_pricing tables, usage_logs.account_stats_cost column - resolveAccountStatsCost: match rules by group/account, then channel pricing, fallback to original formula when unconfigured - Integrate into both GatewayService.recordUsageCore and OpenAIGatewayService.RecordUsage - Update 8 account stats SQL queries to use COALESCE(account_stats_cost, total_cost) * account_rate_multiplier - 23 unit tests for matching, pricing lookup, and cost calculation Frontend: - Channel edit dialog: toggle + custom rules UI with group/account multi-select and pricing entry cards - API types and i18n (zh/en)
-
erio authored
The settings API response now includes the new field; update the expected snapshot in TestAPIContracts to match.
-
erio authored
Inject web search capability for Claude Console (API Key) accounts that don't natively support Anthropic's web_search tool. When a pure web_search request is detected, the gateway calls Brave Search or Tavily API directly and constructs an Anthropic-protocol-compliant SSE/JSON response without forwarding to upstream. Backend: - New `pkg/websearch/` SDK: Brave and Tavily provider implementations with io.LimitReader, proxy support, and Redis-based quota tracking (Lua atomic INCR + TTL, DECR rollback on failure) - Global config via `settings.web_search_emulation_config` (JSON) with in-process cache + singleflight, input validation, API key merge on save, and sanitized API responses - Channel-level toggle via `channels.features_config` JSONB column (DB migration 101) - Account-level toggle via `accounts.extra.web_search_emulation` - Request interception in `Forward()` with SSE streaming response construction using json.Marshal (no manual string concatenation) - Manager hot-reload: `RebuildWebSearchManager()` called on config save and startup via `SetWebSearchRedisClient()` - 70 unit tests covering providers, manager, config validation, sanitization, tool detection, query extraction, and response building Frontend: - Settings → Gateway tab: Web Search Emulation config card with global toggle, provider list (add/remove, API key, priority, quota, proxy) - Channels → Anthropic tab: web search emulation toggle with global state linkage (disabled when global off) - Account Create/Edit modals: web search emulation toggle for API Key type with Toggle component - Full i18n coverage (zh + en)
-
erio authored
Backend fixes: - #1: doSub subscription idempotency via audit log check - #2: markFailed only when status=RECHARGING (prevents overwriting COMPLETED) - #3: ExpireTimedOutOrders checks upstream payment before expiring - #4: Public verify endpoint for payment result page (no auth required) - #5: EasyPay QueryOrder returns amount, confirmPayment handles zero amount - #6: WxPay notifyUrl priority: request-first, config-fallback - #7: EasyPay remove double URL decode in VerifyNotification - #8: checkPaid/cancelUpstreamPayment use order's provider instance - #9: Amount NaN/Inf/negative validation in order creation and refund - #10: Refund amount comparison uses tolerance instead of float64 == - #11: Skip balance deduction on retry when previous rollback failed - #12: checkPaid logs fulfillment errors instead of silently ignoring - #13: WxPay certSerial added to required config fields Frontend fixes: - Payment result page no longer requires authentication - Public verify API fallback for expired sessions
-
erio authored
Backend: - Define OrderTypeBalance/Subscription, EntityStatusActive, DeductionType*, NotificationStatus* constants in payment/types.go - Replace all magic strings in payment_order, payment_fulfillment, payment_refund - Add local constants in easypay.go (tradeStatusSuccess, signTypeMD5) - Add 27 unit tests for load balancer (filterByLimits, pickLeastAmount, getInstanceChannelLimits, startOfDay) Frontend: - Remove all `any` types in SettingsView.vue (18 catch blocks + 1 payload) - Fix bare catch blocks in PaymentResultView, PaymentView - Add `unknown` type annotation to all catch blocks chore: bump version to 0.1.108.140
-
erio authored
Tests (1033 new lines, 100% coverage on modified functions): - amount.go: YuanToFen/FenToYuan with precision edge cases - wxpay: mapWxState, wxSV, formatPEM, NewWxpay validation - alipay: isTradeNotExist, NewAlipay validation - webhook: writeSuccessResponse (wxpay JSON, stripe empty, others text) - config: validateProviderRequest, isSensitiveConfigField, joinTypes - fulfillment: resolveRedeemAction idempotency logic Business logic changes: - Allow empty supported_types on provider instances - Block removing payment types when instance has pending orders - Extract resolveRedeemAction as testable pure function
-
erio authored
Backend: - Extract YuanToFen/FenToYuan to payment/amount.go using shopspring/decimal - Require alipay publicKey in config validation - Fix wxpay webhook response to return JSON per V3 spec - Remove wxpay certSerial fallback to publicKeyId - Define magic strings as named constants in wxpay/alipay providers - Add slog warning for wxpay H5→Native payment downgrade - Make EncryptionKey validation return error on invalid (non-empty) key - Make decryptConfig propagate errors instead of returning nil - Add idempotency check in doBalance to prevent stuck FAILED retries Frontend: - Fix dashboard currency symbol from $ to ¥ - Fix AdminPaymentPlansView any type to proper SubscriptionPlan type - Make quick amount buttons follow selected payment method limits - Center help image with larger height and text below
-
erio authored
The paginated List query was selecting 9 columns but scanning 10 fields, missing c.features. GetByID and ListAll already included it correctly.
-
erio authored
-
erio authored
- Delete payment_channels table and PaymentChannel Ent schema - Add `features` column to upstream channels table (migration 095) - Add Features field to Channel struct, input types, handler request/response - Payment user/admin handlers now use ChannelService directly - Remove Channel CRUD from PaymentConfigService and admin payment routes - Remove "渠道管理" tab from admin orders page (use /admin/channels)
-
erio authored
-
erio authored
- Change channel cache TTL from 60s to 10min (reduce unnecessary DB queries) - Actively rebuild cache after CRUD instead of lazy invalidation - Add slog.Warn logging for channel pricing restriction blocks (4 places)
-
erio authored
- Fix 7 stale comments still mentioning "限制检查" in handlers/services - Make billingModelForRestriction explicitly list channel_mapped case - Add slog.Warn for error swallowing in ResolveChannelMapping and needsUpstreamChannelRestrictionCheck - Document sticky session upstream check exemption
-
erio authored
Move the model pricing restriction check from 8 handler entry points to the account scheduling phase (SelectAccountForModelWithExclusions / SelectAccountWithLoadAwareness), aligning restriction with billing: - requested: check original request model against pricing list - channel_mapped: check channel-mapped model against pricing list - upstream: per-account check using account-mapped model Handler layer now only resolves channel mapping (no restriction). Scheduling layer performs pre-check for requested/channel_mapped, and per-account filtering for upstream billing source.
-
erio authored
-
erio authored
Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 13 Apr, 2026 13 commits
-
-
github-actions[bot] authored
-
shaw authored
Legacy instances created the settings table via ent auto-migration, which emits Go-level defaults only. Migration 005 uses CREATE TABLE IF NOT EXISTS, so the missing SQL DEFAULT was never backfilled. This caused 098's raw INSERT to fail with a NOT NULL violation on updated_at. The new migration is idempotent and safe for fresh installs (no-op) and historical instances (backfills the default).
-
shaw authored
-
Wesley Liddick authored
fix(sidebar): prevent version update dropdown clipping
-
Wesley Liddick authored
fix(gateway): 兼容 Cursor /v1/chat/completions 的 Responses API body
-
Wesley Liddick authored
fix(frontend): reduce account usage request fan-out on pagination
-
Wesley Liddick authored
fix(frontend): lower QR code error correction level to reduce density
-
Wesley Liddick authored
fix(payment): register Alipay/Wxpay providers for base payment types
-
Wesley Liddick authored
fix: anthropic 非流式路径丢失 delta 内容(镜像 b2e379cf)
-
knowsky404 authored
-
sakurawztlt authored
b2e379cf 引入的 BufferedResponseAccumulator 已修复了 chat_completions 非流式路径和 responses OAuth 非流式路径,但遗漏了 Anthropic /v1/messages 非流式路径 (handleAnthropicBufferedStreamingResponse)。 当客户端请求 stream=false 且模型开启思考时,上游 response.completed 终态事件的 output 字段可能为空,实际 message 内容通过 response.output_text.delta 增量事件下发。旧代码只读终态事件的 Response, 导致客户端收到的 content 字段为空 ([{"type":"text"}])。 本 commit 将 b2e379cf 的相同修复模式镜像到 Anthropic 路径:在 SSE 扫描 过程中用 BufferedResponseAccumulator 累积 delta 内容,终态 output 为空 时通过 SupplementResponseOutput 补充重建。 同时修复 handleAnthropicBufferedStreamingResponse 遗漏 response.done 事件类型的问题,与 chat completions 路径保持一致,避免上游发送 response.done 时 handler 认不出终态事件、最终返回 502 的潜在问题。 BufferedResponseAccumulator 已在 chatcompletions_responses_test.go 有 完整单元测试覆盖(TextOnly/ToolCalls/Reasoning/Mixed/SupplementEmpty/ NoSupplementWhenOutputExists/EmptyDeltas/IgnoresNonFunctionCallItems), 本次复用相同累加器无需新增测试。
-
erio authored
Closes #1607
-
erio authored
fix(payment): fix Alipay/Wxpay direct provider type mapping and enable cross-provider load balancing Two issues fixed: 1. Alipay.SupportedTypes() returned ["alipay_direct"] and Wxpay returned ["wxpay_direct"], but the frontend sends payment_type="alipay"/"wxpay". The registry lookup failed with "payment method (alipay) is not configured". Fix: return the base types ["alipay"]/["wxpay"]. 2. When multiple providers support the same payment type (e.g. EasyPay and Alipay direct both handle "alipay"), only the last-registered provider's instances were reachable — the registry mapped one type to one provider key, and SelectInstance queried by that single key. Fix: bypass the registry in invokeProvider and let SelectInstance query across all providers when providerKey is empty. The selected instance's own ProviderKey (now included in InstanceSelection) is used to create the correct provider, enabling true cross-provider load balancing. Closes #1592
-
- 12 Apr, 2026 10 commits
-
-
qingyuzhang authored
-
qingyuzhang authored
-
github-actions[bot] authored
-
shaw authored
-
shaw authored
-
Wesley Liddick authored
fix: handle Anthropic credit balance exhausted (400) as account error
-
Wesley Liddick authored
fix(sidebar): smooth sidebar collapse behavior
-
Wesley Liddick authored
fix(frontend): 补全 messages 调度国际化文案
-
Wesley Liddick authored
docs(payment): add built-in payment configuration guide and settings links
-
bot authored
When an Anthropic API key's credit balance is depleted, the upstream returns HTTP 400 with message containing "credit balance". Previously, the 400 handler only checked for "organization has been disabled", so credit-exhausted accounts kept being scheduled — every request returned the same error. Treat this case identically to 402 (Payment Required): call handleAuthError → SetError to stop scheduling the account until an admin manually recovers it after topping up credits. Closes #1586
-
- 11 Apr, 2026 1 commit
-
-
qingyuzhang authored
# Conflicts: # frontend/src/components/layout/AppSidebar.vue
-