/* 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, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { SideSheet, Typography, Button } from '@douyinfe/semi-ui'; import { IconClose, IconPlayCircle } from '@douyinfe/semi-icons'; import { API } from '../../../../helpers'; import { useIsMobile } from '../../../../hooks/common/useIsMobile'; import ModelHeader from './components/ModelHeader'; import ModelBasicInfo from './components/ModelBasicInfo'; import ModelEndpoints from './components/ModelEndpoints'; import ModelPricingTable from './components/ModelPricingTable'; import ModelChannelList from './components/ModelChannelList'; const { Text } = Typography; const ModelDetailSideSheet = ({ visible, onClose, modelData, groupRatio, groupModelPrice, groupModelRatio, currency, siteDisplayType, tokenUnit, displayPrice, showRatio, usableGroup, vendorsMap, endpointMap, autoGroups, t, selectedGroup, blurPricing = false, showCostPrice = false, channelModelRatioMap = {}, channelModelPriceMap = {}, channelCompletionRatioMap = {}, channelCacheRatioMap = {}, channelCreateCacheRatioMap = {}, channelImageRatioMap = {}, channelImagePriceMap = {}, channelAudioRatioMap = {}, channelAudioCompletionRatioMap = {}, channelVideoRatioMap = {}, channelVideoCompletionRatioMap = {}, channelVideoPriceMap = {}, }) => { const isMobile = useIsMobile(); const navigate = useNavigate(); const handleExperience = () => { const modelName = modelData?.model_name; const vendorId = modelData?.vendor_id; if (modelName) { const params = new URLSearchParams(); params.set('model', modelName); if (vendorId != null && vendorId !== 0) { params.set('model_type', vendorId); } navigate(`/console/playground?${params.toString()}`); } }; /** * channel_id -> 单测/运营展示 DTO(打开详情时按需拉取,不并入 /pricing) */ const [channelMtrMap, setChannelMtrMap] = useState({}); useEffect(() => { if ( !visible || !modelData?.model_name || !modelData?.channel_list?.length ) { setChannelMtrMap({}); return; } const ids = modelData.channel_list .map((c) => c.channel_id) .filter((id) => id != null && id !== ''); if (ids.length === 0) { setChannelMtrMap({}); return; } let cancelled = false; const params = new URLSearchParams(); params.set('model_name', modelData.model_name); params.set('channel_ids', ids.join(',')); (async () => { try { const res = await API.get( `/api/channel/model-test-results?${params.toString()}`, ); const { success, data } = res.data; if (!success || cancelled) { return; } const m = {}; (data || []).forEach((row) => { m[String(row.channel_id)] = row; }); if (!cancelled) { setChannelMtrMap(m); } } catch (e) { if (!cancelled) { setChannelMtrMap({}); } } })(); return () => { cancelled = true; }; }, [visible, modelData?.model_name, modelData?.channel_list]); return ( } bodyStyle={{ padding: '0', display: 'flex', flexDirection: 'column', borderBottom: '1px solid var(--semi-color-border)', }} visible={visible} width={isMobile ? '100%' : 600} closeIcon={