/* Copyright (C) 2025 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 */ import React from 'react'; import { Button, Dropdown, InputNumber, Modal, Space, SplitButtonGroup, Tag, Tooltip, Typography, } from '@douyinfe/semi-ui'; import { timestamp2string, renderGroup, renderQuota, getChannelIcon, renderQuotaWithAmount, showSuccess, showError, } from '../../../helpers'; import { CHANNEL_OPTIONS } from '../../../constants'; import { IconTreeTriangleDown, IconMore, IconAlertTriangle, } from '@douyinfe/semi-icons'; import { FaRandom } from 'react-icons/fa'; // Render functions const renderType = (type, record = {}, t) => { const channelInfo = record?.channel_info; let type2label = new Map(); for (let i = 0; i < CHANNEL_OPTIONS.length; i++) { type2label[CHANNEL_OPTIONS[i].value] = CHANNEL_OPTIONS[i]; } type2label[0] = { value: 0, label: t('未知类型'), color: 'grey' }; let icon = getChannelIcon(type); if (channelInfo?.is_multi_key) { icon = channelInfo?.multi_key_mode === 'random' ? (
{icon}
) : (
{icon}
); } const typeTag = ( {type2label[type]?.label} ); let ionetMeta = null; if (record?.other_info) { try { const parsed = JSON.parse(record.other_info); if (parsed && typeof parsed === 'object' && parsed.source === 'ionet') { ionetMeta = parsed; } } catch (error) { // ignore invalid metadata } } if (!ionetMeta) { return typeTag; } const handleNavigate = (event) => { event?.stopPropagation?.(); if (!ionetMeta?.deployment_id) { return; } const targetUrl = `/console/deployment?deployment_id=${ionetMeta.deployment_id}`; window.open(targetUrl, '_blank', 'noopener'); }; return ( {typeTag}
{t('来源于 IO.NET 部署')}
{ionetMeta?.deployment_id && (
{t('部署 ID')}: {ionetMeta.deployment_id}
)} } > IO.NET
); }; const renderTagType = (t) => { return ( {t('标签聚合')} ); }; const renderStatus = (status, channelInfo = undefined, t) => { if (channelInfo) { if (channelInfo.is_multi_key) { let keySize = channelInfo.multi_key_size; let enabledKeySize = keySize; if (channelInfo.multi_key_status_list) { enabledKeySize = keySize - Object.keys(channelInfo.multi_key_status_list).length; } return renderMultiKeyStatus(status, keySize, enabledKeySize, t); } } switch (status) { case 1: return ( {t('已启用')} ); case 2: return ( {t('已禁用')} ); case 3: return ( {t('自动禁用')} ); default: return ( {t('未知状态')} ); } }; const renderMultiKeyStatus = (status, keySize, enabledKeySize, t) => { switch (status) { case 1: return ( {t('已启用')} {enabledKeySize}/{keySize} ); case 2: return ( {t('已禁用')} {enabledKeySize}/{keySize} ); case 3: return ( {t('自动禁用')} {enabledKeySize}/{keySize} ); default: return ( {t('未知状态')} {enabledKeySize}/{keySize} ); } }; const renderResponseTime = (responseTime, t) => { let time = responseTime / 1000; time = time.toFixed(2) + t(' 秒'); if (responseTime === 0) { return ( {t('未测试')} ); } else if (responseTime <= 1000) { return ( {time} ); } else if (responseTime <= 3000) { return ( {time} ); } else if (responseTime <= 5000) { return ( {time} ); } else { return ( {time} ); } }; const isRequestPassThroughEnabled = (record) => { if (!record || record.children !== undefined) { return false; } const settingValue = record.setting; if (!settingValue) { return false; } if (typeof settingValue === 'object') { return settingValue.pass_through_body_enabled === true; } if (typeof settingValue !== 'string') { return false; } try { const parsed = JSON.parse(settingValue); return parsed?.pass_through_body_enabled === true; } catch (error) { return false; } }; export const getChannelsColumns = ({ t, COLUMN_KEYS, updateChannelBalance, manageChannel, manageTag, submitTagEdit, testChannel, setCurrentTestChannel, setShowModelTestModal, setEditingChannel, setShowEdit, setShowEditTag, setEditingTag, copySelectedChannel, refresh, activePage, channels, checkOllamaVersion, setShowMultiKeyManageModal, setCurrentMultiKeyChannel, }) => { return [ { key: COLUMN_KEYS.ID, title: t('ID'), dataIndex: 'id', }, { key: COLUMN_KEYS.NAME, title: t('名称'), dataIndex: 'name', render: (text, record, index) => { const passThroughEnabled = isRequestPassThroughEnabled(record); const nameNode = record.remark && record.remark.trim() !== '' ? (
{record.remark}
} trigger='hover' position='topLeft' > {text}
) : ( {text} ); if (!passThroughEnabled) { return nameNode; } return ( {nameNode} ); }, }, { key: COLUMN_KEYS.GROUP, title: t('分组'), dataIndex: 'group', render: (text, record, index) => (
{text ?.split(',') .sort((a, b) => { if (a === 'default') return -1; if (b === 'default') return 1; return a.localeCompare(b); }) .map((item, index) => renderGroup(item))}
), }, { key: COLUMN_KEYS.TYPE, title: t('类型'), dataIndex: 'type', render: (text, record, index) => { if (record.children === undefined) { return <>{renderType(text, record, t)}; } else { return <>{renderTagType(t)}; } }, }, { key: COLUMN_KEYS.STATUS, title: t('状态'), dataIndex: 'status', render: (text, record, index) => { if (text === 3) { if (record.other_info === '') { record.other_info = '{}'; } let otherInfo = JSON.parse(record.other_info); let reason = otherInfo['status_reason']; let time = otherInfo['status_time']; return (
{renderStatus(text, record.channel_info, t)}
); } else { return renderStatus(text, record.channel_info, t); } }, }, { key: COLUMN_KEYS.RESPONSE_TIME, title: t('响应时间'), dataIndex: 'response_time', render: (text, record, index) =>
{renderResponseTime(text, t)}
, }, { key: COLUMN_KEYS.BALANCE, title: t('已用/剩余'), dataIndex: 'expired_time', render: (text, record, index) => { if (record.children === undefined) { return (
{renderQuota(record.used_quota)} updateChannelBalance(record)} > {renderQuotaWithAmount(record.balance)}
); } else { return ( {renderQuota(record.used_quota)} ); } }, }, { key: COLUMN_KEYS.PRIORITY, title: t('优先级'), dataIndex: 'priority', render: (text, record, index) => { if (record.children === undefined) { return (
{ manageChannel(record.id, 'priority', record, e.target.value); }} keepFocus={true} innerButtons defaultValue={record.priority} min={-999} size='small' />
); } else { return ( { Modal.warning({ title: t('修改子渠道优先级'), content: t('确定要修改所有子渠道优先级为 ') + e.target.value + t(' 吗?'), onOk: () => { if (e.target.value === '') { return; } submitTagEdit('priority', { tag: record.key, priority: e.target.value, }); }, }); }} innerButtons defaultValue={record.priority} min={-999} size='small' /> ); } }, }, { key: COLUMN_KEYS.WEIGHT, title: t('权重'), dataIndex: 'weight', render: (text, record, index) => { if (record.children === undefined) { return (
{ manageChannel(record.id, 'weight', record, e.target.value); }} keepFocus={true} innerButtons defaultValue={record.weight} min={0} size='small' />
); } else { return ( { Modal.warning({ title: t('修改子渠道权重'), content: t('确定要修改所有子渠道权重为 ') + e.target.value + t(' 吗?'), onOk: () => { if (e.target.value === '') { return; } submitTagEdit('weight', { tag: record.key, weight: e.target.value, }); }, }); }} innerButtons defaultValue={record.weight} min={-999} size='small' /> ); } }, }, { key: COLUMN_KEYS.OPERATE, title: '', dataIndex: 'operate', fixed: 'right', render: (text, record, index) => { if (record.children === undefined) { const moreMenuItems = [ { node: 'item', name: t('删除'), type: 'danger', onClick: () => { Modal.confirm({ title: t('确定是否要删除此渠道?'), content: t('此修改将不可逆'), onOk: () => { (async () => { await manageChannel(record.id, 'delete', record); await refresh(); setTimeout(() => { if (channels.length === 0 && activePage > 1) { refresh(activePage - 1); } }, 100); })(); }, }); }, }, { node: 'item', name: t('复制'), type: 'tertiary', onClick: () => { Modal.confirm({ title: t('确定是否要复制此渠道?'), content: t('复制渠道的所有信息'), onOk: () => copySelectedChannel(record), }); }, }, ]; if (record.type === 4) { moreMenuItems.unshift({ node: 'item', name: t('测活'), type: 'tertiary', onClick: () => checkOllamaVersion(record), }); } return ( ) : ( )} {record.channel_info?.is_multi_key ? ( { setCurrentMultiKeyChannel(record); setShowMultiKeyManageModal(true); }, }, ]} > )} ); } }, }, ]; };