/*
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',
}