feat: add speculative decoding
This commit is contained in:
@@ -169,6 +169,10 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gap-4 {
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.gap-20 {
|
.gap-20 {
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,5 +218,11 @@ export default {
|
|||||||
'models.form.ramRatio.tips':
|
'models.form.ramRatio.tips':
|
||||||
'Ratio of system RAM to GPU VRAM used for KV cache. For example, 2.0 means the cache in RAM can be twice as large as the GPU VRAM.',
|
'Ratio of system RAM to GPU VRAM used for KV cache. For example, 2.0 means the cache in RAM can be twice as large as the GPU VRAM.',
|
||||||
'models.form.ramSize.tips': `Maximum size of the KV cache stored in system memory (GiB). If set, this value overrides "{content}".`,
|
'models.form.ramSize.tips': `Maximum size of the KV cache stored in system memory (GiB). If set, this value overrides "{content}".`,
|
||||||
'models.form.chunkSize.tips': 'Number of tokens per KV cache chunk.'
|
'models.form.chunkSize.tips': 'Number of tokens per KV cache chunk.',
|
||||||
|
'models.form.flavor.throughput.tips':
|
||||||
|
'optimized for high throughput under high request concurrency.',
|
||||||
|
'models.form.flavor.latency.tips':
|
||||||
|
'optimized for low latency under low request concurrency.',
|
||||||
|
'models.form.flavor.reference.tips':
|
||||||
|
'the most compatible option with full precision.'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -218,7 +218,13 @@ export default {
|
|||||||
'models.form.ramRatio.tips':
|
'models.form.ramRatio.tips':
|
||||||
'Ratio of system RAM to GPU VRAM used for KV cache. For example, 2.0 means the cache in RAM can be twice as large as the GPU VRAM.',
|
'Ratio of system RAM to GPU VRAM used for KV cache. For example, 2.0 means the cache in RAM can be twice as large as the GPU VRAM.',
|
||||||
'models.form.ramSize.tips': `Maximum size of the KV cache stored in system memory (GiB). If set, this value overrides "{content}".`,
|
'models.form.ramSize.tips': `Maximum size of the KV cache stored in system memory (GiB). If set, this value overrides "{content}".`,
|
||||||
'models.form.chunkSize.tips': 'Number of tokens per KV cache chunk.'
|
'models.form.chunkSize.tips': 'Number of tokens per KV cache chunk.',
|
||||||
|
'models.form.flavor.throughput.tips':
|
||||||
|
'optimized for high throughput under high request concurrency.',
|
||||||
|
'models.form.flavor.latency.tips':
|
||||||
|
'optimized for low latency under low request concurrency.',
|
||||||
|
'models.form.flavor.reference.tips':
|
||||||
|
'the most compatible option with full precision.'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
BackendItem,
|
BackendItem,
|
||||||
CatalogItem,
|
CatalogItem,
|
||||||
CatalogSpec,
|
CatalogSpec,
|
||||||
|
DraftModelItem,
|
||||||
EvaluateResult,
|
EvaluateResult,
|
||||||
EvaluateSpec,
|
EvaluateSpec,
|
||||||
FormData,
|
FormData,
|
||||||
@@ -27,6 +28,8 @@ export const BACKEND_LIST_API = '/inference-backends/list';
|
|||||||
|
|
||||||
export const MY_MODELS_API = '/my-models';
|
export const MY_MODELS_API = '/my-models';
|
||||||
|
|
||||||
|
export const DRAFT_MODELS_API = '/draft-models';
|
||||||
|
|
||||||
const setProxyUrl = (url: string) => {
|
const setProxyUrl = (url: string) => {
|
||||||
return `/proxy?url=${encodeURIComponent(url)}`;
|
return `/proxy?url=${encodeURIComponent(url)}`;
|
||||||
};
|
};
|
||||||
@@ -443,3 +446,10 @@ export async function queryMyModelDetail(id: number) {
|
|||||||
method: 'GET'
|
method: 'GET'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function queryDraftModelList(params?: Global.SearchParams) {
|
||||||
|
return request<{ items: DraftModelItem[] }>(DRAFT_MODELS_API, {
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { SearchOutlined, SyncOutlined } from '@ant-design/icons';
|
|||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
import { useIntl, useNavigate } from '@umijs/max';
|
import { useIntl, useNavigate } from '@umijs/max';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
import { Button, Input, Pagination, Space, message } from 'antd';
|
import { Button, Input, Space, message } from 'antd';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
@@ -159,17 +159,6 @@ const Catalog: React.FC = () => {
|
|||||||
[openDeployModal]
|
[openDeployModal]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOnPageChange = useCallback(
|
|
||||||
(page: number, pageSize?: number) => {
|
|
||||||
setQueryParams({
|
|
||||||
...queryParams,
|
|
||||||
page,
|
|
||||||
perPage: pageSize || 10
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[queryParams]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSearch = (e: any) => {
|
const handleSearch = (e: any) => {
|
||||||
fetchData({
|
fetchData({
|
||||||
...queryParams,
|
...queryParams,
|
||||||
@@ -312,17 +301,6 @@ const Catalog: React.FC = () => {
|
|||||||
subTitle={intl.formatMessage({ id: 'noresult.catalog.subTitle' })}
|
subTitle={intl.formatMessage({ id: 'noresult.catalog.subTitle' })}
|
||||||
></NoResult>
|
></NoResult>
|
||||||
</ScrollerContext.Provider>
|
</ScrollerContext.Provider>
|
||||||
<PageWrapper>
|
|
||||||
<Pagination
|
|
||||||
hideOnSinglePage={queryParams.perPage === 100}
|
|
||||||
align="end"
|
|
||||||
defaultCurrent={1}
|
|
||||||
total={dataSource.total}
|
|
||||||
pageSize={queryParams.perPage}
|
|
||||||
showSizeChanger
|
|
||||||
onChange={handleOnPageChange}
|
|
||||||
/>
|
|
||||||
</PageWrapper>
|
|
||||||
<DelopyBuiltInModal
|
<DelopyBuiltInModal
|
||||||
open={openDeployModal.show}
|
open={openDeployModal.show}
|
||||||
action={PageAction.CREATE}
|
action={PageAction.CREATE}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import fallbackImg from '@/assets/images/img.png';
|
import fallbackImg from '@/assets/images/img.png';
|
||||||
import AutoTooltip from '@/components/auto-tooltip';
|
import AutoTooltip from '@/components/auto-tooltip';
|
||||||
import IconFont from '@/components/icon-font';
|
import IconFont from '@/components/icon-font';
|
||||||
import TagWrapper from '@/components/tags-wrapper';
|
|
||||||
import ThemeTag from '@/components/tags-wrapper/theme-tag';
|
import ThemeTag from '@/components/tags-wrapper/theme-tag';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import { modelCategories } from '../config';
|
import { modelCategories } from '../config';
|
||||||
import { CatalogItem as CatalogItemType } from '../config/types';
|
import { CatalogItem as CatalogItemType } from '../config/types';
|
||||||
import '../style/catalog-item.less';
|
import '../style/catalog-item.less';
|
||||||
|
import { categoryConfig } from './model-tag';
|
||||||
|
|
||||||
const COLORS = ['blue', 'purple', 'orange'];
|
const COLORS = ['blue', 'purple', 'orange'];
|
||||||
interface CatalogItemProps {
|
interface CatalogItemProps {
|
||||||
@@ -47,11 +47,38 @@ const CatalogItem: React.FC<CatalogItemProps> = (props) => {
|
|||||||
height: 22
|
height: 22
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{sItem}B
|
{sItem.label}
|
||||||
</ThemeTag>
|
</ThemeTag>
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const description = useMemo(() => {
|
||||||
|
return (
|
||||||
|
<Typography.Paragraph
|
||||||
|
className="desc"
|
||||||
|
ellipsis={{
|
||||||
|
rows: 2,
|
||||||
|
tooltip: (
|
||||||
|
<div
|
||||||
|
className="custome-scrollbar"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
maxHeight: 300,
|
||||||
|
maxWidth: 300,
|
||||||
|
overflow: 'auto'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{data.description}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{data.description}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
);
|
||||||
|
}, [data.description]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={handleOnClick}
|
onClick={handleOnClick}
|
||||||
@@ -66,32 +93,8 @@ const CatalogItem: React.FC<CatalogItemProps> = (props) => {
|
|||||||
onError={handleOnError}
|
onError={handleOnError}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<AutoTooltip ghost style={{ flex: 1 }}>
|
<AutoTooltip ghost>{data.name}</AutoTooltip>
|
||||||
{data.name}
|
|
||||||
</AutoTooltip>
|
|
||||||
</div>
|
</div>
|
||||||
<Typography.Paragraph
|
|
||||||
className="desc"
|
|
||||||
ellipsis={{
|
|
||||||
rows: 2,
|
|
||||||
tooltip: (
|
|
||||||
<div
|
|
||||||
className="custome-scrollbar"
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
maxHeight: 300,
|
|
||||||
maxWidth: 300,
|
|
||||||
overflow: 'auto'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{data.description}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{data.description}
|
|
||||||
</Typography.Paragraph>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="item-footer">
|
<div className="item-footer">
|
||||||
<div className="update-time">
|
<div className="update-time">
|
||||||
@@ -121,9 +124,10 @@ const CatalogItem: React.FC<CatalogItemProps> = (props) => {
|
|||||||
{data.categories.map((sItem, i) => {
|
{data.categories.map((sItem, i) => {
|
||||||
return (
|
return (
|
||||||
<ThemeTag
|
<ThemeTag
|
||||||
|
icon={categoryConfig[sItem]?.icon}
|
||||||
key={sItem}
|
key={sItem}
|
||||||
className="tag-item"
|
className="tag-item"
|
||||||
color="blue"
|
color={categoryConfig[sItem]?.color || 'blue'}
|
||||||
opacity={0.7}
|
opacity={0.7}
|
||||||
>
|
>
|
||||||
{_.find(modelCategories, { value: sItem })?.label || sItem}
|
{_.find(modelCategories, { value: sItem })?.label || sItem}
|
||||||
@@ -147,22 +151,16 @@ const CatalogItem: React.FC<CatalogItemProps> = (props) => {
|
|||||||
</ThemeTag>
|
</ThemeTag>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{data.sizes?.length > 0 && (
|
<span className="dot"></span>
|
||||||
<>
|
<ThemeTag>
|
||||||
<span className="dot"></span>
|
{data.activated_size
|
||||||
<div className="box">
|
? `${data.size}B-A${data.activated_size}B`
|
||||||
<TagWrapper
|
: `${data.size}B`}
|
||||||
gap={8}
|
</ThemeTag>
|
||||||
dataList={data.sizes}
|
|
||||||
renderTag={renderTag}
|
|
||||||
></TagWrapper>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(CatalogItem);
|
export default CatalogItem;
|
||||||
|
|||||||
@@ -540,8 +540,8 @@ const AddModal: React.FC<AddModalProps> = (props) => {
|
|||||||
>
|
>
|
||||||
<CatalogFormContext.Provider
|
<CatalogFormContext.Provider
|
||||||
value={{
|
value={{
|
||||||
sizeOptions: sizeOptions,
|
sizeOptions: [],
|
||||||
quantizationOptions: quantizationOptions,
|
quantizationOptions: [],
|
||||||
onSizeChange: handleOnSizeChange,
|
onSizeChange: handleOnSizeChange,
|
||||||
onQuantizationChange: handleOnQuantizationChange
|
onQuantizationChange: handleOnQuantizationChange
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ export const FormContext = React.createContext<FormContextProps>(
|
|||||||
{} as FormContextProps
|
{} as FormContextProps
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Catalog form context
|
||||||
|
*/
|
||||||
export const CatalogFormContext = React.createContext<CatalogFormContextProps>(
|
export const CatalogFormContext = React.createContext<CatalogFormContextProps>(
|
||||||
{} as CatalogFormContextProps
|
{} as CatalogFormContextProps
|
||||||
);
|
);
|
||||||
@@ -50,6 +53,10 @@ export const FormOuterContext = React.createContext<FormOuterContextProps>(
|
|||||||
{} as FormOuterContextProps
|
{} as FormOuterContextProps
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks to use the form context
|
||||||
|
*/
|
||||||
|
|
||||||
export const useFormContext = () => {
|
export const useFormContext = () => {
|
||||||
const context = React.useContext(FormContext);
|
const context = React.useContext(FormContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
@@ -68,6 +75,11 @@ export const useCatalogFormContext = () => {
|
|||||||
return context;
|
return context;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Hooks to use the outer form context
|
||||||
|
*/
|
||||||
|
|
||||||
export const useFormOuterContext = () => {
|
export const useFormOuterContext = () => {
|
||||||
const context = React.useContext(FormOuterContext);
|
const context = React.useContext(FormOuterContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
|
|||||||
@@ -82,6 +82,14 @@ export interface FormData {
|
|||||||
ram_ratio: number;
|
ram_ratio: number;
|
||||||
ram_size: number;
|
ram_size: number;
|
||||||
};
|
};
|
||||||
|
speculative_config: {
|
||||||
|
enabled: boolean;
|
||||||
|
algorithm: string;
|
||||||
|
draft_model_name: string;
|
||||||
|
num_draft_tokens: number;
|
||||||
|
ngram_min_match_length: number;
|
||||||
|
ngram_max_match_length: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ComputedResourceClaim {
|
interface ComputedResourceClaim {
|
||||||
@@ -168,7 +176,8 @@ export interface CatalogItem {
|
|||||||
icon: string;
|
icon: string;
|
||||||
categories: string[];
|
categories: string[];
|
||||||
capabilities: string[];
|
capabilities: string[];
|
||||||
sizes: number[];
|
size: number;
|
||||||
|
activated_size: number;
|
||||||
licenses: string[];
|
licenses: string[];
|
||||||
release_date: string;
|
release_date: string;
|
||||||
}
|
}
|
||||||
@@ -279,3 +288,15 @@ export interface BackendItem {
|
|||||||
is_deprecated: boolean;
|
is_deprecated: boolean;
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DraftModelItem {
|
||||||
|
source: string;
|
||||||
|
huggingface_repo_id: string;
|
||||||
|
huggingface_filename: string;
|
||||||
|
ollama_library_model_name: string;
|
||||||
|
model_scope_model_id: string;
|
||||||
|
model_scope_file_path: string;
|
||||||
|
local_path: string;
|
||||||
|
name: string;
|
||||||
|
algorithm: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -81,41 +81,35 @@ const AdvanceConfig = () => {
|
|||||||
[backendOptionsMap.vllm, backendOptionsMap.ascendMindie].includes(
|
[backendOptionsMap.vllm, backendOptionsMap.ascendMindie].includes(
|
||||||
backend
|
backend
|
||||||
) && (
|
) && (
|
||||||
<div style={{ paddingBottom: 22 }}>
|
<Form.Item<FormData>
|
||||||
<Form.Item<FormData>
|
name="distributed_inference_across_workers"
|
||||||
name="distributed_inference_across_workers"
|
valuePropName="checked"
|
||||||
valuePropName="checked"
|
style={{ marginBottom: 8 }}
|
||||||
style={{ padding: '0 10px', marginBottom: 0 }}
|
>
|
||||||
noStyle
|
<CheckboxField
|
||||||
>
|
description={intl.formatMessage({
|
||||||
<CheckboxField
|
id: 'models.form.distribution.tips'
|
||||||
description={intl.formatMessage({
|
})}
|
||||||
id: 'models.form.distribution.tips'
|
label={intl.formatMessage({
|
||||||
})}
|
id: 'resources.form.enableDistributedInferenceAcrossWorkers'
|
||||||
label={intl.formatMessage({
|
})}
|
||||||
id: 'resources.form.enableDistributedInferenceAcrossWorkers'
|
></CheckboxField>
|
||||||
})}
|
</Form.Item>
|
||||||
></CheckboxField>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
<div style={{ paddingBottom: 22 }}>
|
<Form.Item<FormData>
|
||||||
<Form.Item<FormData>
|
name="restart_on_error"
|
||||||
name="restart_on_error"
|
valuePropName="checked"
|
||||||
valuePropName="checked"
|
style={{ marginBottom: 8 }}
|
||||||
style={{ padding: '0 10px', marginBottom: 0 }}
|
>
|
||||||
noStyle
|
<CheckboxField
|
||||||
>
|
description={intl.formatMessage({
|
||||||
<CheckboxField
|
id: 'models.form.restart.onerror.tips'
|
||||||
description={intl.formatMessage({
|
})}
|
||||||
id: 'models.form.restart.onerror.tips'
|
label={intl.formatMessage({
|
||||||
})}
|
id: 'models.form.restart.onerror'
|
||||||
label={intl.formatMessage({
|
})}
|
||||||
id: 'models.form.restart.onerror'
|
></CheckboxField>
|
||||||
})}
|
</Form.Item>
|
||||||
></CheckboxField>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -366,12 +366,21 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
categories: null,
|
categories: null,
|
||||||
restart_on_error: true,
|
restart_on_error: true,
|
||||||
distributed_inference_across_workers: true,
|
distributed_inference_across_workers: true,
|
||||||
|
mode: 'throughput',
|
||||||
extended_kv_cache: {
|
extended_kv_cache: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
chunk_size: null,
|
chunk_size: null,
|
||||||
ram_ratio: 1.2,
|
ram_ratio: 1.2,
|
||||||
ram_size: null
|
ram_size: null
|
||||||
},
|
},
|
||||||
|
speculative_config: {
|
||||||
|
enabled: false,
|
||||||
|
algorithm: '',
|
||||||
|
draft_model_name: null,
|
||||||
|
num_draft_tokens: 3,
|
||||||
|
ngram_min_match_length: 1,
|
||||||
|
ngram_max_match_length: 10
|
||||||
|
},
|
||||||
...initialValues
|
...initialValues
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -49,32 +49,30 @@ const KVCacheForm = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div style={{ paddingBottom: 22 }}>
|
<Form.Item<FormData>
|
||||||
<Form.Item<FormData>
|
data-field="extended_kv_cache.enabled"
|
||||||
data-field="extended_kv_cache.enabled"
|
name={['extended_kv_cache', 'enabled']}
|
||||||
name={['extended_kv_cache', 'enabled']}
|
valuePropName="checked"
|
||||||
valuePropName="checked"
|
style={{ marginBottom: 8 }}
|
||||||
style={{ padding: '0 10px', marginBottom: 0 }}
|
extra={
|
||||||
extra={
|
!builtInBackend && (
|
||||||
!builtInBackend && (
|
<span
|
||||||
<span
|
dangerouslySetInnerHTML={{
|
||||||
dangerouslySetInnerHTML={{
|
__html: intl.formatMessage({ id: 'models.form.kvCache.tips' })
|
||||||
__html: intl.formatMessage({ id: 'models.form.kvCache.tips' })
|
}}
|
||||||
}}
|
></span>
|
||||||
></span>
|
)
|
||||||
)
|
}
|
||||||
}
|
>
|
||||||
>
|
<CheckboxField
|
||||||
<CheckboxField
|
description={intl.formatMessage({
|
||||||
description={intl.formatMessage({
|
id: 'models.form.kvCache.tips2'
|
||||||
id: 'models.form.kvCache.tips2'
|
})}
|
||||||
})}
|
disabled={!builtInBackend}
|
||||||
disabled={!builtInBackend}
|
onChange={handleOnChange}
|
||||||
onChange={handleOnChange}
|
label={intl.formatMessage({ id: 'models.form.extendedkvcache' })}
|
||||||
label={intl.formatMessage({ id: 'models.form.extendedkvcache' })}
|
></CheckboxField>
|
||||||
></CheckboxField>
|
</Form.Item>
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
{kvCacheEnabled && (
|
{kvCacheEnabled && (
|
||||||
<>
|
<>
|
||||||
<Form.Item<FormData> name={['extended_kv_cache', 'ram_ratio']}>
|
<Form.Item<FormData> name={['extended_kv_cache', 'ram_ratio']}>
|
||||||
|
|||||||
@@ -1,41 +1,51 @@
|
|||||||
|
import SealSelect from '@/components/seal-form/seal-select';
|
||||||
|
import TooltipList from '@/components/tooltip-list';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { deployFormKeyMap } from '../config';
|
||||||
|
import { useFormContext } from '../config/form-context';
|
||||||
import KVCacheForm from './kv-cache';
|
import KVCacheForm from './kv-cache';
|
||||||
|
import SpeculativeDecode from './speculative-decode';
|
||||||
|
|
||||||
|
const flavorTipsList = [
|
||||||
|
{
|
||||||
|
title: 'Throughput',
|
||||||
|
tips: 'models.form.flavor.throughput.tips'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Latency',
|
||||||
|
tips: 'models.form.flavor.latency.tips'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Reference',
|
||||||
|
tips: 'models.form.flavor.reference.tips'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const Performance: React.FC = () => {
|
const Performance: React.FC = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const form = Form.useFormInstance();
|
const form = Form.useFormInstance();
|
||||||
|
const { formKey } = useFormContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<div data-field="extended_kv_cache.enabled"></div>
|
||||||
|
{formKey === deployFormKeyMap.catalog && (
|
||||||
|
<Form.Item name="mode">
|
||||||
|
<SealSelect
|
||||||
|
description={<TooltipList list={flavorTipsList}></TooltipList>}
|
||||||
|
label="Mode"
|
||||||
|
options={[
|
||||||
|
{ label: 'Throughput', value: 'throughput' },
|
||||||
|
{ label: 'Latency', value: 'latency' },
|
||||||
|
{ label: 'Reference', value: 'reference' }
|
||||||
|
]}
|
||||||
|
></SealSelect>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
<KVCacheForm></KVCacheForm>
|
<KVCacheForm></KVCacheForm>
|
||||||
{/* <div style={{ paddingBottom: 22, paddingLeft: 10 }}>
|
<SpeculativeDecode></SpeculativeDecode>
|
||||||
<Form.Item<FormData>
|
|
||||||
name="optimize_long_prompt"
|
|
||||||
valuePropName="checked"
|
|
||||||
style={{ padding: '0 10px', marginBottom: 0 }}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<CheckboxField
|
|
||||||
label={intl.formatMessage({ id: 'models.form.optimizeLongPrompt' })}
|
|
||||||
></CheckboxField>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
<div style={{ paddingBottom: 22, paddingLeft: 10 }}>
|
|
||||||
<Form.Item<FormData>
|
|
||||||
name="enable_speculative_decoding"
|
|
||||||
valuePropName="checked"
|
|
||||||
style={{ padding: '0 10px', marginBottom: 0 }}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<CheckboxField
|
|
||||||
label={intl.formatMessage({
|
|
||||||
id: 'models.form.enableSpeculativeDecoding'
|
|
||||||
})}
|
|
||||||
></CheckboxField>
|
|
||||||
</Form.Item>
|
|
||||||
</div> */}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
import CheckboxField from '@/components/seal-form/checkbox-field';
|
||||||
|
import SealInputNumber from '@/components/seal-form/input-number';
|
||||||
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
|
import SealSelect from '@/components/seal-form/seal-select';
|
||||||
|
import useAppUtils from '@/hooks/use-app-utils';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Form } from 'antd';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { queryDraftModelList } from '../apis';
|
||||||
|
import { FormData } from '../config/types';
|
||||||
|
|
||||||
|
const AlgorithmMap = {
|
||||||
|
Eagle3: 'eagle3',
|
||||||
|
MTP: 'mtp',
|
||||||
|
Ngram: 'ngram'
|
||||||
|
};
|
||||||
|
|
||||||
|
const SpeculativeDecode = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const { getRuleMessage } = useAppUtils();
|
||||||
|
const form = Form.useFormInstance();
|
||||||
|
const speculativeEnabled = Form.useWatch(
|
||||||
|
['speculative_config', 'enabled'],
|
||||||
|
form
|
||||||
|
);
|
||||||
|
const algorithm = Form.useWatch(['speculative_config', 'algorithm'], form);
|
||||||
|
const [draftModelList, setDraftModelList] = useState<
|
||||||
|
Global.BaseOption<string>[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const fetchDraftModels = async () => {
|
||||||
|
const response = await queryDraftModelList({
|
||||||
|
page: 1,
|
||||||
|
perPage: 100
|
||||||
|
});
|
||||||
|
const options = response.items.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.name
|
||||||
|
}));
|
||||||
|
setDraftModelList(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSpeculativeEnabledChange = (e: any) => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
form.setFieldValue('speculative_config', {
|
||||||
|
enabled: true,
|
||||||
|
algorithm: AlgorithmMap.Eagle3,
|
||||||
|
draft_model_name: null,
|
||||||
|
num_draft_tokens: 3,
|
||||||
|
ngram_min_match_length: 1,
|
||||||
|
ngram_max_match_length: 10
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (algorithm === AlgorithmMap.Eagle3) {
|
||||||
|
fetchDraftModels();
|
||||||
|
}
|
||||||
|
}, [algorithm]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item<FormData>
|
||||||
|
name={['speculative_config', 'enabled']}
|
||||||
|
valuePropName="checked"
|
||||||
|
style={{ marginBottom: 8 }}
|
||||||
|
>
|
||||||
|
<CheckboxField
|
||||||
|
label={'Enable Speculative Decoding'}
|
||||||
|
onChange={handleSpeculativeEnabledChange}
|
||||||
|
></CheckboxField>
|
||||||
|
</Form.Item>
|
||||||
|
{speculativeEnabled && (
|
||||||
|
<>
|
||||||
|
<Form.Item<FormData>
|
||||||
|
name={['speculative_config', 'algorithm']}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: getRuleMessage('select', 'Algorithm', false)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<SealSelect
|
||||||
|
required
|
||||||
|
label={'Algorithm'}
|
||||||
|
options={[
|
||||||
|
{ label: 'Eagle3', value: AlgorithmMap.Eagle3 },
|
||||||
|
{ label: 'MTP', value: AlgorithmMap.MTP },
|
||||||
|
{ label: 'N-gram', value: AlgorithmMap.Ngram }
|
||||||
|
]}
|
||||||
|
></SealSelect>
|
||||||
|
</Form.Item>
|
||||||
|
{algorithm === AlgorithmMap.Eagle3 && (
|
||||||
|
<Form.Item<FormData>
|
||||||
|
name={['speculative_config', 'draft_model_name']}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: getRuleMessage('select', 'Draft Model', false)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<SealSelect
|
||||||
|
required
|
||||||
|
label={'Draft Model'}
|
||||||
|
options={draftModelList}
|
||||||
|
></SealSelect>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
<Form.Item<FormData>
|
||||||
|
name={['speculative_config', 'num_draft_tokens']}
|
||||||
|
>
|
||||||
|
<SealInputNumber
|
||||||
|
label={'Number of Draft Tokens'}
|
||||||
|
min={1}
|
||||||
|
step={1}
|
||||||
|
precision={0}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
{algorithm === AlgorithmMap.Ngram && (
|
||||||
|
<>
|
||||||
|
<Form.Item<FormData>
|
||||||
|
name={['speculative_config', 'ngram_min_match_length']}
|
||||||
|
>
|
||||||
|
<SealInputNumber
|
||||||
|
label={'Minimum N-gram Match Length'}
|
||||||
|
min={1}
|
||||||
|
step={1}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item<FormData>
|
||||||
|
name={['speculative_config', 'ngram_max_match_length']}
|
||||||
|
>
|
||||||
|
<SealInput.Input
|
||||||
|
label={'Maximum N-gram Match Length'}
|
||||||
|
min={2}
|
||||||
|
step={1}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SpeculativeDecode;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
.catalog-item {
|
.catalog-item {
|
||||||
height: 180px;
|
height: 150px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 16px 20px;
|
padding: 16px 20px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: space-between;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
border: 1px solid var(--ant-color-border);
|
border: 1px solid var(--ant-color-border);
|
||||||
border-radius: var(--border-radius-base);
|
border-radius: var(--border-radius-base);
|
||||||
@@ -12,15 +12,12 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.img {
|
.img {
|
||||||
width: 32px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
width: 32px;
|
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,7 +26,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
flex: 1;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +43,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
gap: 12px;
|
gap: 16px;
|
||||||
margin-bottom: 12px;
|
|
||||||
|
|
||||||
.name {
|
.name {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -67,7 +62,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 12px;
|
gap: 16px;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user