From 295b4b3b72309665439d0038f91922894d9e734b Mon Sep 17 00:00:00 2001 From: jialin Date: Thu, 27 Feb 2025 18:02:28 +0800 Subject: [PATCH] chore: download progress --- src/components/drop-down-buttons/index.tsx | 3 +- .../seal-form/components/wrapper.less | 8 + src/components/simple-table/header.tsx | 8 +- src/components/simple-table/index.tsx | 9 +- src/hooks/use-chunk-fetch.ts | 13 +- src/hooks/use-download-stream.ts | 66 ++- .../llmodels/components/instance-item.tsx | 504 ++++++++++-------- src/pages/llmodels/components/instances.tsx | 38 ++ src/pages/llmodels/components/table-list.tsx | 23 +- src/pages/playground/apis/index.ts | 6 +- .../playground/components/view-code-modal.tsx | 6 +- src/pages/playground/view-code/audio.ts | 23 +- src/pages/playground/view-code/embedding.ts | 9 +- src/pages/playground/view-code/image.ts | 14 +- 14 files changed, 441 insertions(+), 289 deletions(-) create mode 100644 src/pages/llmodels/components/instances.tsx diff --git a/src/components/drop-down-buttons/index.tsx b/src/components/drop-down-buttons/index.tsx index 46e13a16..6a9dd6da 100644 --- a/src/components/drop-down-buttons/index.tsx +++ b/src/components/drop-down-buttons/index.tsx @@ -3,7 +3,6 @@ import { useIntl } from '@umijs/max'; import { Button, Dropdown, Tooltip, type MenuProps } from 'antd'; import classNames from 'classnames'; import _ from 'lodash'; -import { memo } from 'react'; import './index.less'; type Trigger = 'click' | 'hover'; @@ -106,4 +105,4 @@ const DropdownButtons: React.FC = ({ ); }; -export default memo(DropdownButtons); +export default DropdownButtons; diff --git a/src/components/seal-form/components/wrapper.less b/src/components/seal-form/components/wrapper.less index f8b8d1e8..97c4292b 100644 --- a/src/components/seal-form/components/wrapper.less +++ b/src/components/seal-form/components/wrapper.less @@ -324,6 +324,14 @@ top: 50%; } } + + :global(.ant-select-selector) { + padding-block-start: 0 !important; + } + + :global(.ant-select .ant-select-selection-search) { + top: 10px !important; + } } :global( diff --git a/src/components/simple-table/header.tsx b/src/components/simple-table/header.tsx index ffdae3c4..8ee172b3 100644 --- a/src/components/simple-table/header.tsx +++ b/src/components/simple-table/header.tsx @@ -1,3 +1,4 @@ +import { useIntl } from '@umijs/max'; import React from 'react'; interface TableHeaderProps { @@ -7,12 +8,17 @@ interface TableHeaderProps { columns: any[]; } const TableHeader = ({ columns }: TableHeaderProps) => { + const intl = useIntl(); return ( {columns.map((column: any, index: number) => { return ( - {column.title} + + {column.locale + ? intl.formatMessage({ id: column.title }) + : column.title} + ); })} diff --git a/src/components/simple-table/index.tsx b/src/components/simple-table/index.tsx index de375164..867279a7 100644 --- a/src/components/simple-table/index.tsx +++ b/src/components/simple-table/index.tsx @@ -6,8 +6,15 @@ import TableHeader from './header'; import './index.less'; import TableRow from './row'; +interface ColumnProps { + title: string; + key: string; + render?: (data: { dataIndex: string; row: any }) => any; + locale?: boolean; +} + interface SimpleTableProps { - columns: any[]; + columns: ColumnProps[]; dataSource: any[]; bordered?: boolean; rowKey?: string; diff --git a/src/hooks/use-chunk-fetch.ts b/src/hooks/use-chunk-fetch.ts index 2700c1f2..13e5aaae 100644 --- a/src/hooks/use-chunk-fetch.ts +++ b/src/hooks/use-chunk-fetch.ts @@ -13,6 +13,7 @@ type HandlerFunction = (data: any, options?: HandlerOptions) => any; interface RequestConfig { url: string; handler: HandlerFunction; + errorHandler?: (error: any) => void; beforeReconnect?: () => void; params?: object; watch?: boolean; @@ -59,7 +60,7 @@ const useSetChunkFetch = () => { currentBuffer.forEach((item, i) => { const isComplete = i === currentBuffer.length - 1 && done; callback(item, { - isComplete, + isComplete: isComplete || this.percent === 100, percent: this.percent, progress: this.progress, contentLength: this.contentLength @@ -110,6 +111,7 @@ const useSetChunkFetch = () => { const fetchChunkRequest = async ({ url, handler, + errorHandler, watch, params = {} }: RequestConfig) => { @@ -133,7 +135,11 @@ const useSetChunkFetch = () => { if (!response.ok) { const error = await response.json(); - handler(error?.message); + if (errorHandler) { + errorHandler(error); + } else { + handler(error?.message); + } return; } @@ -141,8 +147,7 @@ const useSetChunkFetch = () => { console.log('chunkDataRef.current===1', chunkDataRef.current); } catch (error) { - // handle error - console.log('error============', error); + // handle error: catched in request interceptor } return axiosToken.current; diff --git a/src/hooks/use-download-stream.ts b/src/hooks/use-download-stream.ts index f67919a5..2f1554ed 100644 --- a/src/hooks/use-download-stream.ts +++ b/src/hooks/use-download-stream.ts @@ -1,5 +1,5 @@ import useSetChunkFetch, { HandlerOptions } from '@/hooks/use-chunk-fetch'; -import dayjs from 'dayjs'; +import { message } from 'antd'; import { useEffect, useRef } from 'react'; export default function useDownloadStream() { @@ -7,18 +7,17 @@ export default function useDownloadStream() { const logParseWorker = useRef(null); const clearScreen = useRef(false); const filename = useRef('log'); + const downloadNotificationRef = useRef(null); + const { setChunkFetch } = useSetChunkFetch(); const downloadFile = (content: string) => { - const timestamp = dayjs().format('YYYY-MM-DD_HH-mm-ss'); - const fileName = `${filename.current}_${timestamp}.txt`; - const blob = new Blob([content], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; - a.download = fileName; + a.download = filename.current; document.body.appendChild(a); a.click(); document.body.removeChild(a); @@ -28,6 +27,13 @@ export default function useDownloadStream() { const updateContent = (data: string, options?: HandlerOptions) => { const { isComplete } = options || {}; + + downloadNotificationRef.current?.({ + ...options, + duration: isComplete ? 1 : null, + filename: filename.current + }); + logParseWorker.current?.postMessage({ inputStr: data, page: 1, @@ -38,6 +44,18 @@ export default function useDownloadStream() { clearScreen.current = false; }; + const handleError = (error: any) => { + const errorMsg = error?.message || error; + const msg = + typeof errorMsg === 'string' ? errorMsg : JSON.stringify(errorMsg); + message.error(msg); + downloadNotificationRef.current?.({ + duration: 1, + percent: 0, + filename: filename.current + }); + }; + const downloadStream = async (props: { data?: any; url: string; @@ -46,20 +64,36 @@ export default function useDownloadStream() { method?: string; headers?: any; filename?: string; + downloadNotification?: (data: any) => void; }) => { - clearScreen.current = true; - filename.current = props.filename || 'log'; - const { params, url } = props; + try { + clearScreen.current = true; + filename.current = props.filename || 'log'; + downloadNotificationRef.current = props.downloadNotification; + const { params, url } = props; - chunkRequedtRef.current?.current?.abort?.(); + downloadNotificationRef.current?.({ + filename: filename.current + }); - chunkRequedtRef.current = setChunkFetch({ - url, - params, - watch: false, - contentType: 'text', - handler: updateContent - }); + chunkRequedtRef.current?.current?.abort?.(); + + chunkRequedtRef.current = setChunkFetch({ + url, + params, + watch: false, + contentType: 'text', + errorHandler: handleError, + handler: updateContent + }); + } catch (error) { + // + downloadNotificationRef.current?.({ + duration: 1, + percent: 0, + filename: filename.current + }); + } }; useEffect(() => { diff --git a/src/pages/llmodels/components/instance-item.tsx b/src/pages/llmodels/components/instance-item.tsx index 764c4769..3c5045b4 100644 --- a/src/pages/llmodels/components/instance-item.tsx +++ b/src/pages/llmodels/components/instance-item.tsx @@ -4,10 +4,9 @@ import IconFont from '@/components/icon-font'; import RowChildren from '@/components/seal-table/components/row-children'; import SimpleTabel from '@/components/simple-table'; import StatusTag from '@/components/status-tag'; -import { - GPUDeviceItem, - ListItem as WorkerListItem -} from '@/pages/resources/config/types'; +import { HandlerOptions } from '@/hooks/use-chunk-fetch'; +import useDownloadStream from '@/hooks/use-download-stream'; +import { ListItem as WorkerListItem } from '@/pages/resources/config/types'; import { DeleteOutlined, DownloadOutlined, @@ -16,24 +15,29 @@ import { ThunderboltFilled } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; -import { Button, Col, Divider, Row, Space, Tag, Tooltip } from 'antd'; +import { + Button, + Col, + Divider, + Progress, + Row, + Tag, + Tooltip, + notification +} from 'antd'; import dayjs from 'dayjs'; import _ from 'lodash'; -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; +import { MODEL_INSTANCE_API } from '../apis'; import { InstanceStatusMap, InstanceStatusMapValue, status } from '../config'; import { ModelInstanceListItem } from '../config/types'; import '../style/instance-item.less'; interface InstanceItemProps { - list: ModelInstanceListItem[]; - gpuDeviceList: GPUDeviceItem[]; + instanceData: ModelInstanceListItem; workerList: WorkerListItem[]; modelData?: any; - handleChildSelect: ( - val: string, - item: ModelInstanceListItem, - list: ModelInstanceListItem[] - ) => void; + handleChildSelect: (val: string, item: ModelInstanceListItem) => void; } const childActionList = [ @@ -71,49 +75,87 @@ const childActionList = [ } ]; -const setChildActionList = (item: ModelInstanceListItem) => { - return _.filter(childActionList, (action: any) => { - if (action.key === 'viewlog' || action.key === 'download') { - return action.status.includes(item.state); +const distributeCols = [ + { + title: 'Worker', + key: 'worker_name' + }, + { + title: 'IP', + key: 'worker_ip', + render: ({ row }: { row: ModelInstanceListItem }) => { + return row.port ? `${row.worker_ip}:${row.port}` : row.worker_ip; } - return true; - }); + }, + { + title: 'models.table.gpuindex', + locale: true, + key: 'gpu_index' + } +]; + +const renderMessage = (title: string) => { + return ( +
+ {title} +
+ ); }; const InstanceItem: React.FC = ({ - list, + instanceData, workerList, modelData, handleChildSelect }) => { + const [api, contextHolder] = notification.useNotification(); + const { downloadStream } = useDownloadStream(); const intl = useIntl(); - const distributeCols = [ - { - title: 'Worker', - key: 'worker_name' - }, - { - title: 'IP', - key: 'worker_ip', - render: ({ row }: { row: ModelInstanceListItem }) => { - return row.port ? `${row.worker_ip}:${row.port}` : row.worker_ip; + const actionItems = useMemo(() => { + return _.filter(childActionList, (action: any) => { + if (action.key === 'viewlog' || action.key === 'download') { + return action.status.includes(instanceData.state); } - }, - { - title: intl.formatMessage({ id: 'models.table.gpuindex' }), - key: 'gpu_index' - } - ]; + return true; + }); + }, [instanceData]); - const renderWorkerInfo = (item: ModelInstanceListItem) => { + const createFileName = (name: string) => { + const timestamp = dayjs().format('YYYY-MM-DD_HH-mm-ss'); + const fileName = `${name}_${timestamp}.txt`; + return fileName; + }; + + const downloadNotification = useCallback( + (data: HandlerOptions & { filename: string; duration?: number }) => { + api.open({ + duration: data.duration, + message: renderMessage(data.filename), + key: data.filename, + description: + }); + }, + [] + ); + + const renderWorkerInfo = useMemo(() => { let workerIp = '-'; - if (item.worker_ip) { - workerIp = item.port ? `${item.worker_ip}:${item.port}` : item.worker_ip; + if (instanceData.worker_ip) { + workerIp = instanceData.port + ? `${instanceData.worker_ip}:${instanceData.port}` + : instanceData.worker_ip; } return (
-
{item.worker_name}
+
{instanceData.worker_name}
{workerIp} @@ -121,7 +163,7 @@ const InstanceItem: React.FC = ({
{intl.formatMessage({ id: 'models.table.gpuindex' })}: [ - {_.join(item.gpu_indexes?.sort?.(), ',')}] + {_.join(instanceData.gpu_indexes?.sort?.(), ',')}]
@@ -131,10 +173,10 @@ const InstanceItem: React.FC = ({
); - }; + }, [modelData, instanceData, intl]); - const renderDistributionInfo = (row: ModelInstanceListItem) => { - const rpcServerList = row.distributed_servers?.rpc_servers || []; + const renderDistributionInfo = useMemo(() => { + const rpcServerList = instanceData.distributed_servers?.rpc_servers || []; const list = _.map(rpcServerList, (item: any) => { const data = _.find(workerList, { id: item.worker_id }); return { @@ -147,10 +189,10 @@ const InstanceItem: React.FC = ({ const mainWorker = [ { - worker_name: `${row.worker_name}`, - worker_ip: `${row.worker_ip}`, + worker_name: `${instanceData.worker_name}`, + worker_ip: `${instanceData.worker_ip}`, port: '', - gpu_index: `${row.gpu_indexes?.sort?.()} (main)` + gpu_index: `${instanceData.gpu_indexes?.sort?.()} (main)` } ]; @@ -165,194 +207,202 @@ const InstanceItem: React.FC = ({ >
); - }; + }, [workerList, instanceData, intl]); + + const handleOnSelect = useCallback( + (val: string) => { + console.log('handleOnSelect', val); + if (val === 'download') { + downloadStream({ + url: `${MODEL_INSTANCE_API}/${instanceData.id}/logs`, + filename: createFileName(instanceData.name), + downloadNotification + }); + } else { + handleChildSelect(val, instanceData); + } + }, + [handleChildSelect, instanceData] + ); return ( - - {_.map(list, (item: ModelInstanceListItem, index: number) => { - return ( -
- - - - - - {item.name} - - - - + <> + {contextHolder} +
+ + + + + + {instanceData.name} + + + + + + + + + + + {instanceData.computed_resource_claim?.total_layers !== + instanceData.computed_resource_claim?.offload_layers && ( + + + CPU:{' '} + {_.subtract( + instanceData.computed_resource_claim?.total_layers, + instanceData.computed_resource_claim?.offload_layers + ) || 0}{' '} + {intl.formatMessage({ + id: 'models.table.layers' + })} + + + + GPU:{' '} + {instanceData.computed_resource_claim?.offload_layers}{' '} + {intl.formatMessage({ + id: 'models.table.layers' + })} + - - - - - + + + {intl.formatMessage({ + id: 'models.table.cpuoffload' + })} + + + )} + {instanceData?.distributed_servers?.rpc_servers?.length && ( + - {item.computed_resource_claim?.total_layers !== - item.computed_resource_claim?.offload_layers && ( - - - CPU:{' '} - {_.subtract( - item.computed_resource_claim?.total_layers, - item.computed_resource_claim?.offload_layers - ) || 0}{' '} - {intl.formatMessage({ - id: 'models.table.layers' - })} - - - - GPU:{' '} - {item.computed_resource_claim?.offload_layers}{' '} - {intl.formatMessage({ - id: 'models.table.layers' - })} - - - } - > - + + {intl.formatMessage({ + id: 'models.table.acrossworker' + })} + + + )} + + + + + {instanceData.state && ( + + handleChildSelect('viewlog', instanceData) + } > - {intl.formatMessage({ - id: 'models.table.cpuoffload' + id: 'models.list.more.logs' })} - - - )} - {item?.distributed_servers?.rpc_servers?.length && ( - - - - {intl.formatMessage({ - id: 'models.table.acrossworker' - })} - - - )} - - - - - {item.state && ( - - handleChildSelect('viewlog', item, list) - } - > - {intl.formatMessage({ - id: 'models.list.more.logs' - })} - - ) : null - } - statusValue={{ - status: - item.state === InstanceStatusMap.Downloading && - item.download_progress === 100 - ? status[InstanceStatusMap.Running] - : (status[item.state] as any), - text: InstanceStatusMapValue[item.state], - message: - item.state === InstanceStatusMap.Downloading && - item.download_progress === 100 - ? '' - : item.state_message - }} - > - )} - - - - - {dayjs(item.created_at).format('YYYY-MM-DD HH:mm:ss')} - - - -
- - handleChildSelect(val, item, list) - } - > -
- -
-
-
- ); - })} - + + ) : null + } + statusValue={{ + status: + instanceData.state === InstanceStatusMap.Downloading && + instanceData.download_progress === 100 + ? status[InstanceStatusMap.Running] + : (status[instanceData.state] as any), + text: InstanceStatusMapValue[instanceData.state], + message: + instanceData.state === InstanceStatusMap.Downloading && + instanceData.download_progress === 100 + ? '' + : instanceData.state_message + }} + > + )} +
+ + + + {dayjs(instanceData.created_at).format('YYYY-MM-DD HH:mm:ss')} + + + +
+ +
+ +
+
+
+ ); }; export default React.memo(InstanceItem); diff --git a/src/pages/llmodels/components/instances.tsx b/src/pages/llmodels/components/instances.tsx new file mode 100644 index 00000000..fb109435 --- /dev/null +++ b/src/pages/llmodels/components/instances.tsx @@ -0,0 +1,38 @@ +import { ListItem as WorkerListItem } from '@/pages/resources/config/types'; +import { Space } from 'antd'; +import _ from 'lodash'; +import React from 'react'; +import { ModelInstanceListItem } from '../config/types'; +import '../style/instance-item.less'; +import InstanceItem from './instance-item'; + +interface InstanceItemProps { + list: ModelInstanceListItem[]; + workerList: WorkerListItem[]; + modelData?: any; + handleChildSelect: (val: string, item: ModelInstanceListItem) => void; +} + +const Instances: React.FC = ({ + list, + workerList, + modelData, + handleChildSelect +}) => { + return ( + + {_.map(list, (item: ModelInstanceListItem, index: number) => { + return ( + + ); + })} + + ); +}; +export default Instances; diff --git a/src/pages/llmodels/components/table-list.tsx b/src/pages/llmodels/components/table-list.tsx index 930030d5..58d0bec6 100644 --- a/src/pages/llmodels/components/table-list.tsx +++ b/src/pages/llmodels/components/table-list.tsx @@ -10,7 +10,6 @@ import { SealColumnProps } from '@/components/seal-table/types'; import { PageAction } from '@/config'; import HotKeys from '@/config/hotkeys'; import useBodyScroll from '@/hooks/use-body-scroll'; -import useDownloadStream from '@/hooks/use-download-stream'; import useExpandedRowKeys from '@/hooks/use-expanded-row-keys'; import useTableRowSelection from '@/hooks/use-table-row-selection'; import useTableSort from '@/hooks/use-table-sort'; @@ -71,7 +70,7 @@ import { FormData, ListItem, ModelInstanceListItem } from '../config/types'; import { useGenerateFormEditInitialValues } from '../hooks'; import DeployDropdown from './deploy-dropdown'; import DeployModal from './deploy-modal'; -import InstanceItem from './instance-item'; +import Instances from './instances'; import ModelTag from './model-tag'; import UpdateModel from './update-modal'; import ViewLogsModal from './view-logs-modal'; @@ -182,7 +181,6 @@ const Models: React.FC = ({ loadend, total }) => { - const { downloadStream } = useDownloadStream(); const { getGPUList, generateFormValues, gpuDeviceList } = useGenerateFormEditInitialValues(); const { saveScrollHeight, restoreScrollHeight } = useBodyScroll(); @@ -566,7 +564,7 @@ const Models: React.FC = ({ [onViewLogs] ); const handleDeleteInstace = useCallback( - (row: any, list: ModelInstanceListItem[]) => { + (row: any) => { modalRef.current.show({ content: 'models.instances', okText: 'common.button.delrecreate', @@ -643,23 +641,17 @@ const Models: React.FC = ({ }); } }, - [handleEdit, handleOpenPlayGround, handleDelete] + [handleEdit, handleOpenPlayGround, handleDelete, expandedRowKeys] ); const handleChildSelect = useCallback( - (val: any, row: ModelInstanceListItem, list: ModelInstanceListItem[]) => { + (val: any, row: ModelInstanceListItem) => { if (val === 'delete') { - handleDeleteInstace(row, list); + handleDeleteInstace(row); } if (val === 'viewlog') { handleViewLogs(row); } - if (val === 'download') { - downloadStream({ - url: `${MODEL_INSTANCE_API}/${row.id}/logs`, - filename: row.name - }); - } }, [handleViewLogs, handleDeleteInstace] ); @@ -667,13 +659,12 @@ const Models: React.FC = ({ const renderChildren = useCallback( (list: any, parent?: any) => { return ( - + > ); }, [workerList] diff --git a/src/pages/playground/apis/index.ts b/src/pages/playground/apis/index.ts index 691e4e75..acb638f6 100644 --- a/src/pages/playground/apis/index.ts +++ b/src/pages/playground/apis/index.ts @@ -1,6 +1,8 @@ import { request } from '@umijs/max'; -export const OPENAI_COMPATIBLE = 'v1'; +export const OPENAI_COMPATIBLE = 'v1-openai'; + +export const GPUSTACK_API = 'v1'; export const CHAT_API = `/${OPENAI_COMPATIBLE}/chat/completions`; @@ -9,7 +11,7 @@ export const EDIT_IMAGE_API = `/${OPENAI_COMPATIBLE}/images/edits`; export const EMBEDDING_API = `/${OPENAI_COMPATIBLE}/embeddings`; -export const OPENAI_MODELS = `/v1-openai/models`; +export const OPENAI_MODELS = `/${OPENAI_COMPATIBLE}/models`; export const RERANKER_API = '/rerank'; diff --git a/src/pages/playground/components/view-code-modal.tsx b/src/pages/playground/components/view-code-modal.tsx index ea42ff68..e3c67162 100644 --- a/src/pages/playground/components/view-code-modal.tsx +++ b/src/pages/playground/components/view-code-modal.tsx @@ -5,7 +5,7 @@ import { useIntl } from '@umijs/max'; import { Button, Modal } from 'antd'; import _ from 'lodash'; import React, { useMemo, useState } from 'react'; -import { OPENAI_COMPATIBLE } from '../apis'; +import { GPUSTACK_API } from '../apis'; type ViewModalProps = { systemMessage?: string; @@ -47,7 +47,7 @@ const ViewCodeModal: React.FC = (props) => { const intl = useIntl(); const [lang, setLang] = useState(langMap.shell); - const BaseURL = `${window.location.origin}/${OPENAI_COMPATIBLE}`; + const BaseURL = `${window.location.origin}/${GPUSTACK_API}`; const formatPyParams = (params: any) => { return _.keys(params).reduce((acc: string, key: string) => { @@ -70,7 +70,7 @@ const ViewCodeModal: React.FC = (props) => { const printLog = logcommand ? `print(response.${logcommand})` : ''; if (lang === langMap.shell) { - const code = `curl ${window.location.origin}/${OPENAI_COMPATIBLE}/${api} \\\n-H "Content-Type: application/json" \\\n-H "Authorization: Bearer $\{YOUR_GPUSTACK_API_KEY}" \\\n-d '${JSON.stringify( + const code = `curl ${window.location.origin}/${GPUSTACK_API}/${api} \\\n-H "Content-Type: application/json" \\\n-H "Authorization: Bearer $\{YOUR_GPUSTACK_API_KEY}" \\\n-d '${JSON.stringify( { ...parameters, ...payload diff --git a/src/pages/playground/view-code/audio.ts b/src/pages/playground/view-code/audio.ts index ef6aa15f..f4aca83f 100644 --- a/src/pages/playground/view-code/audio.ts +++ b/src/pages/playground/view-code/audio.ts @@ -1,8 +1,13 @@ -import { OPENAI_COMPATIBLE } from '../apis'; +import { GPUSTACK_API, OPENAI_COMPATIBLE } from '../apis'; import { fomatNodeJsParams, formatCurlArgs, formatPyParams } from './utils'; -export const speechToTextCode = ({ api, parameters }: Record) => { +export const speechToTextCode = ({ + api: url, + parameters +}: Record) => { const host = window.location.origin; + // replace url OPENAI_COMPATIBLE with GPUSTACK + const api = url.replace(OPENAI_COMPATIBLE, GPUSTACK_API); // ========================= Curl ========================= const curlCode = ` @@ -19,7 +24,7 @@ ${formatCurlArgs(parameters, true)}` from openai import OpenAI\n audio_file = open("audio.mp3", "rb") client = OpenAI( - base_url="${host}/${OPENAI_COMPATIBLE}", + base_url="${host}/${GPUSTACK_API}", api_key="YOUR_GPUSTACK_API_KEY" ) @@ -44,7 +49,7 @@ const OpenAI = require("openai"); const openai = new OpenAI({ "apiKey": "YOUR_GPUSTACK_API_KEY", - "baseURL": "${host}/${OPENAI_COMPATIBLE}" + "baseURL": "${host}/${GPUSTACK_API}" }); async function main() { @@ -62,8 +67,12 @@ main();`.trim(); }; }; -export const TextToSpeechCode = ({ api, parameters }: Record) => { +export const TextToSpeechCode = ({ + api: url, + parameters +}: Record) => { const host = window.location.origin; + const api = url.replace(OPENAI_COMPATIBLE, GPUSTACK_API); // ========================= Curl ========================= const curlCode = ` @@ -78,7 +87,7 @@ from pathlib import Path from openai import OpenAI\n output_file_path = Path(__file__).parent / "output.mp3" client = OpenAI( - base_url="${host}/${OPENAI_COMPATIBLE}", + base_url="${host}/${GPUSTACK_API}", api_key="YOUR_GPUSTACK_API_KEY" ) @@ -103,7 +112,7 @@ const ouptFile = path.resolve("./output.mp3"); const openai = new OpenAI({ "apiKey": "YOUR_GPUSTACK_API_KEY", - "baseURL": "${host}/${OPENAI_COMPATIBLE}" + "baseURL": "${host}/${GPUSTACK_API}" }); async function main() { diff --git a/src/pages/playground/view-code/embedding.ts b/src/pages/playground/view-code/embedding.ts index e3800db8..450dc88d 100644 --- a/src/pages/playground/view-code/embedding.ts +++ b/src/pages/playground/view-code/embedding.ts @@ -1,11 +1,12 @@ -import { OPENAI_COMPATIBLE } from '../apis'; +import { GPUSTACK_API, OPENAI_COMPATIBLE } from '../apis'; import { fomatNodeJsParams, formatCurlArgs, formatPyParams } from './utils'; export const generateEmbeddingCode = ({ - api, + api: url, parameters }: Record) => { const host = window.location.origin; + const api = url.replace(OPENAI_COMPATIBLE, GPUSTACK_API); // ========================= Curl ========================= const curlCode = ` @@ -18,7 +19,7 @@ ${formatCurlArgs(parameters, false)}`.trim(); const pythonCode = ` from openai import OpenAI\n client = OpenAI( - base_url="${host}/${OPENAI_COMPATIBLE}", + base_url="${host}/${GPUSTACK_API}", api_key="YOUR_GPUSTACK_API_KEY" ) @@ -35,7 +36,7 @@ const OpenAI = require("openai"); const openai = new OpenAI({ "apiKey": "YOUR_GPUSTACK_API_KEY", - "baseURL": "${host}/${OPENAI_COMPATIBLE}" + "baseURL": "${host}/${GPUSTACK_API}" }); async function main() { diff --git a/src/pages/playground/view-code/image.ts b/src/pages/playground/view-code/image.ts index 3ec7ccd3..3f8554fb 100644 --- a/src/pages/playground/view-code/image.ts +++ b/src/pages/playground/view-code/image.ts @@ -1,14 +1,15 @@ import _ from 'lodash'; -import { OPENAI_COMPATIBLE } from '../apis'; +import { GPUSTACK_API, OPENAI_COMPATIBLE } from '../apis'; import { fomatNodeJsParams, formatCurlArgs, formatPyParams } from './utils'; export const generateImageCode = ({ - api, + api: url, parameters, isFormdata = false, edit = false }: Record) => { const host = window.location.origin; + const api = url.replace(OPENAI_COMPATIBLE, GPUSTACK_API); // ========================= Curl ========================= let curlCode = ` @@ -45,7 +46,7 @@ print(response.json()['data'][0]['b64_json'])`.trim(); const nodeJsCode = ` const axios = require('axios'); -const url = "${host}/${OPENAI_COMPATIBLE}/images/generations"; +const url = "${host}/${GPUSTACK_API}/images/generations"; const headers = { "Content-type": "application/json", "Authorization": "Bearer $\{YOUR_GPUSTACK_API_KEY}" @@ -64,12 +65,13 @@ axios.post(url, data, { headers }).then((response) => { }; export const generateOpenaiImageCode = ({ - api, + api: url, parameters, isFormdata = false, edit = false }: Record) => { const host = window.location.origin; + const api = url.replace(OPENAI_COMPATIBLE, GPUSTACK_API); // ========================= Curl ========================= let curlCode = ` @@ -93,7 +95,7 @@ ${formatCurlArgs(_.omit(parameters, ['mask', 'image']), isFormdata)}` const pythonCode = ` from openai import OpenAI\n client = OpenAI( - base_url="${host}/${OPENAI_COMPATIBLE}", + base_url="${host}/${GPUSTACK_API}", api_key="YOUR_GPUSTACK_API_KEY" ) @@ -110,7 +112,7 @@ const OpenAI = require("openai"); const openai = new OpenAI({ "apiKey": "YOUR_GPUSTACK_API_KEY", - "baseURL": "${host}/${OPENAI_COMPATIBLE}" + "baseURL": "${host}/${GPUSTACK_API}" }); async function main() {