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 1 commit
    • 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 3 commits
    • erio's avatar
      test: add unit tests for channel platform matching, interval validation, credits check · 55343473
      erio authored
      - TestIsPlatformPricingMatch: 12 cases covering all platform combinations
      - TestMatchingPlatforms: 4 cases for platform expansion
      - TestGetChannelModelPricing_AntigravityCrossPlatform: antigravity sees anthropic pricing
      - TestGetChannelModelPricing_AnthropicCannotSeeAntigravityPricing: no reverse leakage
      - TestResolveChannelMapping_AntigravityCrossPlatform: antigravity uses anthropic mapping
      - TestFilterValidIntervals: 8 cases for empty interval filtering
      - TestHasEnoughCredits: 10 cases for credits balance threshold logic
      - Extract hasEnoughCredits() pure function for testability
      55343473
    • 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): 渠道管理系统 — 多模式定价 + 统一计费解析 · 91c9b8d0
      erio authored
      Cherry-picked from release/custom-0.1.106: a9117600
      91c9b8d0