From 83403a1a1af9fd87f4e8dcbea88fabf1070a28fd Mon Sep 17 00:00:00 2001 From: jialin Date: Tue, 14 Oct 2025 15:20:51 +0800 Subject: [PATCH] feat: add extended_kv_cache --- src/components/seal-form/seal-cascader.tsx | 16 ++- src/components/seal-form/wrapper/index.tsx | 1 + src/components/seal-form/wrapper/slider.ts | 3 + src/locales/en-US/models.ts | 7 +- src/locales/ja-JP/models.ts | 15 ++- src/locales/ru-RU/models.ts | 14 ++- src/locales/zh-CN/models.ts | 7 +- src/pages/llmodels/config/types.ts | 6 + src/pages/llmodels/forms/advance-config.tsx | 6 +- src/pages/llmodels/forms/index.tsx | 28 +++++ src/pages/llmodels/forms/kv-cache.tsx | 60 +++++++++ src/pages/llmodels/forms/performance.tsx | 132 +------------------- src/pages/llmodels/forms/schedule-type.tsx | 124 ++++++++++++++++++ 13 files changed, 277 insertions(+), 142 deletions(-) create mode 100644 src/pages/llmodels/forms/kv-cache.tsx create mode 100644 src/pages/llmodels/forms/schedule-type.tsx diff --git a/src/components/seal-form/seal-cascader.tsx b/src/components/seal-form/seal-cascader.tsx index 3578a86d..90c7cde3 100644 --- a/src/components/seal-form/seal-cascader.tsx +++ b/src/components/seal-form/seal-cascader.tsx @@ -1,3 +1,4 @@ +import IconFont from '@/components/icon-font'; import { isNotEmptyValue } from '@/utils/index'; import { useIntl } from '@umijs/max'; import type { CascaderAutoProps } from 'antd'; @@ -33,10 +34,11 @@ const renderTag = (props: any) => { const OptionNodes = (props: { data: any; + notFoundContent?: React.ReactNode; optionNode: React.FC<{ data: any }>; }) => { const intl = useIntl(); - const { data, optionNode: OptionNode } = props; + const { data, optionNode: OptionNode, notFoundContent } = props; if (data.value === '__EMPTY__') { return ( ); } @@ -85,6 +90,7 @@ const SealCascader: React.FC< allowNull, isInFormItems = true, optionNode, + notFoundContent, tagRender, ...rest } = props; @@ -168,11 +174,13 @@ const SealCascader: React.FC< > } optionRender={ optionNode ? (data) => ( ) diff --git a/src/components/seal-form/wrapper/index.tsx b/src/components/seal-form/wrapper/index.tsx index 2d3fe728..47b478b4 100644 --- a/src/components/seal-form/wrapper/index.tsx +++ b/src/components/seal-form/wrapper/index.tsx @@ -158,6 +158,7 @@ const Inner = styled.div.attrs({ className: '__inner__' })` width: 100%; + display: flex; `; const Extra = styled.div` diff --git a/src/components/seal-form/wrapper/slider.ts b/src/components/seal-form/wrapper/slider.ts index 23c2a53a..1a3a8a8e 100644 --- a/src/components/seal-form/wrapper/slider.ts +++ b/src/components/seal-form/wrapper/slider.ts @@ -10,6 +10,9 @@ const SliderWrapper = styled.div` .borderless { background-color: transparent; } + .ant-slider { + flex: 1; + } padding-block: 0; padding-inline: 2px; input.ant-input-number-input { diff --git a/src/locales/en-US/models.ts b/src/locales/en-US/models.ts index b1747839..164c51ea 100644 --- a/src/locales/en-US/models.ts +++ b/src/locales/en-US/models.ts @@ -182,5 +182,10 @@ export default { 'models.table.users.all': 'All Users', 'models.table.users.selected': 'Selected Users', 'models.table.nouserFound': 'No users found', - 'models.form.performance': 'Performance' + 'models.form.performance': 'Performance', + 'models.form.gpus.notfound': 'No GPUs found', + 'models.form.extendedkvcache': 'Enable Extended KV Cache', + 'models.form.chunkSize': 'Size Of Cache Chunks', + 'models.form.maxCPUSize': 'Maximum CPU Cache Size (GiB)', + 'models.form.remoteURL': 'Remote Storage URL' }; diff --git a/src/locales/ja-JP/models.ts b/src/locales/ja-JP/models.ts index 0f9d888f..2e757e05 100644 --- a/src/locales/ja-JP/models.ts +++ b/src/locales/ja-JP/models.ts @@ -179,7 +179,12 @@ export default { 'models.table.users.all': 'All Users', 'models.table.users.selected': 'Selected Users', 'models.table.nouserFound': 'No users found', - 'models.form.performance': 'Performance' + 'models.form.performance': 'Performance', + 'models.form.gpus.notfound': 'No GPUs found', + 'models.form.extendedkvcache': 'Enable Extended KV Cache', + 'models.form.chunkSize': 'Size Of Cache Chunks', + 'models.form.maxCPUSize': 'Maximum CPU Cache Size (GiB)', + 'models.form.remoteURL': 'Remote Storage URL' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== @@ -202,5 +207,11 @@ export default { // 17. 'models.table.users.all': 'All Users', // 18. 'models.table.users.selected': 'Selected Users', // 19. 'models.table.nouserFound': 'No users found', -// 20. 'models.form.performance': 'Performance' +// 20. 'models.form.performance': 'Performance', +// 21. 'models.form.gpus.notfound': 'No GPUs found', +// 22. 'models.form.extendedkvcache': 'Enable Extended KV Cache', +// 23. 'models.form.chunkSize': 'Size Of Cache Chunks', +// 24. 'models.form.maxCPUSize': 'Maximum CPU Cache Size (GiB)', +// 25. 'models.form.remoteURL': 'Remote Storage URL' + // ========== End of To-Do List ========== diff --git a/src/locales/ru-RU/models.ts b/src/locales/ru-RU/models.ts index 166a8706..98b241e4 100644 --- a/src/locales/ru-RU/models.ts +++ b/src/locales/ru-RU/models.ts @@ -182,7 +182,12 @@ export default { 'models.table.users.all': 'All users', 'models.table.users.selected': 'Selected users', 'models.table.nouserFound': 'No users found', - 'models.form.performance': 'Performance' + 'models.form.performance': 'Performance', + 'models.form.gpus.notfound': 'No GPUs found', + 'models.form.extendedkvcache': 'Enable Extended KV Cache', + 'models.form.chunkSize': 'Size Of Cache Chunks', + 'models.form.maxCPUSize': 'Maximum CPU Cache Size (GiB)', + 'models.form.remoteURL': 'Remote Storage URL' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== @@ -197,5 +202,10 @@ export default { // 9. 'models.table.users.all': 'All users', // 10. 'models.table.users.selected': 'Selected users', // 11. 'models.table.nouserFound': 'No users found', -// 12. 'models.form.performance': 'Performance' +// 12. 'models.form.performance': 'Performance', +// 13. 'models.form.gpus.notfound': 'No GPUs found', +// 14. 'models.form.extendedkvcache': 'Enable Extended KV Cache', +// 15. 'models.form.chunkSize': 'Size Of Cache Chunks', +// 16. 'models.form.maxCPUSize': 'Maximum CPU Cache Size (GiB)', +// 17. 'models.form.remoteURL': 'Remote Storage URL' // ========== End of To-Do List ========== diff --git a/src/locales/zh-CN/models.ts b/src/locales/zh-CN/models.ts index 6c44bab8..676c972f 100644 --- a/src/locales/zh-CN/models.ts +++ b/src/locales/zh-CN/models.ts @@ -172,5 +172,10 @@ export default { 'models.table.users.all': '所有用户', 'models.table.users.selected': '已选用户', 'models.table.nouserFound': '未找到用户', - 'models.form.performance': '性能' + 'models.form.performance': '性能', + 'models.form.gpus.notfound': '未找到 GPU', + 'models.form.extendedkvcache': '启用扩展 KV 缓存', + 'models.form.chunkSize': '缓存分块大小', + 'models.form.maxCPUSize': 'CPU 缓存最大占用 (GiB)', + 'models.form.remoteURL': '远程存储地址' }; diff --git a/src/pages/llmodels/config/types.ts b/src/pages/llmodels/config/types.ts index f3072240..88f35b53 100644 --- a/src/pages/llmodels/config/types.ts +++ b/src/pages/llmodels/config/types.ts @@ -76,6 +76,12 @@ export interface FormData { optimize_long_prompt: boolean; enable_speculative_decoding: boolean; cluster_id: number; + extended_kv_cache: { + enabled: boolean; + chunk_size: number; + max_local_cpu_size: number; + remote_url: string; + }; } interface ComputedResourceClaim { diff --git a/src/pages/llmodels/forms/advance-config.tsx b/src/pages/llmodels/forms/advance-config.tsx index f5082b3d..099aa730 100644 --- a/src/pages/llmodels/forms/advance-config.tsx +++ b/src/pages/llmodels/forms/advance-config.tsx @@ -16,6 +16,7 @@ import { useFormContext } from '../config/form-context'; import { FormData } from '../config/types'; import Backend from '../forms/backend'; import BackendParametersList from '../forms/backend-parameters-list'; +import ScheduleTypeForm from '../forms/schedule-type'; const placementStrategyTips = [ { @@ -88,6 +89,7 @@ const AdvanceConfig = () => { options={modelCategories} > + {scheduleType === ScheduleValueMap.Auto && ( <> @@ -169,7 +171,7 @@ const AdvanceConfig = () => { [backendOptionsMap.vllm, backendOptionsMap.ascendMindie].includes( backend ) && ( -
+
name="distributed_inference_across_workers" valuePropName="checked" @@ -187,7 +189,7 @@ const AdvanceConfig = () => {
)} -
+
name="restart_on_error" valuePropName="checked" diff --git a/src/pages/llmodels/forms/index.tsx b/src/pages/llmodels/forms/index.tsx index cf2c5bf6..23a045f5 100644 --- a/src/pages/llmodels/forms/index.tsx +++ b/src/pages/llmodels/forms/index.tsx @@ -191,6 +191,12 @@ const DataForm: React.FC = forwardRef((props, ref) => { categories: null, restart_on_error: true, distributed_inference_across_workers: true, + extended_kv_cache: { + enabled: false, + chunk_size: 256, + max_local_cpu_size: 10, + remote_url: '' + }, ...initialValues }} > @@ -254,6 +260,28 @@ const DataForm: React.FC = forwardRef((props, ref) => { > } + + name="replicas" + rules={[ + { + required: true, + message: getRuleMessage('input', 'models.form.replicas') + } + ]} + > + + name="description"> { + const intl = useIntl(); + const form = Form.useFormInstance(); + const kvCacheEnabled = Form.useWatch(['extended_kv_cache', 'enabled'], form); + + return ( + <> +
+ + name={['extended_kv_cache', 'enabled']} + valuePropName="checked" + style={{ padding: '0 10px', marginBottom: 0 }} + noStyle + > + + +
+ {kvCacheEnabled && ( + <> + + name={['extended_kv_cache', 'max_local_cpu_size']} + > + + + name={['extended_kv_cache', 'chunk_size']}> + + + name={['extended_kv_cache', 'remote_url']}> + + + + )} + + ); +}; + +export default KVCacheForm; diff --git a/src/pages/llmodels/forms/performance.tsx b/src/pages/llmodels/forms/performance.tsx index 950b128a..511f6938 100644 --- a/src/pages/llmodels/forms/performance.tsx +++ b/src/pages/llmodels/forms/performance.tsx @@ -1,143 +1,15 @@ -import SealCascader from '@/components/seal-form/seal-cascader'; -import SealInput from '@/components/seal-form/seal-input'; -import SealSelect from '@/components/seal-form/seal-select'; -import TooltipList from '@/components/tooltip-list'; -import useAppUtils from '@/hooks/use-app-utils'; import { useIntl } from '@umijs/max'; import { Form } from 'antd'; import React from 'react'; -import GPUCard from '../components/gpu-card'; -import { scheduleList, ScheduleValueMap } from '../config'; -import { backendOptionsMap } from '../config/backend-parameters'; -import { useCatalogFormContext, useFormContext } from '../config/form-context'; -import { FormData } from '../config/types'; - -const scheduleTypeTips = [ - { - title: { - text: 'models.form.scheduletype.auto', - locale: true - }, - tips: 'models.form.scheduletype.auto.tips' - }, - { - title: { - text: 'models.form.scheduletype.manual', - locale: true - }, - tips: 'models.form.scheduletype.manual.tips' - } -]; +import KVCacheForm from './kv-cache'; const Performance: React.FC = () => { const intl = useIntl(); - const { onValuesChange, gpuOptions } = useFormContext(); - const { onQuantizationChange } = useCatalogFormContext(); - const { getRuleMessage } = useAppUtils(); const form = Form.useFormInstance(); - const scheduleType = Form.useWatch('scheduleType', form); - - const handleScheduleTypeChange = (value: string) => { - if (value === ScheduleValueMap.Auto) { - onValuesChange?.({}, form.getFieldsValue()); - } - }; - - const handleOnQuantizationChange = (val: any) => { - onQuantizationChange?.(val); - }; - - const handleBeforeGpuSelectorChange = (gpuIds: any[]) => {}; - - const handleGpuSelectorChange = (value: any[]) => { - handleBeforeGpuSelectorChange(value); - onValuesChange?.({}, form.getFieldsValue()); - }; return ( <> - - name="replicas" - rules={[ - { - required: true, - message: getRuleMessage('input', 'models.form.replicas') - } - ]} - > - - - - } - options={scheduleList} - > - - {scheduleType === ScheduleValueMap.SpecificGPUType && ( - <> - - - - - - - - )} - {scheduleType === ScheduleValueMap.Manual && - !form.getFieldValue('fix_gpu_type') && ( - <> - - triggerNode.parentNode} - onChange={handleGpuSelectorChange} - > - - - )} + {/*
name="optimize_long_prompt" diff --git a/src/pages/llmodels/forms/schedule-type.tsx b/src/pages/llmodels/forms/schedule-type.tsx new file mode 100644 index 00000000..9d36238b --- /dev/null +++ b/src/pages/llmodels/forms/schedule-type.tsx @@ -0,0 +1,124 @@ +import SealCascader from '@/components/seal-form/seal-cascader'; +import SealSelect from '@/components/seal-form/seal-select'; +import TooltipList from '@/components/tooltip-list'; +import useAppUtils from '@/hooks/use-app-utils'; +import { useIntl } from '@umijs/max'; +import { Form } from 'antd'; +import React from 'react'; +import GPUCard from '../components/gpu-card'; +import { scheduleList, ScheduleValueMap } from '../config'; +import { backendOptionsMap } from '../config/backend-parameters'; +import { useCatalogFormContext, useFormContext } from '../config/form-context'; + +const scheduleTypeTips = [ + { + title: { + text: 'models.form.scheduletype.auto', + locale: true + }, + tips: 'models.form.scheduletype.auto.tips' + }, + { + title: { + text: 'models.form.scheduletype.manual', + locale: true + }, + tips: 'models.form.scheduletype.manual.tips' + } +]; + +const ScheduleTypeForm: React.FC = () => { + const intl = useIntl(); + const { onValuesChange, gpuOptions } = useFormContext(); + const { onQuantizationChange } = useCatalogFormContext(); + const { getRuleMessage } = useAppUtils(); + const form = Form.useFormInstance(); + const scheduleType = Form.useWatch('scheduleType', form); + + const handleScheduleTypeChange = (value: string) => { + if (value === ScheduleValueMap.Auto) { + onValuesChange?.({}, form.getFieldsValue()); + } + }; + + const handleOnQuantizationChange = (val: any) => { + onQuantizationChange?.(val); + }; + + const handleBeforeGpuSelectorChange = (gpuIds: any[]) => {}; + + const handleGpuSelectorChange = (value: any[]) => { + handleBeforeGpuSelectorChange(value); + onValuesChange?.({}, form.getFieldsValue()); + }; + + return ( + <> + + } + options={scheduleList} + > + + {scheduleType === ScheduleValueMap.SpecificGPUType && ( + <> + + + + + + + + )} + {scheduleType === ScheduleValueMap.Manual && + !form.getFieldValue('fix_gpu_type') && ( + <> + + triggerNode.parentNode} + notFoundContent={intl.formatMessage({ + id: 'models.form.gpus.notfound' + })} + onChange={handleGpuSelectorChange} + > + + + )} + + ); +}; + +export default ScheduleTypeForm;