diff --git a/src/components/seal-form/seal-cascader.tsx b/src/components/seal-form/seal-cascader.tsx index 7e9f1cc0..7806d013 100644 --- a/src/components/seal-form/seal-cascader.tsx +++ b/src/components/seal-form/seal-cascader.tsx @@ -3,6 +3,7 @@ import { isNotEmptyValue } from '@/utils/index'; import { useIntl } from '@umijs/max'; import type { CascaderAutoProps } from 'antd'; import { Cascader, Empty, Form } from 'antd'; +import classNames from 'classnames'; import _, { cloneDeep } from 'lodash'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import AutoTooltip from '../auto-tooltip'; @@ -10,28 +11,6 @@ import { SealFormItemProps } from './types'; import Wrapper from './wrapper'; import SelectWrapper from './wrapper/select'; -const tag = (props: any) => { - if (props.isMaxTag) { - return props.label?.slice(0, -3); - } - const parent = _.split(props.value, '__RC_CASCADER_SPLIT__')?.[0]; - return `${parent} / ${props?.label}`; -}; - -const renderTag = (props: any) => { - return ( - - {tag(props)} - - ); -}; - const OptionNodes = (props: { data: any; notFoundContent?: React.ReactNode; @@ -100,7 +79,9 @@ const SealCascader: React.FC< alwaysFocus = false, optionNode, notFoundContent, + size = 'middle', tagRender, + displayRender, ...rest } = props; const intl = useIntl(); @@ -172,10 +153,34 @@ const SealCascader: React.FC< props.onOpenChange?.(open); }; + const tag = (props: any) => { + if (props.isMaxTag) { + return props.label?.slice(0, -3); + } + const parent = _.split(props.value, '__RC_CASCADER_SPLIT__')?.[0]; + return displayRender ? props.label : `${parent} / ${props?.label}`; + }; + + const renderTag = (props: any) => { + return ( + + {tag(props)} + + ); + }; + return ( } optionRender={(data) => ( )} - tagRender={tagRender ?? renderTag} + tagRender={tagRender || renderTag} + displayRender={displayRender} ref={inputRef} options={children ? null : _options} onFocus={handleOnFocus} diff --git a/src/components/seal-form/simple-select.tsx b/src/components/seal-form/simple-select.tsx index 76b8ab6a..8d7c4bd1 100644 --- a/src/components/seal-form/simple-select.tsx +++ b/src/components/seal-form/simple-select.tsx @@ -179,8 +179,8 @@ const SimpleSelect: React.FC = closable={props.closable} onClose={props.onClose} style={{ - height: 24, - backgroundColor: 'var(--ant-color-fill-tertiary)', + height: 22, + backgroundColor: 'var(--ant-color-fill-secondary)', fontSize: 'var(--ant-font-size)' }} className="flex-center" diff --git a/src/components/seal-form/wrapper/select.ts b/src/components/seal-form/wrapper/select.ts index a0085484..6eb3bf97 100644 --- a/src/components/seal-form/wrapper/select.ts +++ b/src/components/seal-form/wrapper/select.ts @@ -146,6 +146,9 @@ const SelectWrapper = styled.div` margin-inline-start: 0 !important; } } + &.seal-cascader-small { + height: 40px; + } .ant-select-input { height: ${INPUTHEIGHT}px !important; @@ -190,6 +193,21 @@ const SelectWrapper = styled.div` } } } + &.seal-cascader-wrapper-small { + height: 40px; + .cascader-popup-wrapper { + top: 39px !important; + } + .ant-select-input { + height: 36px !important; + } + .ant-select { + padding-inline: 12px !important; + } + .__wrapper__.no-label .ant-select.ant-cascader .ant-select-placeholder { + top: 50% !important; + } + } } `; diff --git a/src/pages/dashboard/components/usage-inner/export-data.tsx b/src/pages/dashboard/components/usage-inner/export-data.tsx index 69df4fe6..00af7db8 100644 --- a/src/pages/dashboard/components/usage-inner/export-data.tsx +++ b/src/pages/dashboard/components/usage-inner/export-data.tsx @@ -24,6 +24,7 @@ const ExportData: React.FC<{ result, userList, modelList, + selectedModels, query, setQuery, handleExport, @@ -37,6 +38,21 @@ const ExportData: React.FC<{ disabledDate: false }); + const getModelName = (record: any) => { + if (record.model_id) { + const children = + modelList.find((item) => item.value === 'deployments')?.children || []; + return ( + children?.find((item) => item.value === record.model_id)?.label || + record.model_id + ); + } + const provider = + modelList.find((item) => item.value === record.provider_id)?.label || + record.provider_id; + return `${provider} / ${record.model_name}`; + }; + const exportTableColumns: TableColumnType[] = [ { title: intl.formatMessage({ id: 'resources.table.index' }), @@ -63,12 +79,9 @@ const ExportData: React.FC<{ { title: intl.formatMessage({ id: 'dashboard.usage.export.model' }), dataIndex: 'model_id', - render: (text: string) => { - return ( - - {modelList.find((item) => item.value === text)?.label || text} - - ); + render: (text: string, record: any) => { + console.log('render model id: ', record, modelList); + return {getModelName(record)}; } }, @@ -91,6 +104,8 @@ const ExportData: React.FC<{ width: 150 } ]; + + console.log('export data: ', modelList); const handleSubmit = () => { const fileName = `usage-data_${query.start_date || ''}_${query.end_date || ''}.xlsx`; exportJsonToExcel({ @@ -111,8 +126,8 @@ const ExportData: React.FC<{ user_id: (value: string) => { return userList.find((item) => item.value === value)?.label || value; }, - model_id: (value: string) => { - return modelList.find((item) => item.value === value)?.label || value; + model_id: (value: string, record: any) => { + return getModelName(record); } } }); @@ -126,6 +141,7 @@ const ExportData: React.FC<{ start_date: dayjs().subtract(29, 'days').format('YYYY-MM-DD'), end_date: dayjs().format('YYYY-MM-DD'), model_ids: [], + provider_model_names: [], user_ids: [] }); setResult({ @@ -164,6 +180,8 @@ const ExportData: React.FC<{ query={query} userList={userList} modelList={modelList} + selectedModels={selectedModels} + cascaderWidth={360} handleDateChange={handleDateChange} handleUsersChange={handleUsersChange} handleModelsChange={handleModelsChange} @@ -171,7 +189,7 @@ const ExportData: React.FC<{ void; handleModelsChange: (value: any) => void; handleExport?: () => void; + selectedModels: string[][]; url: string; disabledDate?: boolean; + cascaderWidth?: number; } const FilterBar: React.FC = (props) => { @@ -45,6 +64,8 @@ const FilterBar: React.FC = (props) => { query, userList, modelList, + selectedModels, + cascaderWidth = 300, handleDateChange, handleUsersChange, handleModelsChange, @@ -52,7 +73,6 @@ const FilterBar: React.FC = (props) => { url, disabledDate } = props; - const { disabledRangeDaysDate, rangePresets } = useRangePickerPreset({ range: DefaultDateConfig.maxRange, disabledDate: disabledDate @@ -60,6 +80,50 @@ const FilterBar: React.FC = (props) => { const intl = useIntl(); + const displayRender = (labels: any[], option: any) => { + return ( + + {labels[0]} / {labels[1]} + + } + > + {labels[0]} / {labels[1]} + + ); + }; + + const optionRender = (option: any) => { + const { data } = option; + + if (!data.isParent) { + return {data.label}; + } + + if (data.providerType === 'deployments') { + return ( + + + + {intl.formatMessage({ id: 'menu.models.deployment' })} + + + ); + } + + return ( + + + + {data.label} + + + ); + }; + return (
@@ -89,19 +153,37 @@ const FilterBar: React.FC = (props) => { value={query.user_ids} onChange={handleUsersChange} > - + options={modelList} + value={selectedModels} + showCheckedStrategy="SHOW_CHILD" + displayRender={displayRender} + optionNode={optionRender} + getPopupContainer={(triggerNode) => triggerNode.parentNode} + > {url === DASHBOARD_STATS_API && ( diff --git a/src/pages/dashboard/components/usage-inner/index.tsx b/src/pages/dashboard/components/usage-inner/index.tsx index 2bf9b913..11a7862f 100644 --- a/src/pages/dashboard/components/usage-inner/index.tsx +++ b/src/pages/dashboard/components/usage-inner/index.tsx @@ -16,6 +16,7 @@ import useUsageData from './use-usage-data'; const TitleWrapper = styled.div` margin: 0; font-weight: 700; + min-width: max-content; `; const UsageInner: FC<{ maxWidth: number }> = ({ maxWidth }) => { @@ -27,6 +28,7 @@ const UsageInner: FC<{ maxWidth: number }> = ({ maxWidth }) => { query, userList, modelList, + selectedModels, handleOnCancel, init, handleExport, @@ -109,6 +111,7 @@ const UsageInner: FC<{ maxWidth: number }> = ({ maxWidth }) => { query={query} userList={userList} modelList={modelList} + selectedModels={selectedModels} disabledDate={true} handleDateChange={handleDateChange} handleUsersChange={handleUsersChange} diff --git a/src/pages/dashboard/components/usage-inner/use-usage-data.tsx b/src/pages/dashboard/components/usage-inner/use-usage-data.tsx index e3f0824f..a6d24f43 100644 --- a/src/pages/dashboard/components/usage-inner/use-usage-data.tsx +++ b/src/pages/dashboard/components/usage-inner/use-usage-data.tsx @@ -1,5 +1,6 @@ import { queryModelsList } from '@/pages/llmodels/apis'; import { ListItem as ModelListItem } from '@/pages/llmodels/config/types'; +import useTargetSourceModels from '@/pages/model-routes/hooks/use-target-source-models'; import { queryUsersList } from '@/pages/users/apis'; import dayjs from 'dayjs'; import _ from 'lodash'; @@ -93,6 +94,7 @@ export default function useUseageData(config: { start_date: string; end_date: string; model_ids: number[]; + provider_model_names: string[]; user_ids: number[]; }>({ start_date: dayjs() @@ -100,12 +102,15 @@ export default function useUseageData(config: { .format('YYYY-MM-DD'), end_date: dayjs().format('YYYY-MM-DD'), model_ids: [], - user_ids: [] + user_ids: [], + provider_model_names: [] }); - - const [modelList, setModelList] = useState[]>([]); + const { sourceModels: modelList, fetchSourceModels } = + useTargetSourceModels(); + const [models, setModelList] = useState[]>([]); const [userList, setUserList] = useState[]>([]); const [loading, setLoading] = useState(false); + const [selectedModels, setSelectedModels] = useState([]); const usageData = useMemo<{ requestTokenData: RequestTokenData; @@ -301,19 +306,36 @@ export default function useUseageData(config: { }); fetchUsageData({ ...query, user_ids: value }); }; - const handleModelsChange = (value: number[]) => { + + const generateModelsValue = (value: string[][]) => { + const modelIds = [] as number[]; + const providerModelNames = [] as string[]; + value.forEach((item: Array) => { + if (item[0] === 'deployments') { + modelIds.push(item[1] as number); + } else { + providerModelNames.push(`${item[0]}:${item[1]}`); + } + }); + return { + model_ids: modelIds, + provider_model_names: providerModelNames + }; + }; + const handleModelsChange = (value: string[][]) => { + setSelectedModels(value); setQuery((pre) => { return { ...pre, - model_ids: value + ...generateModelsValue(value) }; }); - fetchUsageData({ ...query, model_ids: value }); + fetchUsageData({ ...query, ...generateModelsValue(value) }); }; const init = () => { fetchUsageData(query); - fetchModelsList(); + fetchSourceModels(); fetchUsersList(); }; @@ -325,6 +347,7 @@ export default function useUseageData(config: { userList, modelList, query, + selectedModels, setQuery, init, setResult, diff --git a/src/pages/model-routes/components/route-targets.tsx b/src/pages/model-routes/components/route-targets.tsx index 8944fa5c..90ca8ca5 100644 --- a/src/pages/model-routes/components/route-targets.tsx +++ b/src/pages/model-routes/components/route-targets.tsx @@ -90,26 +90,24 @@ const RouteItem: React.FC = ({
- {data.weight > 0 && ( + {data.fallback_status_codes && + data.fallback_status_codes?.length > 0 ? ( + <> + {data.weight > 0 && ( + / + )} + + {intl.formatMessage({ + id: 'routes.table.label.fallback' + })} + + + ) : ( {intl.formatMessage({ id: 'routes.form.target.weight' })}:{' '} - {data.weight} + {data.weight || 0} )} - - {data.fallback_status_codes && - data.fallback_status_codes?.length > 0 && ( - <> - {data.weight > 0 && ( - / - )} - - {intl.formatMessage({ - id: 'routes.table.label.fallback' - })} - - - )} diff --git a/src/pages/model-routes/forms/targets.tsx b/src/pages/model-routes/forms/targets.tsx index ab430233..0aef7e6c 100644 --- a/src/pages/model-routes/forms/targets.tsx +++ b/src/pages/model-routes/forms/targets.tsx @@ -257,13 +257,6 @@ const TargetsForm = forwardRef((props, ref) => { { validator(rule, value) { if (value && value?.length > 0) { - // if (_.some(value, (item: any) => !item.weight)) { - // setValidTriggered(true); - // return Promise.reject( - // getRuleMessage('input', 'routes.form.target.weight') - // ); - // } - if ( _.some( dataList,