Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf521ad79e | ||
|
|
76a3e5cfcf | ||
|
|
200d63e5fc | ||
|
|
0aeefb1217 | ||
|
|
7dfeb859e2 | ||
|
|
5151f26b9e | ||
|
|
053e5cca86 | ||
|
|
89765c19c4 | ||
|
|
0824f0c13b | ||
|
|
04092c3fa5 |
@@ -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 };
|
||||
});
|
||||
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
) => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface FormData {
|
||||
api_key: string;
|
||||
proxy_url: string;
|
||||
proxy_timeout: number;
|
||||
proxy_enabled?: boolean;
|
||||
config: {
|
||||
type: maasProviderType;
|
||||
openaiCustomUrl?: string;
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user