1. 22 Apr, 2026 1 commit
    • erio's avatar
      fix(channels): supported models = mapping ∪ pricing with global LiteLLM fallback · 6cd7c605
      erio authored
      Why: channels with model pricing entries but no model mapping (e.g. azcc with
      3 priced claude models, no mapping) were rendering as 未配置模型 in the
      'Available Channels' page. The algorithm only iterated ModelMapping and
      silently dropped any platform without a mapping entry.
      
      Changes:
      - channel.go: SupportedModels now unions mapping + pricing entries.
        For exact mapping src → target, pricing is looked up by target (the actually
        billed name), not by src.
      - channel_available.go: ListAvailable enriches each entry with nil pricing
        via PricingService.GetModelPricing (global LiteLLM fallback) so the popover
        always shows a price.
      - channel_service.go: NewChannelService takes *PricingService as 4th param.
      - channel_test.go: rewrote 4 tests that froze the old mapping-only semantics;
        added pricing-only / mapping-target / target-missing coverage.
      6cd7c605
  2. 20 Apr, 2026 2 commits
    • erio's avatar
      refactor(channels): consolidate pricing index, tighten types, polish DTOs · 365ef1fd
      erio authored
      Follow-up to the available-channels review pass. No behavior change for
      end users; tightens internals based on three independent code reviews.
      
      Backend
      - service/channel.go: collapse buildPricingLookup + pricedNamesFor
        into a single platformPricingIndex (byLower + originalCase + ordered
        names), built once per SupportedModels call. Fixes a casing-
        consistency bug where the same logical model appeared with mapping
        case in the exact branch but pricing case in the wildcard branch —
        pricing's original case now wins everywhere.
      - service/channel.go: doc that a mapping key of just "*" expands to
        every priced model on the platform (intentional "passthrough all").
      - service/channel_available.go: normalize empty BillingModelSource to
        channel_mapped at construction time, removing the same fallback
        duplicated in the admin DTO mapper and the admin Vue template.
      - handler/admin/available_channel_handler.go: unexport
        availableChannelToAdminResponse (same-package usage only); mapper
        is now a pure passthrough.
      - handler/available_channel_handler.go: drop the middleware2 alias
        (no name collision in this file).
      
      Frontend
      - utils/pricing.ts: extract formatScaled, used by SupportedModelChip
        and PricingRow.
      - api/admin/channels.ts: re-export BillingMode from constants/channel;
        tighten Channel.status / billing_model_source to ChannelStatus /
        BillingModelSource (and same for AvailableChannel).
      - components/channels/AvailableChannelsTable.vue: drop dead
        withDefaults wrapper (loading is required, both call sites pass it).
      - views/admin/AvailableChannelsView.vue: drop the redundant
        || BILLING_MODEL_SOURCE_CHANNEL_MAPPED fallback (now applied in
        service layer); remove unused import.
      - i18n zh + en: delete unused tierLabel and tokenRange keys from
        both availableChannels.pricing and admin.availableChannels.pricing.
      
      Tests
      - New: SupportedModels_ExactKeyUsesPricedCaseWhenAvailable locks the
        pricing-case-wins rule.
      - New: SupportedModels_AsteriskOnlyMappingExpandsAllPriced documents
        the "*" expansion rule.
      - Admin handler: existing tests adjusted to pass an explicit
        BillingModelSource (default-fill is now exercised by service tests).
      365ef1fd
    • erio's avatar
      feat(channels): add "Available Channels" aggregate view · 654cfb64
      erio authored
      Add a read-only aggregate view per channel: its linked groups and a
      deterministic wildcard-free supported-model list with pricing details.
      
      Backend
      - service.Channel.SupportedModels(): combine ModelMapping keys with
        same-platform ModelPricing.Models; trailing "*" keys expand via
        pricing prefix match; platforms without a mapping produce no
        entries (intentional "no mapping = not shown" rule).
      - Extract splitWildcardSuffix() shared with toModelEntry.
      - Build a per-call pricing lookup map (platform+lowerName -> *pricing)
        to avoid O(N*M) scans in SupportedModels.
      - ChannelService.ListAvailable() aggregates channels + active groups;
        filters out group IDs no longer active.
      - Admin route GET /api/v1/admin/channels/available returns the full
        DTO (id, status, billing_model_source, restrict_models, groups,
        supported_models).
      - User route GET /api/v1/channels/available applies three filters:
        Status==active, visible-group intersection, and platform filter
        on supported_models (prevents cross-platform leak when a channel
        links to both a user-accessible group and an inaccessible one on
        another platform). Response is a plain array (matches the
        /groups/available sibling shape). Field whitelist omits
        billing_model_source, restrict_models, ids, status, sort_order.
      
      Frontend
      - New /admin/available-channels and /available-channels views backed
        by a shared AvailableChannelsTable component (admin adds status +
        billing-source columns via slots).
      - PricingRow extracted to its own SFC; SupportedModelChip references
        shared billing-mode constants in constants/channel.ts.
      - Sidebar: new entry above "渠道管理" for admin; matching entry in
        user nav.
      - i18n: zh + en coverage for both namespaces.
      
      Tests
      - SupportedModels: wildcard-only pricing skipped, prefix-matches-
        nothing, cross-platform bleed, case-insensitive dedup, empty
        platform mapping.
      - ListAvailable: nil groupRepo, inactive-group-ID dropped, stable
        case-insensitive name sort.
      - User handler: 401 on unauthenticated, visible-group intersection,
        platform filter on supported_models, JSON whitelist.
      - Admin handler: full DTO including default BillingModelSource
        fallback.
      
      Refs: issue #1729
      654cfb64
  3. 04 Apr, 2026 5 commits
    • erio's avatar
      fix: resolve 5 audit findings in channel/credits/scheduling · 71f61bbc
      erio authored
      P0-1: Credits degraded response retry + fail-open
      - Add isAntigravityDegradedResponse() to detect transient API failures
      - Retry up to 3 times with exponential backoff (500ms/1s/2s)
      - Invalidate singleflight cache between retries
      - Fail-open after exhausting retries instead of 5h circuit break
      
      P1-1: Fix channel restriction pre-check timing conflict
      - Swap checkClaudeCodeRestriction before checkChannelPricingRestriction
      - Ensures channel restriction is checked against final fallback groupID
      
      P1-2: Add interval pricing validation (frontend + backend)
      - Backend: ValidateIntervals() with boundary, price, overlap checks
      - Frontend: validateIntervals() with Chinese error messages
      - Rules: MinTokens>=0, MaxTokens>MinTokens, prices>=0, no overlap
      
      P2: Fix cross-platform same-model pricing/mapping override
      - Store cache keys using original platform instead of group platform
      - Lookup across matching platforms (antigravity→anthropic→gemini)
      - Prevents anthropic/gemini same-name models from overwriting each other
      71f61bbc
    • erio's avatar
      feat(channel): 渠道管理全链路集成 — 模型映射、定价、限制、用量统计 · 2555951b
      erio authored
      - 渠道模型映射:支持精确匹配和通配符映射,按平台隔离
      - 渠道模型定价:支持 token/按次/图片三种计费模式,区间分层定价
      - 模型限制:渠道可限制仅允许定价列表中的模型
      - 计费模型来源:支持 requested/upstream 两种计费模型选择
      - 用量统计:usage_logs 新增 channel_id/model_mapping_chain/billing_tier/billing_mode 字段
      - Dashboard 支持 model_source 维度(requested/upstream/mapping)查看模型统计
      - 全部 gateway handler 统一接入 ResolveChannelMappingAndRestrict
      - 修复测试:同步 SoraGenerationRepository 接口、SQL INSERT 参数、scan 字段
      2555951b
    • erio's avatar
      feat(channel): 缓存扁平化 + 网关映射集成 + 计费模式统一 + 模型限制 · ebac0dc6
      erio authored
      - 缓存重构为 O(1) 哈希结构 (pricingByGroupModel, mappingByGroupModel)
      - 渠道模型映射接入网关流程 (Forward 前应用, a→b→c 映射链)
      - 新增 billing_model_source 配置 (请求模型/最终模型计费)
      - usage_logs 新增 channel_id, model_mapping_chain, billing_tier 字段
      - 每种计费模式统一支持默认价格 + 区间定价
      - 渠道模型限制开关 (restrict_models)
      - 分组按平台分类展示 + 彩色图标
      - 必填字段红色星号 + 模型映射 UI
      - 去除模型通配符支持
      ebac0dc6
    • erio's avatar
      feat(channel): 模型标签输入 + $/MTok 价格单位 + 左开右闭区间 + i18n · dca0054e
      erio authored
      - 模型输入改为标签列表(输入回车添加,支持粘贴批量导入)
      - 价格显示单位改为 $/MTok(每百万 token),提交时自动转换
      - Token 模式增加图片输出价格字段(适配 Gemini 图片模型按 token 计费)
      - 区间边界改为左开右闭 (min, max],右边界包含
      - 默认价格作为未命中区间时的回退价格
      - 添加完整中英文 i18n 翻译
      dca0054e
    • erio's avatar
      feat(channel): 渠道管理系统 — 多模式定价 + 统一计费解析 · 91c9b8d0
      erio authored
      Cherry-picked from release/custom-0.1.106: a9117600
      91c9b8d0