feat: add context length

This commit is contained in:
jialin
2026-01-06 17:21:32 +08:00
parent d8b3331648
commit 0f7a475ec9
23 changed files with 164 additions and 40 deletions
+19 -2
View File
@@ -8,6 +8,8 @@ import { useIntl } from '@umijs/max';
import { Button, Tag } from 'antd';
import _ from 'lodash';
import { useMemo } from 'react';
import semverCoerce from 'semver/functions/coerce';
import semverGt from 'semver/functions/gt';
import styled from 'styled-components';
import {
backendActions,
@@ -140,7 +142,7 @@ const BackendCard: React.FC<BackendCardProps> = ({ data, onSelect }) => {
const icon = customIcons[data.id % customIcons.length];
return (
<TagInner color={color} bordered={false}>
<TagInner color={color} variant="filled">
{icon}
</TagInner>
);
@@ -157,13 +159,28 @@ const BackendCard: React.FC<BackendCardProps> = ({ data, onSelect }) => {
e.stopPropagation();
};
const sortVersions = (v2: string, v1: string) => {
const sv1 = semverCoerce(v1);
const sv2 = semverCoerce(v2);
if (!sv1 && !sv2) return 0;
if (!sv1) return 1;
if (!sv2) return -1;
if (semverGt(sv1, sv2)) return -1;
return 1;
};
const renderTag = (item: any) => {
return (
<AutoTooltip
ghost
minWidth={20}
showTitle
title={_.join(data.framework_index_map?.[item], ', ') || false}
title={
_.join(data.framework_index_map?.[item].sort(sortVersions), ', ') ||
false
}
>
<ThemeTag
key={item}
+17
View File
@@ -454,3 +454,20 @@ export async function queryDraftModelList(params?: Global.SearchParams) {
params
});
}
export async function queryModelContextLength(params: {
model: {
source: string;
model_scope_model_id?: string;
huggingface_repo_id?: string;
local_path?: string;
};
}) {
return request<{ native: number; scaled: number }>(
`${MODELS_API}/context-length`,
{
method: 'POST',
data: params
}
);
}
@@ -575,7 +575,6 @@ const AddModal: FC<AddModalProps> = (props) => {
modelSource={props.source}
setIsGGUF={handleSetIsGGUF}
></ModelCard>
{isGGUF && (
<HFModelFile
ref={modelFileRef}
@@ -590,7 +589,6 @@ const AddModal: FC<AddModalProps> = (props) => {
</ColWrapper>
</>
)}
<FormWrapper>
<ColumnWrapper
styles={{
@@ -82,7 +82,7 @@ const IncompatiableInfo: React.FC<IncompatiableInfoProps> = (props) => {
if (isEvaluating) {
return (
<CompatibleTag color="blue" bordered={false}>
<CompatibleTag color="blue" variant="filled">
<Tooltip title={intl.formatMessage({ id: 'models.form.evaluating' })}>
<LoadingOutlined />
</Tooltip>
@@ -695,7 +695,7 @@ const InstanceItem: React.FC<InstanceItemProps> = ({
</Col>
<Col span={4}>
<span
style={{ paddingLeft: '50px', gap: 4 }}
style={{ paddingLeft: '40px', gap: 4 }}
className="flex-center"
>
<InstanceStatusTag
@@ -22,6 +22,7 @@ interface FormContextProps {
workerLabelOptions: CascaderOption[];
backendOptions: BackendOption[];
initialValues?: FormData; // for editing model
modelContextData?: Record<string, any>;
clearCacheFormValues?: () => void;
onValuesChange?: (changedValues: any, allValues: any) => void;
onBackendChange: (backend: string, option: any) => void;
+2 -1
View File
@@ -358,7 +358,8 @@ export const DO_NOT_TRIGGER_CHECK_COMPATIBILITY = [
'extended_kv_cache.enabled',
'extended_kv_cache.ram_size',
'speculative_config.enabled',
'speculative_config.draft_model'
'speculative_config.draft_model',
'max_context_len'
];
// ignore to compare old and new data when these fields change in updating model
+22 -5
View File
@@ -17,7 +17,8 @@ const AdvanceConfig = () => {
const form = Form.useFormInstance();
const EnviromentVars = Form.useWatch('env', form);
const backend = Form.useWatch('backend', form);
const { onValuesChange, backendOptions, formKey } = useFormContext();
const { onValuesChange, backendOptions, isGGUF, modelContextData } =
useFormContext();
const currentBackendOptions = useMemo(() => {
return backendOptions?.find((item) => item.value === backend);
@@ -46,6 +47,10 @@ const AdvanceConfig = () => {
onValuesChange?.({}, form.getFieldsValue());
};
const handleContextLengthChange = _.debounce((value: number) => {
onValuesChange?.({}, form.getFieldsValue());
}, 300);
return (
<>
<Form.Item<FormData>
@@ -63,10 +68,22 @@ const AdvanceConfig = () => {
options={modelCategories}
></SealSelect>
</Form.Item>
<Form.Item<FormData> name="max_context_len">
<SealSlider label="Max Context Length" inputnumber></SealSlider>
</Form.Item>
{!isGGUF && modelContextData?.native && (
<Form.Item<FormData> name="max_context_len">
<SealSlider
label={intl.formatMessage({ id: 'models.form.maxContextLength' })}
inputnumber={true}
onChange={handleContextLengthChange}
included={true}
max={modelContextData?.scaled || 163840}
tooltip={{}}
marks={{
[modelContextData?.native]: 'native',
[modelContextData?.scaled]: 'scaled'
}}
></SealSlider>
</Form.Item>
)}
<BackendParametersList></BackendParametersList>
<Form.Item<FormData> name="env">
<LabelSelector
+29 -1
View File
@@ -8,7 +8,12 @@ import { useIntl } from '@umijs/max';
import useMemoizedFn from 'ahooks/lib/useMemoizedFn';
import { Form } from 'antd';
import _ from 'lodash';
import React, { forwardRef, useImperativeHandle, useMemo } from 'react';
import React, {
forwardRef,
useEffect,
useImperativeHandle,
useMemo
} from 'react';
import styled from 'styled-components';
import {
DeployFormKeyMap,
@@ -29,6 +34,7 @@ import { generateGPUIds } from '../config/utils';
import useFieldScroll from '../hooks/use-field-scroll';
import { useGenerateGPUOptions } from '../hooks/use-form-initial-values';
import useQueryBackends from '../hooks/use-query-backends';
import { useQueryContextLength } from '../services/use-query-context-length';
import AdvanceConfig from './advance-config';
import BasicForm from './basic';
import Performance from './performance';
@@ -103,6 +109,10 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
const intl = useIntl();
const [activeKey, setActiveKey] = React.useState<string[]>([]);
const [target, setTarget] = React.useState<string>(TABKeysMap.BASIC);
const { modelContextData, fetchContextLength } = useQueryContextLength();
const localPath = Form.useWatch('local_path', form);
const modelScopeModelId = Form.useWatch('model_scope_model_id', form);
const huggingfaceRepoId = Form.useWatch('huggingface_repo_id', form);
const segmentOptions = [
{
@@ -390,6 +400,23 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
};
});
useEffect(() => {
if (isGGUF || (!localPath && !modelScopeModelId && !huggingfaceRepoId)) {
return;
}
let params = {};
if (source === modelSourceMap.local_path_value) {
params = { local_path: localPath };
} else if (source === modelSourceMap.modelscope_value) {
params = { model_scope_model_id: modelScopeModelId };
} else if (source === modelSourceMap.huggingface_value) {
params = { huggingface_repo_id: huggingfaceRepoId };
}
// TODO
// fetchContextLength({ ...params, source });
}, [isGGUF, source, localPath, modelScopeModelId, huggingfaceRepoId]);
return (
<FormContext.Provider
value={{
@@ -401,6 +428,7 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
backendOptions: backendOptions,
workerLabelOptions: workerLabelOptions,
initialValues: initialValues,
modelContextData: modelContextData,
clearCacheFormValues: clearCacheFormValues,
onValuesChange: onValuesChange,
onBackendChange: handleBackendChange
@@ -115,7 +115,7 @@ const useModelsColumns = ({
title: intl.formatMessage({ id: 'models.table.replicas.edit' })
},
render: (text: number, record: ListItem) => (
<span style={{ paddingLeft: 10, minWidth: '33px' }}>
<span style={{ minWidth: '23px' }}>
{record.ready_replicas} / {record.replicas}
</span>
)
@@ -0,0 +1,29 @@
import { useState } from 'react';
import { queryModelContextLength } from '../apis';
export const useQueryContextLength = () => {
const [modelContextData, setModelContextData] = useState<{
native: number;
scaled: number;
}>({} as any);
const fetchContextLength = async (params: {
source: string;
model_scope_model_id?: string;
huggingface_repo_id?: string;
local_path?: string;
}) => {
try {
const res = await queryModelContextLength({
model: params
});
setModelContextData(res);
} catch (error) {
setModelContextData({} as any);
}
};
return {
modelContextData,
fetchContextLength
};
};