fix: api access info
This commit is contained in:
@@ -3,16 +3,48 @@ import CopyButton from '@/components/copy-button';
|
||||
import IconFont from '@/components/icon-font';
|
||||
import ScrollerModal from '@/components/scroller-modal';
|
||||
import { GPUSTACK_API_BASE_URL } from '@/config/settings';
|
||||
import {
|
||||
AUDIO_SPEECH_TO_TEXT_API,
|
||||
AUDIO_TEXT_TO_SPEECH_API,
|
||||
CHAT_API,
|
||||
CREAT_IMAGE_API,
|
||||
EMBEDDING_API,
|
||||
MODEL_PROXY,
|
||||
RERANKER_API
|
||||
} from '@/pages/playground/apis';
|
||||
import { BulbOutlined } from '@ant-design/icons';
|
||||
import { useIntl, useNavigate } from '@umijs/max';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { Button, Tag } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import { useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { modelCategoriesMap } from '../config';
|
||||
import { ListItem } from '../config/types';
|
||||
|
||||
const GPUSTACK_API = GPUSTACK_API_BASE_URL;
|
||||
|
||||
const API_MAP: Record<string, { api: string }> = {
|
||||
[modelCategoriesMap.embedding]: {
|
||||
api: EMBEDDING_API
|
||||
},
|
||||
[modelCategoriesMap.llm]: {
|
||||
api: CHAT_API
|
||||
},
|
||||
[modelCategoriesMap.image]: {
|
||||
api: CREAT_IMAGE_API
|
||||
},
|
||||
[modelCategoriesMap.text_to_speech]: {
|
||||
api: AUDIO_TEXT_TO_SPEECH_API
|
||||
},
|
||||
[modelCategoriesMap.speech_to_text]: {
|
||||
api: AUDIO_SPEECH_TO_TEXT_API
|
||||
},
|
||||
[modelCategoriesMap.reranker]: {
|
||||
api: RERANKER_API
|
||||
}
|
||||
};
|
||||
|
||||
const ApiAccessInfoWrapper = styled.div`
|
||||
display: grid;
|
||||
padding-left: 20px;
|
||||
@@ -59,7 +91,7 @@ const CreateButton = styled(Button)`
|
||||
|
||||
interface ApiAccessInfoProps {
|
||||
open: boolean;
|
||||
data: any;
|
||||
data: ListItem;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
@@ -67,7 +99,21 @@ const ApiAccessInfo = ({ open, data, onClose }: ApiAccessInfoProps) => {
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const endPoint = `${window.location.origin}/${GPUSTACK_API}`;
|
||||
const getModelCategory = useMemoizedFn((categories: string[]) => {
|
||||
for (const [category, config] of Object.entries(API_MAP)) {
|
||||
if (categories.includes(category)) {
|
||||
return `${MODEL_PROXY}${config.api}`;
|
||||
}
|
||||
}
|
||||
return `${MODEL_PROXY}${CHAT_API}`;
|
||||
});
|
||||
|
||||
const endPoint = useMemo(() => {
|
||||
if (!data.generic_proxy) {
|
||||
return `${window.location.origin}/${GPUSTACK_API}`;
|
||||
}
|
||||
return getModelCategory(data.categories || []);
|
||||
}, [data]);
|
||||
|
||||
const isRanker = useMemo(() => {
|
||||
return _.includes(data.categories, modelCategoriesMap.reranker);
|
||||
@@ -113,9 +159,13 @@ const ApiAccessInfo = ({ open, data, onClose }: ApiAccessInfoProps) => {
|
||||
<BulbOutlined />
|
||||
</dt>
|
||||
<dd>
|
||||
{intl.formatMessage({
|
||||
id: 'models.table.button.apiAccessInfo.tips'
|
||||
})}
|
||||
{data.generic_proxy
|
||||
? intl.formatMessage({
|
||||
id: 'models.table.genericProxy'
|
||||
})
|
||||
: intl.formatMessage({
|
||||
id: 'models.table.button.apiAccessInfo.tips'
|
||||
})}
|
||||
</dd>
|
||||
</dl>
|
||||
</Tips>
|
||||
@@ -127,13 +177,17 @@ const ApiAccessInfo = ({ open, data, onClose }: ApiAccessInfoProps) => {
|
||||
<AutoTooltip ghost maxWidth={180}>
|
||||
{endPoint}
|
||||
</AutoTooltip>
|
||||
<APITAG color="geekblue">
|
||||
{intl.formatMessage({
|
||||
id: isRanker
|
||||
? 'models.table.apiAccessInfo.jinaCompatible'
|
||||
: 'models.table.apiAccessInfo.openaiCompatible'
|
||||
})}
|
||||
</APITAG>
|
||||
{!data.generic_proxy && (
|
||||
<APITAG color="geekblue">
|
||||
{isRanker
|
||||
? intl.formatMessage({
|
||||
id: 'models.table.apiAccessInfo.jinaCompatible'
|
||||
})
|
||||
: intl.formatMessage({
|
||||
id: 'models.table.apiAccessInfo.openaiCompatible'
|
||||
})}
|
||||
</APITAG>
|
||||
)}
|
||||
</span>
|
||||
<span className="copy-btn">
|
||||
<CopyButton text={endPoint} type="link" size="small"></CopyButton>
|
||||
|
||||
@@ -58,7 +58,6 @@ import {
|
||||
SourceType
|
||||
} from '../config/types';
|
||||
import useFormInitialValues from '../hooks/use-form-initial-values';
|
||||
import useGenericProxy from '../hooks/use-generic-proxy';
|
||||
import useModelsColumns from '../hooks/use-models-columns';
|
||||
import AccessControlModal from './access-control-modal';
|
||||
import APIAccessInfoModal from './api-access-info';
|
||||
@@ -196,8 +195,6 @@ const Models: React.FC<ModelsProps> = ({
|
||||
});
|
||||
const modalRef = useRef<any>(null);
|
||||
|
||||
const { GenericProxyModal, openProxyModal } = useGenericProxy();
|
||||
|
||||
useEffect(() => {
|
||||
if (deleteIds?.length) {
|
||||
rowSelection.removeSelectedKey(deleteIds);
|
||||
@@ -429,12 +426,7 @@ const Models: React.FC<ModelsProps> = ({
|
||||
const handleViewAPIInfo = useCallback((row: ListItem) => {
|
||||
setAPIAccessInfo({
|
||||
show: true,
|
||||
data: {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
categories: row.categories,
|
||||
url: `${MODELS_API}/${row.id}/instances`
|
||||
}
|
||||
data: row
|
||||
});
|
||||
}, []);
|
||||
const handleSelect = useMemoizedFn(async (val: any, row: ListItem) => {
|
||||
@@ -459,10 +451,6 @@ const Models: React.FC<ModelsProps> = ({
|
||||
handleViewAPIInfo(row);
|
||||
}
|
||||
|
||||
if (val === 'proxy') {
|
||||
openProxyModal(row);
|
||||
}
|
||||
|
||||
if (val === 'stop') {
|
||||
modalRef.current?.show({
|
||||
content: 'models.instances',
|
||||
@@ -796,7 +784,6 @@ const Models: React.FC<ModelsProps> = ({
|
||||
currentData={openAccessControlModal.currentData}
|
||||
action={openAccessControlModal.action}
|
||||
></AccessControlModal>
|
||||
{GenericProxyModal}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import icons from '@/components/icon-font/icons';
|
||||
import HotKeys from '@/config/hotkeys';
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import { modelCategoriesMap, modelSourceMap } from './index';
|
||||
|
||||
@@ -50,11 +49,6 @@ export const ActionList: ActionItem[] = [
|
||||
key: 'chat',
|
||||
icon: icons.ExperimentOutlined
|
||||
},
|
||||
{
|
||||
label: 'models.form.generic_proxy.button',
|
||||
key: 'proxy',
|
||||
icon: icons.CaptivePortal
|
||||
},
|
||||
{
|
||||
label: 'models.table.button.apiAccessInfo',
|
||||
key: 'api',
|
||||
@@ -157,27 +151,6 @@ export const generateSource = (record: any) => {
|
||||
return '';
|
||||
};
|
||||
|
||||
export const setModelActionList = (record: any) => {
|
||||
return _.filter(ActionList, (action: any) => {
|
||||
if (action.key === 'chat' || action.key === 'api') {
|
||||
return record.ready_replicas > 0;
|
||||
}
|
||||
if (action.key === 'start') {
|
||||
return record.replicas === 0;
|
||||
}
|
||||
|
||||
if (action.key === 'stop') {
|
||||
return record.replicas > 0;
|
||||
}
|
||||
|
||||
if (action.key === 'proxy') {
|
||||
return record.generic_proxy;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
export const modelFileActions = [
|
||||
{
|
||||
label: 'common.button.deploy',
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface ListItem {
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
access_policy: 'public' | 'authed' | 'allowed_users';
|
||||
generic_proxy?: boolean;
|
||||
gpu_selector?: {
|
||||
gpu_ids: string[];
|
||||
gpus_per_replica?: number;
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
import ScrollerModal from '@/components/scroller-modal';
|
||||
import CommandViewer from '@/pages/_components/command-viewer';
|
||||
import {
|
||||
AUDIO_SPEECH_TO_TEXT_API,
|
||||
AUDIO_TEXT_TO_SPEECH_API,
|
||||
CHAT_API,
|
||||
CREAT_IMAGE_API,
|
||||
EMBEDDING_API,
|
||||
MODEL_PROXY,
|
||||
RERANKER_API
|
||||
} from '@/pages/playground/apis';
|
||||
import {
|
||||
generateSpeechToTextCurlCode,
|
||||
generateTextToSpeechCurlCode
|
||||
} from '@/pages/playground/view-code/audio';
|
||||
import { generateEmbeddingCurlCode } from '@/pages/playground/view-code/embedding';
|
||||
import { generateImageCurlCode } from '@/pages/playground/view-code/image';
|
||||
import { generateLLmCurlCode } from '@/pages/playground/view-code/llm';
|
||||
import { generateRerankCurlCode } from '@/pages/playground/view-code/rerank';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useState } from 'react';
|
||||
import { modelCategoriesMap } from '../config';
|
||||
|
||||
const API_MAP: Record<
|
||||
string,
|
||||
{ api: string; parameters: any; generateCurlCode: (args: any) => string }
|
||||
> = {
|
||||
[modelCategoriesMap.embedding]: {
|
||||
api: EMBEDDING_API,
|
||||
parameters: {
|
||||
query: 'What are the benefits of regular exercise?',
|
||||
documents: [
|
||||
'Regular physical activity helps improve cardiovascular health and mental well-being.',
|
||||
'Eating too much sugar can lead to health issues.',
|
||||
'Exercise is often done in gyms or outdoors.'
|
||||
]
|
||||
},
|
||||
generateCurlCode: generateEmbeddingCurlCode
|
||||
},
|
||||
[modelCategoriesMap.llm]: {
|
||||
api: CHAT_API,
|
||||
parameters: {
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Hello, introduce yourself'
|
||||
}
|
||||
]
|
||||
},
|
||||
generateCurlCode: generateLLmCurlCode
|
||||
},
|
||||
[modelCategoriesMap.image]: {
|
||||
api: CREAT_IMAGE_API,
|
||||
parameters: {},
|
||||
generateCurlCode: generateImageCurlCode
|
||||
},
|
||||
[modelCategoriesMap.text_to_speech]: {
|
||||
api: AUDIO_TEXT_TO_SPEECH_API,
|
||||
parameters: {
|
||||
response_format: 'mp3',
|
||||
input: ''
|
||||
},
|
||||
generateCurlCode: generateTextToSpeechCurlCode
|
||||
},
|
||||
[modelCategoriesMap.speech_to_text]: {
|
||||
api: AUDIO_SPEECH_TO_TEXT_API,
|
||||
parameters: {},
|
||||
generateCurlCode: generateSpeechToTextCurlCode
|
||||
},
|
||||
[modelCategoriesMap.reranker]: {
|
||||
api: RERANKER_API,
|
||||
parameters: {
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Hello, introduce yourself'
|
||||
}
|
||||
]
|
||||
},
|
||||
generateCurlCode: generateRerankCurlCode
|
||||
}
|
||||
};
|
||||
|
||||
const langOptions = [{ label: 'Curl', value: 'bash' }];
|
||||
|
||||
const useGenericProxy = () => {
|
||||
const intl = useIntl();
|
||||
const [modalStatus, setModalStatus] = useState<{
|
||||
open: boolean;
|
||||
codeValue: string;
|
||||
}>({ open: false, codeValue: '' });
|
||||
|
||||
const onCancel = () => {
|
||||
setModalStatus({
|
||||
open: false,
|
||||
codeValue: ''
|
||||
});
|
||||
};
|
||||
|
||||
const getModelCategory = (categories: string[]) => {
|
||||
for (const [category, config] of Object.entries(API_MAP)) {
|
||||
if (categories.includes(category)) {
|
||||
return {
|
||||
category,
|
||||
api: `${MODEL_PROXY}${config.api}`,
|
||||
parameters: config.parameters,
|
||||
generateCurlCode: config.generateCurlCode
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
category: modelCategoriesMap.llm,
|
||||
api: CHAT_API,
|
||||
parameters: {
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Hello, introduce yourself'
|
||||
}
|
||||
]
|
||||
},
|
||||
generateCurlCode: generateLLmCurlCode
|
||||
};
|
||||
};
|
||||
|
||||
const openProxyModal = (data?: any) => {
|
||||
const { api, generateCurlCode, parameters } = getModelCategory(
|
||||
data?.categories || []
|
||||
);
|
||||
|
||||
setModalStatus({
|
||||
open: true,
|
||||
codeValue: generateCurlCode({
|
||||
api,
|
||||
modelProxy: true,
|
||||
parameters: {
|
||||
model: data?.name || '',
|
||||
...parameters
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
const GenericProxyModal = (
|
||||
<ScrollerModal
|
||||
title={intl.formatMessage({
|
||||
id: 'models.form.generic_proxy.button'
|
||||
})}
|
||||
open={modalStatus.open}
|
||||
centered={true}
|
||||
onCancel={onCancel}
|
||||
destroyOnHidden={true}
|
||||
closeIcon={true}
|
||||
maskClosable={false}
|
||||
keyboard={false}
|
||||
width={700}
|
||||
footer={false}
|
||||
>
|
||||
<div
|
||||
style={{ marginBottom: 8 }}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: intl.formatMessage({ id: 'models.table.genericProxy' })
|
||||
}}
|
||||
></div>
|
||||
<CommandViewer
|
||||
code={modalStatus.codeValue}
|
||||
copyText={modalStatus.codeValue}
|
||||
options={langOptions}
|
||||
defaultValue={'bash'}
|
||||
></CommandViewer>
|
||||
</ScrollerModal>
|
||||
);
|
||||
|
||||
return {
|
||||
GenericProxyModal,
|
||||
openProxyModal,
|
||||
setModalStatus
|
||||
};
|
||||
};
|
||||
|
||||
export default useGenericProxy;
|
||||
@@ -8,11 +8,30 @@ import { useIntl } from '@umijs/max';
|
||||
import { Tooltip } from 'antd';
|
||||
import type { SortOrder } from 'antd/es/table/interface';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
import { useMemo } from 'react';
|
||||
import ModelTag from '../components/model-tag';
|
||||
import { generateSource, setModelActionList } from '../config/button-actions';
|
||||
import { ActionList, generateSource } from '../config/button-actions';
|
||||
import { ListItem } from '../config/types';
|
||||
|
||||
const setModelActionList = (record: any) => {
|
||||
return _.filter(ActionList, (action: any) => {
|
||||
if (action.key === 'chat' || action.key === 'api') {
|
||||
return record.ready_replicas > 0;
|
||||
}
|
||||
|
||||
if (action.key === 'start') {
|
||||
return record.replicas === 0;
|
||||
}
|
||||
|
||||
if (action.key === 'stop') {
|
||||
return record.replicas > 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
interface ModelsColumnsHookProps {
|
||||
handleSelect: (val: string, record: ListItem) => void;
|
||||
sortOrder: SortOrder;
|
||||
|
||||
Reference in New Issue
Block a user