Compare commits

..
10 Commits
17 changed files with 138 additions and 76 deletions
+5 -6
View File
@@ -80,13 +80,12 @@ const LabelSelector: React.FC<LabelSelectorProps> = ({
if (!clipboardText || clipboardText.indexOf('=') === -1) return;
e.preventDefault();
const lines = clipboardText
.split(/\r?\n/)
.map((line) => line.trim())
.filter((line) => line && line.includes('='));
const lines = _.split(clipboardText, /\r?\n/)
.map((line: string) => line.trim())
.filter((line: string) => line && line.includes('='));
const parsedData = lines.map((line) => {
const [key, value] = line.split('=').map((part) => part.trim());
const parsedData = lines.map((line: string) => {
const [key, value] = line.split(/=(.+)/).map((s) => s.trim());
return { key, value };
});
+4 -5
View File
@@ -92,7 +92,7 @@ export function useQueryData<Detail, Params = any>(option: {
key: string;
delay?: number;
fetchDetail: (params: Params, options?: any) => Promise<Detail>;
getData?: (response: Detail) => any;
getData?: (response: Detail, params?: any) => any;
errorMsg?: string;
}): {
loading: boolean;
@@ -123,7 +123,7 @@ export function useQueryData<Detail, Params = any>(option: {
});
}
setDetailData(getData ? getData(res) : res);
setDetailData(getData ? getData(res, params) : res);
return res;
},
@@ -132,7 +132,7 @@ export function useQueryData<Detail, Params = any>(option: {
onSuccess: () => {},
onError: (error) => {
message.error(
error?.message || errorMsg || `Failed to fetch ${key} list`
error?.message || errorMsg || `Failed to fetch ${key} data`
);
setDetailData({} as Detail);
}
@@ -146,8 +146,7 @@ export function useQueryData<Detail, Params = any>(option: {
useEffect(() => {
return () => {
cancel();
axiosTokenRef.current?.cancel();
cancelRequest();
};
}, []);
@@ -1,7 +1,8 @@
import AutoTooltip from '@/components/auto-tooltip';
import FullMarkdown from '@/components/markdown-viewer/full-markdown';
import { BulbOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Tag, Typography } from 'antd';
import { Button, Tag } from 'antd';
import _ from 'lodash';
import React, { useEffect } from 'react';
import styled from 'styled-components';
@@ -146,9 +147,7 @@ const BackendDetail: React.FC<{
</span>
</Subtitle>
<Content>
<Typography.Paragraph style={{ whiteSpace: 'pre-line' }}>
{currentData?.description}
</Typography.Paragraph>
<FullMarkdown content={currentData?.description}></FullMarkdown>
</Content>
</Section>
)}
+1 -5
View File
@@ -145,11 +145,7 @@ const AddModal: React.FC<AddModalProps> = (props) => {
is_built_in:
data.is_built_in &&
data.backend_source === BackendSourceValueMap.BUILTIN,
..._.pick(values.built_in_version_configs?.[key], [
'image_name',
'run_command',
'entrypoint'
])
..._.pick(values.built_in_version_configs?.[key], versionFields)
}));
return {
+5 -2
View File
@@ -192,7 +192,8 @@ export const customBackendFields = [
'health_check_path',
'default_run_command',
'version_configs',
'default_backend_param'
'default_backend_param',
'default_env'
];
/**
@@ -201,7 +202,8 @@ export const customBackendFields = [
export const builtInBackendFields = [
'description',
'version_configs',
'default_backend_param'
'default_backend_param',
'default_env'
];
export const frameworks = [
@@ -278,6 +280,7 @@ export const yamlTemplate = `# ----------------------------------------
# - custom_framework:
# - required
# - choose from: ${Object.values(GPUDriverMap).join(', ')}, cpu
# - env: optional, map of env key and value
backend_name: vllm-custom
description: this is my custom vllm backend
+2 -1
View File
@@ -201,6 +201,7 @@ const VersionsForm: React.FC<AddModalProps> = ({
};
const isBuiltin = backendSource === BackendSourceValueMap.BUILTIN;
const isCommunity = backendSource === BackendSourceValueMap.COMMUNITY;
return (
<>
@@ -305,7 +306,7 @@ const VersionsForm: React.FC<AddModalProps> = ({
></Form.Item>
)}
</span>
{(fields.length > 1 || isBuiltin) && (
{(fields.length > 1 || isBuiltin || isCommunity) && (
<Button
size="small"
shape="circle"
@@ -1,5 +1,6 @@
import IconFont from '@/components/icon-font';
import { PageAction } from '@/config';
import { PageActionType } from '@/config/types';
import useBodyScroll from '@/hooks/use-body-scroll';
import { ListItem } from '../config/types';
import useCommunityBackend from './use-community-backend';
@@ -30,7 +31,7 @@ const useCreateBackend = () => {
];
const handleEditBackend = (
action: PageAction,
action: PageActionType,
title: string,
row: ListItem
) => {
+8 -32
View File
@@ -1,46 +1,22 @@
import { useMemoizedFn } from 'ahooks';
import { Spin } from 'antd';
import { omit } from 'lodash';
import { useEffect, useState } from 'react';
import { useEffect } from 'react';
import PageBox from '../_components/page-box';
import { queryDashboardData } from './apis';
import DashboardInner from './components/dahboard-inner';
import DashboardContext from './config/dashboard-context';
import { DashboardProps } from './config/types';
import useQueryDashboard from './services/use-query-dashboard';
const Dashboard: React.FC = () => {
const [data, setData] = useState<DashboardProps>({} as DashboardProps);
const [loading, setLoading] = useState(false);
const fetchDashboardData = useMemoizedFn(
async (params?: { cluster_id?: number }) => {
try {
setLoading(true);
const res = await queryDashboardData(params);
setData((prev) => {
return params?.cluster_id
? {
...omit(prev, ['system_load']),
system_load: res.system_load
}
: res;
});
} catch (error) {
setData({} as DashboardProps);
} finally {
setLoading(false);
}
}
);
const { fetchData, loading, data, cancelRequest } = useQueryDashboard();
useEffect(() => {
fetchDashboardData();
fetchData({});
return () => {
cancelRequest();
};
}, []);
return (
<DashboardContext.Provider
value={{ ...data, fetchData: fetchDashboardData }}
>
<DashboardContext.Provider value={{ ...data, fetchData: fetchData }}>
<PageBox>
<Spin spinning={loading} style={{ minHeight: 300 }}>
<DashboardInner />
@@ -0,0 +1,25 @@
import { useQueryData } from '@/hooks/use-query-data-list';
import { omit } from 'lodash';
import { queryDashboardData } from '../apis';
export default function useQueryDashboard() {
const { detailData, loading, fetchData, cancelRequest } = useQueryData({
key: 'dashboard',
fetchDetail: queryDashboardData,
getData(response, params) {
return params?.cluster_id
? {
...omit(detailData, ['system_load']),
system_load: response.system_load
}
: response;
}
});
return {
loading,
data: detailData,
cancelRequest,
fetchData
};
}
@@ -7,6 +7,7 @@ interface FormContextProps {
action: PageActionType;
currentData?: any;
id?: number;
getCustomConfig?: () => Record<string, any>;
}
const FormContext = createContext<FormContextProps>({} as FormContextProps);
+1
View File
@@ -12,6 +12,7 @@ export interface FormData {
api_key: string;
proxy_url: string;
proxy_timeout: number;
proxy_enabled?: boolean;
config: {
type: maasProviderType;
openaiCustomUrl?: string;
+12 -1
View File
@@ -97,12 +97,18 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
);
};
const getCustomConfig = () => {
const customConfig = yaml2Json(advanceRef.current?.getYamlValue() || '');
return customConfig;
};
const handleOnFinish = (values: FormData) => {
const data = {
..._.omit(values, ['api_key']),
api_tokens: formatAPIKeys(values),
config: {
type: values.config.type,
openaiCustomUrl: values.config.openaiCustomUrl || undefined,
...yaml2Json(advanceRef.current?.getYamlValue() || '')
},
models: _.uniqBy(values.models, 'name'),
@@ -170,7 +176,12 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
getScrollElementScrollableHeight={getScrollElementScrollableHeight}
>
<FormContext.Provider
value={{ action, id: currentData?.id, currentData }}
value={{
action,
id: currentData?.id,
currentData,
getCustomConfig: getCustomConfig
}}
>
<Form
form={form}
+12 -3
View File
@@ -54,7 +54,7 @@ const ModelItem: React.FC<ModelItemProps> = ({
const intl = useIntl();
const form = Form.useFormInstance<FormData>();
const { runTestModel, loading: testLoading } = useTestProviderModel();
const { id, action, currentData } = useFormContext();
const { id, action, currentData, getCustomConfig } = useFormContext();
const [openTip, setOpenTip] = React.useState(false);
const generateCurrentAPIKey = (currentAPIKey: string) => {
@@ -75,6 +75,8 @@ const ModelItem: React.FC<ModelItemProps> = ({
};
const handleTestModel = async () => {
const proxyConfigEnabled = form.getFieldValue('proxy_enabled');
const customConfig = getCustomConfig?.();
const res = await runTestModel({
id: generateID(),
data: {
@@ -82,9 +84,16 @@ const ModelItem: React.FC<ModelItemProps> = ({
api_token: generateCurrentAPIKey(
form.getFieldValue('api_key')
) as string,
proxy_url: form.getFieldValue('proxy_url') || undefined,
proxy_url: proxyConfigEnabled
? form.getFieldValue('proxy_url') || null
: null,
config: {
type: form.getFieldValue(['config', 'type']) || ''
type: form.getFieldValue(['config', 'type']) || '',
...customConfig,
openaiCustomUrl:
customConfig?.openaiCustomUrl ||
form.getFieldValue(['config', 'openaiCustomUrl']) ||
null
}
}
});
@@ -1,8 +1,8 @@
import MetadataList from '@/components/metadata-list';
import { PageAction } from '@/config';
import useAppUtils from '@/hooks/use-app-utils';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import _ from 'lodash';
import { useRef } from 'react';
import { useFormContext } from '../config/form-context';
import { FormData, ProviderModel } from '../config/types';
@@ -15,9 +15,12 @@ const SupportedModels = () => {
useQueryProviderModels();
const form = Form.useFormInstance<FormData>();
const modelList = Form.useWatch('models', form) || [];
const prevAPIKeyRef = useRef<string>('');
const { getRuleMessage } = useAppUtils();
const { id, action, currentData } = useFormContext();
const prevConfigRef = useRef<{
type: string;
api_key: string;
openaiCustomUrl: string;
}>({ type: '', api_key: '', openaiCustomUrl: '' });
const { id, action, currentData, getCustomConfig } = useFormContext();
const generateCurrentAPIKey = (currentAPIKey: string) => {
if (
@@ -36,28 +39,63 @@ const SupportedModels = () => {
return 0;
};
const checkConfigChange = (current: {
type: string;
api_key: string;
openaiCustomUrl: string;
}) => {
return (
!_.isEqual(current, prevConfigRef.current) &&
current.api_key &&
current.type
);
};
const handleOpenChange = async (open: boolean) => {
try {
await form.validateFields(['api_key', ['config', 'type']]);
const proxyConfigEnabled = form.getFieldValue('proxy_enabled');
const currentAPIKey = form.getFieldValue('api_key') || '';
const configType = form.getFieldValue(['config', 'type']);
const openaiCustomUrl = form.getFieldValue(['config', 'openaiCustomUrl']);
const customConfig = getCustomConfig?.();
const currentConfig = {
type: configType,
api_key: currentAPIKey,
openaiCustomUrl: customConfig?.openaiCustomUrl || openaiCustomUrl || ''
};
// Avoid repeated requests with the same API key
if (open && prevAPIKeyRef.current !== currentAPIKey && currentAPIKey) {
prevAPIKeyRef.current = currentAPIKey;
if (open && checkConfigChange(currentConfig)) {
prevConfigRef.current = {
...currentConfig
};
fetchProviderModels({
id: generateID(),
data: {
api_token: generateCurrentAPIKey(currentAPIKey) as string,
proxy_url: form.getFieldValue('proxy_url') || undefined,
proxy_url: proxyConfigEnabled
? form.getFieldValue('proxy_url') || null
: null,
config: {
type: form.getFieldValue(['config', 'type']) || ''
type: form.getFieldValue(['config', 'type']) || '',
...customConfig,
openaiCustomUrl:
customConfig?.openaiCustomUrl || openaiCustomUrl || null
}
}
});
}
} catch (error) {
prevAPIKeyRef.current = '';
prevConfigRef.current = {
type: '',
api_key: '',
openaiCustomUrl: ''
};
// If validation fails, reset the provider model list to avoid confusion
}
};
@@ -26,7 +26,11 @@ export const useQueryProviderModels = () => {
} = useRequest(
async (params: {
id: number;
data: { api_token: string; config: { type: string }; proxy_url: string };
data: {
api_token: string;
config: { type: string; [key: string]: any };
proxy_url: string;
};
}) => {
axiosTokenRef.current?.cancel();
axiosTokenRef.current = createAxiosToken();
@@ -83,7 +87,7 @@ export const useTestProviderModel = () => {
id: number;
data: {
api_token: string;
config: { type: string };
config: { type: string; [key: string]: any };
model_name: string;
proxy_url: string;
};
+1 -1
View File
@@ -318,7 +318,7 @@ const ModelRoutes: React.FC = () => {
loading={dataSource.loading}
loadend={dataSource.loadend}
dataSource={dataSource.dataList}
image={<IconFont type="icon-extension-outline" />}
image={<IconFont type="icon-captive_portal" />}
filters={_.omit(queryParams, ['sort_by'])}
noFoundText={intl.formatMessage({
id: 'noresult.routes.nofound'
@@ -53,15 +53,14 @@ export default function useChatCompletion(
}
const deltaReasoningContent =
_.get(chunk, 'choices.0.delta.reasoning_content', '') === null
? ''
: _.get(chunk, 'choices.0.delta.reasoning_content', '');
_.get(chunk, 'choices.0.delta.reasoning_content', '') ||
_.get(chunk, 'choices.0.delta.reasoning', '') ||
'';
const deltaContent =
_.get(chunk, 'choices.0.delta.content', '') === null
? ''
: _.get(chunk, 'choices.0.delta.content', '');
console.log('deltaContent:', deltaContent);
reasonContentRef.current = reasonContentRef.current + deltaReasoningContent;
contentRef.current = contentRef.current + deltaContent;