From 5d383ec39b838eb57b223b83cdef9e5c8059b865 Mon Sep 17 00:00:00 2001 From: jialin Date: Thu, 20 Nov 2025 10:20:44 +0800 Subject: [PATCH] fix: add worker token do not update --- .../_components/command-viewer/index.tsx | 4 +- src/pages/cluster-management/clusters.tsx | 3 +- .../components/add-worker/index.tsx | 3 + .../llmodels/components/api-access-info.tsx | 27 +++- src/pages/llmodels/forms/kv-cache.tsx | 2 +- .../llmodels/hooks/use-generic-proxy.tsx | 149 ++++++++++++++++++ src/pages/playground/view-code/audio.ts | 6 +- src/pages/playground/view-code/embedding.ts | 6 +- src/pages/playground/view-code/image.ts | 6 +- src/pages/playground/view-code/llm.ts | 6 +- src/pages/playground/view-code/rerank.ts | 4 +- 11 files changed, 197 insertions(+), 19 deletions(-) create mode 100644 src/pages/llmodels/hooks/use-generic-proxy.tsx diff --git a/src/pages/_components/command-viewer/index.tsx b/src/pages/_components/command-viewer/index.tsx index 0608f205..8e3bc001 100644 --- a/src/pages/_components/command-viewer/index.tsx +++ b/src/pages/_components/command-viewer/index.tsx @@ -11,6 +11,7 @@ interface ViewerProps { options?: Global.BaseOption[]; defaultValue?: string; headerHeight?: number; + height?: number; lang?: string; onChange?: (value: string | number) => void; } @@ -31,6 +32,7 @@ const CommandViewer: React.FC = (props) => { defaultValue, options = [], headerHeight = 40, + height = 380, lang, onChange } = props || {}; @@ -66,7 +68,7 @@ const CommandViewer: React.FC = (props) => { } > { useEffect(() => { const fetchCredentialList = async () => { const data = await queryCredentialList({ page: -1 }); - const list = data?.items?.map((item) => ({ + const list = data?.items?.map((item: CredentialListItem) => ({ label: item.name, value: item.id })); diff --git a/src/pages/cluster-management/components/add-worker/index.tsx b/src/pages/cluster-management/components/add-worker/index.tsx index f8d5302b..8432aee8 100644 --- a/src/pages/cluster-management/components/add-worker/index.tsx +++ b/src/pages/cluster-management/components/add-worker/index.tsx @@ -79,6 +79,9 @@ const AddWorker: React.FC = (props) => { if (open && cluster_id && firstLoad.current) { handleOnClusterChange(cluster_id); } + return () => { + firstLoad.current = true; + }; }, [open, cluster_id]); return ( diff --git a/src/pages/llmodels/components/api-access-info.tsx b/src/pages/llmodels/components/api-access-info.tsx index 0cafc9c8..2d8fc6d7 100644 --- a/src/pages/llmodels/components/api-access-info.tsx +++ b/src/pages/llmodels/components/api-access-info.tsx @@ -17,10 +17,11 @@ import { useIntl, useNavigate } from '@umijs/max'; import { useMemoizedFn } from 'ahooks'; import { Button, Tag } from 'antd'; import _ from 'lodash'; -import { useMemo } from 'react'; +import { useEffect, useMemo } from 'react'; import styled from 'styled-components'; import { modelCategoriesMap } from '../config'; import { ListItem } from '../config/types'; +import useGenericProxy from '../hooks/use-generic-proxy'; const GPUSTACK_API = GPUSTACK_API_BASE_URL; @@ -75,7 +76,7 @@ const Tips = styled.div` gap: 8px; } dd { - margin-bottom: 16px; + margin-bottom: 0; } `; @@ -98,21 +99,22 @@ interface ApiAccessInfoProps { const ApiAccessInfo = ({ open, data, onClose }: ApiAccessInfoProps) => { const intl = useIntl(); const navigate = useNavigate(); + const { GenericProxyCommandCode, openProxyModal } = useGenericProxy(); - const getModelCategory = useMemoizedFn((categories: string[]) => { + const getProxyEndPoint = 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}`; + return `${window.location.origin}${MODEL_PROXY}/`; }); const endPoint = useMemo(() => { if (!data.generic_proxy) { return `${window.location.origin}/${GPUSTACK_API}`; } - return getModelCategory(data.categories || []); + return getProxyEndPoint(data.categories || []); }, [data]); const isRanker = useMemo(() => { @@ -123,6 +125,12 @@ const ApiAccessInfo = ({ open, data, onClose }: ApiAccessInfoProps) => { onClose(); }; + useEffect(() => { + if (open && data.generic_proxy) { + openProxyModal(data); + } + }, [open, data, openProxyModal]); + return ( { top: '20%' }} title={intl.formatMessage({ id: 'models.table.button.apiAccessInfo' })} - width={550} + width={600} destroyOnHidden closable={true} maskClosable={false} @@ -156,12 +164,17 @@ const ApiAccessInfo = ({ open, data, onClose }: ApiAccessInfoProps) => { > + {data.generic_proxy && ( +
+ {GenericProxyCommandCode} +
+ )} {intl.formatMessage({ id: 'models.table.apiAccessInfo.endpoint' })} - + {endPoint} {!data.generic_proxy && ( diff --git a/src/pages/llmodels/forms/kv-cache.tsx b/src/pages/llmodels/forms/kv-cache.tsx index a1283e34..4884d037 100644 --- a/src/pages/llmodels/forms/kv-cache.tsx +++ b/src/pages/llmodels/forms/kv-cache.tsx @@ -106,7 +106,7 @@ const KVCacheForm = () => { )} min={0} step={1} - precision={1} + precision={0} /> name={['extended_kv_cache', 'chunk_size']}> diff --git a/src/pages/llmodels/hooks/use-generic-proxy.tsx b/src/pages/llmodels/hooks/use-generic-proxy.tsx new file mode 100644 index 00000000..b03fe57e --- /dev/null +++ b/src/pages/llmodels/hooks/use-generic-proxy.tsx @@ -0,0 +1,149 @@ +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 { useMemoizedFn } from 'ahooks'; +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 [modalStatus, setModalStatus] = useState<{ + codeValue: string; + }>({ 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 = useMemoizedFn((data?: any) => { + const { api, generateCurlCode, parameters } = getModelCategory( + data?.categories || [] + ); + + setModalStatus({ + codeValue: generateCurlCode({ + api, + modelProxy: true, + parameters: { + model: data?.name || '' + } + }) + }); + }); + + const GenericProxyCommandCode = ( + + ); + + return { + GenericProxyCommandCode, + openProxyModal, + setModalStatus + }; +}; + +export default useGenericProxy; diff --git a/src/pages/playground/view-code/audio.ts b/src/pages/playground/view-code/audio.ts index 05a3e4ba..d6bc2ab2 100644 --- a/src/pages/playground/view-code/audio.ts +++ b/src/pages/playground/view-code/audio.ts @@ -1,5 +1,5 @@ import _ from 'lodash'; -import { GPUSTACK_API, OPENAI_COMPATIBLE } from '../apis'; +import { GPUSTACK_API, MODEL_PROXY, OPENAI_COMPATIBLE } from '../apis'; import { fomatNodeJsParams, formatCurlArgs, formatPyParams } from './utils'; export const generateSpeechToTextCurlCode = ({ @@ -9,7 +9,9 @@ export const generateSpeechToTextCurlCode = ({ }: Record) => { const host = window.location.origin; // replace url OPENAI_COMPATIBLE with GPUSTACK - const api = url.replace(OPENAI_COMPATIBLE, GPUSTACK_API); + const api = modelProxy + ? `${MODEL_PROXY}/\${YOUR_API_PATH}` + : url.replace(OPENAI_COMPATIBLE, GPUSTACK_API); // ========================= Curl ========================= const curlCode = ` diff --git a/src/pages/playground/view-code/embedding.ts b/src/pages/playground/view-code/embedding.ts index 7b02788d..f983856e 100644 --- a/src/pages/playground/view-code/embedding.ts +++ b/src/pages/playground/view-code/embedding.ts @@ -1,4 +1,4 @@ -import { GPUSTACK_API, OPENAI_COMPATIBLE } from '../apis'; +import { GPUSTACK_API, MODEL_PROXY, OPENAI_COMPATIBLE } from '../apis'; import { fomatNodeJsParams, formatCurlArgs, formatPyParams } from './utils'; export const generateEmbeddingCurlCode = ({ @@ -7,7 +7,9 @@ export const generateEmbeddingCurlCode = ({ parameters }: Record) => { const host = window.location.origin; - const api = url.replace(OPENAI_COMPATIBLE, GPUSTACK_API); + const api = modelProxy + ? `${MODEL_PROXY}/\${YOUR_API_PATH}` + : url.replace(OPENAI_COMPATIBLE, GPUSTACK_API); // ========================= Curl ========================= const curlCode = ` diff --git a/src/pages/playground/view-code/image.ts b/src/pages/playground/view-code/image.ts index 3010e940..e3bf642a 100644 --- a/src/pages/playground/view-code/image.ts +++ b/src/pages/playground/view-code/image.ts @@ -1,5 +1,5 @@ import _ from 'lodash'; -import { GPUSTACK_API, OPENAI_COMPATIBLE } from '../apis'; +import { GPUSTACK_API, MODEL_PROXY, OPENAI_COMPATIBLE } from '../apis'; import { fomatNodeJsParams, formatCurlArgs, formatPyParams } from './utils'; export const generateImageCurlCode = ({ @@ -10,7 +10,9 @@ export const generateImageCurlCode = ({ edit = false }: Record) => { const host = window.location.origin; - const api = url.replace(OPENAI_COMPATIBLE, GPUSTACK_API); + const api = modelProxy + ? `${MODEL_PROXY}/\${YOUR_API_PATH}` + : url.replace(OPENAI_COMPATIBLE, GPUSTACK_API); // ========================= Curl ========================= let curlCode = ` diff --git a/src/pages/playground/view-code/llm.ts b/src/pages/playground/view-code/llm.ts index afce2335..522176e5 100644 --- a/src/pages/playground/view-code/llm.ts +++ b/src/pages/playground/view-code/llm.ts @@ -1,4 +1,4 @@ -import { GPUSTACK_API, OPENAI_COMPATIBLE } from '../apis'; +import { GPUSTACK_API, MODEL_PROXY, OPENAI_COMPATIBLE } from '../apis'; import { fomatNodeJsParams, formatCurlArgs, formatPyParams } from './utils'; export const generateLLmCurlCode = ({ @@ -7,7 +7,9 @@ export const generateLLmCurlCode = ({ parameters }: Record) => { const host = window.location.origin; - const api = url.replace(OPENAI_COMPATIBLE, GPUSTACK_API); + const api = modelProxy + ? `${MODEL_PROXY}/\${YOUR_API_PATH}` + : url.replace(OPENAI_COMPATIBLE, GPUSTACK_API); // ========================= Curl ========================= const curlCode = ` diff --git a/src/pages/playground/view-code/rerank.ts b/src/pages/playground/view-code/rerank.ts index 92a8e7cf..962b7263 100644 --- a/src/pages/playground/view-code/rerank.ts +++ b/src/pages/playground/view-code/rerank.ts @@ -1,3 +1,4 @@ +import { MODEL_PROXY } from '../apis'; import { formatCurlArgs } from './utils'; export const generateRerankCurlCode = ({ @@ -6,10 +7,11 @@ export const generateRerankCurlCode = ({ parameters }: Record) => { const host = window.location.origin; + const apiUrl = modelProxy ? `${MODEL_PROXY}/\${YOUR_API_PATH}` : api; // ========================= Curl ========================= const curlCode = ` -curl ${host}${api} \\ +curl ${host}${apiUrl} \\ -H "Content-Type: application/json" \\ -H "Authorization: Bearer $\{YOUR_GPUSTACK_API_KEY}" \\${modelProxy ? `\n-H "X-GPUStack-Model: ${parameters.model}" \\` : ''} ${formatCurlArgs(parameters, false)}`.trim();