/* Copyright (C) 2023-2026 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ // ============================================================================ // Channel Types (from constant/channel.go) // All label/name values are i18n keys; use t(value) when displaying. // ============================================================================ export const CHANNEL_TYPE_NEW_API = 60 export const CHANNEL_TYPES = { 0: 'Unknown', 1: 'OpenAI', 2: 'MjProxy', 3: 'Azure', 4: 'Ollama', 5: 'MjProxyPlus', // 6: 'OpenAIMax', 7: 'OhMyGPT', 8: 'Custom', // 9: 'AILS', // 10: 'AI Proxy', // 11: 'PaLM', // 12: 'API2GPT', // 13: 'AIGC2D', 14: 'Anthropic', 15: 'Baidu', 16: 'Zhipu', 17: 'Ali', 18: 'Xunfei', 19: '360', 20: 'OpenRouter', // 21: 'AI Proxy Library', 22: 'FastGPT', 23: 'Tencent', 24: 'Gemini', 25: 'Moonshot', 26: 'Zhipu V4', 27: 'Perplexity', 31: 'LingYiWanWu', 33: 'AWS', 34: 'Cohere', 35: 'MiniMax', 36: 'SunoAPI', 37: 'Dify', 38: 'Jina', 39: 'Cloudflare', 40: 'SiliconFlow', 41: 'Vertex AI', 42: 'Mistral', 43: 'DeepSeek', 44: 'MokaAI', 45: 'VolcEngine', 46: 'Baidu V2', 47: 'Xinference', 48: 'xAI', 49: 'Coze', 50: 'Kling', 51: 'Jimeng', 52: 'Vidu', 53: 'Submodel', 54: 'DoubaoVideo', 55: 'Sora', 56: 'Replicate', 57: 'ChatGPT Subscription (Codex)', 58: 'Advanced Custom', 59: 'Sub2API', 60: 'New API', } as const const CHANNEL_TYPE_DISPLAY_ORDER: number[] = [ 1, 14, 33, 24, 43, 3, 41, 48, 60, 58, 42, 34, 20, 4, 40, 27, 25, 17, 26, 15, 46, 23, 18, 45, 31, 35, 49, 19, 47, 37, 38, 39, 11, 8, 57, 59, 22, 21, 44, 2, 5, 36, 50, 51, 52, 53, 54, 55, 56, ] export const CHANNEL_TYPE_OPTIONS: { value: number; label: string }[] = (() => { const ordered: { value: number; label: string }[] = [] const seen = new Set() for (const id of CHANNEL_TYPE_DISPLAY_ORDER) { const label = CHANNEL_TYPES[id as keyof typeof CHANNEL_TYPES] if (label) { ordered.push({ value: id, label }) seen.add(id) } } for (const [key, label] of Object.entries(CHANNEL_TYPES)) { const id = Number(key) if (id !== 0 && !seen.has(id)) { ordered.push({ value: id, label }) } } return ordered })() // ============================================================================ // Channel Status (label values are i18n keys; use t(config.label) in components) // ============================================================================ export const CHANNEL_STATUS = { UNKNOWN: 0, ENABLED: 1, MANUAL_DISABLED: 2, AUTO_DISABLED: 3, } as const export const CHANNEL_STATUS_LABELS = { [CHANNEL_STATUS.UNKNOWN]: 'Unknown', [CHANNEL_STATUS.ENABLED]: 'Enabled', [CHANNEL_STATUS.MANUAL_DISABLED]: 'Disabled', [CHANNEL_STATUS.AUTO_DISABLED]: 'Auto Disabled', } as const export const CHANNEL_STATUS_OPTIONS = [ { value: 'all', label: 'All Status' }, { value: 'enabled', label: 'Enabled' }, { value: 'disabled', label: 'Disabled' }, ] as const export const CHANNEL_STATUS_CONFIG = { [CHANNEL_STATUS.UNKNOWN]: { variant: 'neutral' as const, label: 'Unknown', }, [CHANNEL_STATUS.ENABLED]: { variant: 'success' as const, label: 'Enabled', }, [CHANNEL_STATUS.MANUAL_DISABLED]: { variant: 'danger' as const, label: 'Disabled', }, [CHANNEL_STATUS.AUTO_DISABLED]: { variant: 'warning' as const, label: 'Auto Disabled', }, } // ============================================================================ // Multi-Key Status // ============================================================================ export const MULTI_KEY_STATUS = { ENABLED: 1, MANUAL_DISABLED: 2, AUTO_DISABLED: 3, } as const export const MULTI_KEY_STATUS_LABELS = { [MULTI_KEY_STATUS.ENABLED]: 'Enabled', [MULTI_KEY_STATUS.MANUAL_DISABLED]: 'Manual Disabled', [MULTI_KEY_STATUS.AUTO_DISABLED]: 'Auto Disabled', } as const export const MULTI_KEY_STATUS_CONFIG = { [MULTI_KEY_STATUS.ENABLED]: { variant: 'success' as const, label: 'Enabled', }, [MULTI_KEY_STATUS.MANUAL_DISABLED]: { variant: 'neutral' as const, label: 'Manual Disabled', }, [MULTI_KEY_STATUS.AUTO_DISABLED]: { variant: 'danger' as const, label: 'Auto Disabled', }, } // ============================================================================ // Multi-Key Modes // ============================================================================ export const MULTI_KEY_MODES = [ { value: 'random', label: 'Random' }, { value: 'polling', label: 'Polling' }, ] as const export const ADD_MODE_OPTIONS = [ { value: 'single', label: 'Single Key' }, { value: 'batch', label: 'Batch Add (one key per line)' }, { value: 'multi_to_single', label: 'Multi-Key Mode (multiple keys, one channel)', }, ] as const // ============================================================================ // Multi-Key Management // ============================================================================ export const MULTI_KEY_FILTER_OPTIONS = [ { value: 'all', label: 'All Status' }, { value: '1', label: 'Enabled' }, { value: '2', label: 'Manual Disabled' }, { value: '3', label: 'Auto Disabled' }, ] as const export const MULTI_KEY_CONFIRM_MESSAGES = { DELETE: 'Are you sure you want to delete this key? This action cannot be undone.', ENABLE: 'Enable this key?', DISABLE: 'Disable this key?', ENABLE_ALL: 'Are you sure you want to enable all keys?', DISABLE_ALL: 'Are you sure you want to disable all enabled keys?', DELETE_DISABLED: 'Are you sure you want to delete all auto-disabled keys? This action cannot be undone.', } as const // ============================================================================ // Auto Ban Options // ============================================================================ export const AUTO_BAN_OPTIONS = [ { value: 1, label: 'Enabled' }, { value: 0, label: 'Disabled' }, ] as const // ============================================================================ // Error / Success Messages (i18n keys: use t(ERROR_MESSAGES.xxx) when displaying) // ============================================================================ export const ERROR_MESSAGES = { REQUIRED_NAME: 'Channel name is required', REQUIRED_TYPE: 'Channel type is required', REQUIRED_KEY: 'API key is required', REQUIRED_MODELS: 'Models are required', REQUIRED_GROUP: 'Group is required', INVALID_JSON: 'Invalid JSON format', INVALID_MODEL_MAPPING: 'Invalid model mapping format', INVALID_PROXY: 'Proxy address must use HTTP, HTTPS, SOCKS5, or SOCKS5H and include a valid host', INVALID_HTTP_PROTOCOL: 'HTTP protocol must be Auto or HTTP/1.1', INVALID_HTTP2_CONNECTION_SHARDS: 'HTTP/2 connection shards must be between 1 and 8', INVALID_HTTP1_WITH_SHARDS: 'HTTP/2 connection shards must be 1 when HTTP/1.1 is selected', CREATE_FAILED: 'Failed to create channel', UPDATE_FAILED: 'Failed to update channel', DELETE_FAILED: 'Failed to delete channel', TEST_FAILED: 'Failed to test channel', BALANCE_QUERY_FAILED: 'Failed to query balance', FETCH_MODELS_FAILED: 'Failed to fetch models', } as const export const SUCCESS_MESSAGES = { CREATED: 'Channel created successfully', UPDATED: 'Channel updated successfully', DELETED: 'Channel deleted successfully', ENABLED: 'Channel enabled successfully', DISABLED: 'Channel disabled successfully', TESTED: 'Channel test completed', BALANCE_QUERIED: 'Balance queried successfully', MODELS_FETCHED: 'Models fetched successfully', COPIED: 'Channel copied successfully', TAG_SET: 'Tag set successfully', BATCH_DELETED: 'Channels deleted successfully', } as const // ============================================================================ // Default Values // ============================================================================ export const DEFAULT_PAGE_SIZE = 20 export const DEFAULT_CHANNEL_VALUES = { name: '', type: 0, base_url: '', key: '', models: '', group: 'default', status: CHANNEL_STATUS.ENABLED, priority: 0, weight: 0, auto_ban: 1, remark: '', } as const // ============================================================================ // Table Configuration // ============================================================================ export const CHANNELS_TABLE_PAGE_SIZE_OPTIONS = [10, 20, 50, 100] // ============================================================================ // Sort Options (label values are i18n keys) // ============================================================================ export const SORT_OPTIONS = [ { value: 'priority', label: 'Priority (Default)' }, { value: 'id', label: 'ID' }, { value: 'name', label: 'Name' }, { value: 'balance', label: 'Balance' }, { value: 'response_time', label: 'Response Time' }, ] as const // ============================================================================ // Balance Display // ============================================================================ export const BALANCE_THRESHOLDS = { LOW: 1, MEDIUM: 10, HIGH: 100, } as const // ============================================================================ // Response Time Thresholds (in ms) // ============================================================================ export const RESPONSE_TIME_THRESHOLDS = { EXCELLENT: 500, GOOD: 1000, FAIR: 2000, POOR: 5000, } as const export const RESPONSE_TIME_CONFIG = { EXCELLENT: { variant: 'success' as const, label: 'Excellent' }, GOOD: { variant: 'success' as const, label: 'Good' }, FAIR: { variant: 'warning' as const, label: 'Fair' }, POOR: { variant: 'danger' as const, label: 'Poor' }, UNKNOWN: { variant: 'neutral' as const, label: 'Not tested' }, } as const // ============================================================================ // Field Hints and Placeholders (i18n keys; use t() when displaying) // ============================================================================ export const FIELD_PLACEHOLDERS = { NAME: 'e.g., OpenAI GPT-4 Production', BASE_URL: 'Leave empty to use default', KEY: 'API Key (one per line for batch mode)', MODELS: 'Comma-separated model names, e.g., gpt-4,gpt-3.5-turbo', GROUP: 'Please Select user groups that can access this channel.', MODEL_MAPPING: '{"request_model": "actual_model"}', TEST_MODEL: 'Model to use for testing', TAG: 'Optional tag for grouping channels', REMARK: 'Optional notes about this channel', PARAM_OVERRIDE: '{"temperature": 0.7}', HEADER_OVERRIDE: '{"X-Custom-Header": "value"}', STATUS_CODE_MAPPING: '{"400": "500"}', } as const export const FIELD_DESCRIPTIONS = { NAME: 'Friendly name to identify this channel', TYPE: 'Provider type (OpenAI, Anthropic, etc.)', BASE_URL: 'Custom API base URL. Leave empty to use provider default.', KEY: 'API key from the provider', MODELS: 'List of models supported by this channel. Use comma to separate multiple models.', GROUP: 'User groups that can access this channel. ', MODEL_MAPPING: 'Map request model names to actual provider model names (JSON format)', PRIORITY: 'Higher priority channels are selected first', WEIGHT: 'Used for load balancing. Higher weight = more requests', TEST_MODEL: 'Model to use when testing channel connectivity', AUTO_BAN: 'Automatically disable channel on repeated failures', STATUS_CODE_MAPPING: 'Map response status codes (JSON format)', TAG: 'Group channels by tag for batch operations', REMARK: 'Internal notes (not shown to users)', SETTING: 'Channel-specific settings (JSON format)', PARAM_OVERRIDE: 'Override request parameters (JSON format)', HEADER_OVERRIDE: 'Override request headers (JSON format)', MULTI_KEY_MODE: 'How to select keys: random or sequential polling', BATCH_ADD: 'Create multiple channels from multiple keys', OPENAI_ORG: 'OpenAI Organization ID (optional)', } as const // ============================================================================ // Channel Type Specific Configurations // ============================================================================ export const MODEL_FETCHABLE_TYPES = new Set([ 1, 4, 14, 17, 20, 23, 24, 25, 26, 27, 31, 34, 35, 40, 42, 43, 47, 48, 57, 58, 59, 60, ]) export const TYPE_TO_KEY_PROMPT: Record = { 15: 'Format: APIKey|SecretKey', 18: 'Format: APPID|APISecret|APIKey', 22: 'Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041', 23: 'Format: TokenHub API Key, or legacy AppId|SecretId|SecretKey', 33: 'Format: Ak|Sk|Region', 50: 'Format: AccessKey|SecretKey (or just ApiKey if upstream is New API)', 51: 'Format: Access Key ID|Secret Access Key', 57: 'Paste Codex OAuth JSON credential (access_token / refresh_token / account_id)', 59: 'Enter API key for this channel', 60: 'Enter API key for this channel', } export const CHANNEL_TYPE_WARNINGS: Record = { 3: 'For channels added after May 10, 2025, no need to remove "." from model names during deployment', 8: 'If connecting to upstream One API or New API relay projects, use OpenAI type instead unless you know what you are doing', 37: 'Dify channels only support chatflow and agent, and agent does not support images', }