diff --git a/index.html b/index.html index 9ea8def296..a5aab2968a 100644 --- a/index.html +++ b/index.html @@ -32,8 +32,8 @@ NODE_ENV: 'production' } }; - globalThis.packageVersion = "24.09.0-alpha.1"; - globalThis.buildNumber = "6011"; + globalThis.packageVersion = "25.1.0-alpha.1"; + globalThis.buildNumber = "6441"; diff --git a/manifest.json b/manifest.json index ea4a8b3a3d..191cb43f3b 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "manifest_version": 9, "name": "Backend.AI Web UI", "short_name": "BackendAIWebUI", - "version": "24.09.0-alpha.1", + "version": "25.1.0-alpha.1", "start_url": "/", "display": "standalone", "background_color": "#fff", diff --git a/package.json b/package.json index 46ae97c2e4..d04c7c752c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "backend.ai-webui", "productName": "Backend.AI Desktop", - "version": "24.09.0-alpha.1", + "version": "25.1.0-alpha.1", "repository": "https://github.com/lablup/backend.ai-webui.git", "author": "Lablup Inc. ", "license": "LGPL-3.0-or-later", diff --git a/react/package.json b/react/package.json index 5bedcca981..7178df7bd4 100644 --- a/react/package.json +++ b/react/package.json @@ -1,6 +1,6 @@ { "name": "backend-ai-webui-react", - "version": "24.09.0-alpha.1", + "version": "25.1.0-alpha.1", "private": true, "dependencies": { "@ai-sdk/openai": "^1.0.11", diff --git a/react/src/App.tsx b/react/src/App.tsx index 98b76919d6..d87a54a9e5 100644 --- a/react/src/App.tsx +++ b/react/src/App.tsx @@ -64,7 +64,13 @@ const ComputeSessionList = React.lazy( () => import('./components/ComputeSessionList'), ); const AgentSummaryPage = React.lazy(() => import('./pages/AgentSummaryPage')); - +/** + * Pages for Model Player + */ +const PlaygroundPage = React.lazy( + () => import('./components/lablupTalkativotUI/LLMPlaygroundPage'), +); +const ModelStorePage = React.lazy(() => import('./pages/ModelStorePage')); interface CustomHandle { title?: string; labelKey?: string; @@ -106,17 +112,17 @@ const router = createBrowserRouter([ children: [ { path: '/', - element: , + element: , }, { //for electron dev mode path: '/build/electron-app/app/index.html', - element: , + element: , }, { //for electron prod mode path: '/app/index.html', - element: , + element: , }, { path: '/summary', @@ -359,6 +365,19 @@ const router = createBrowserRouter([ path: '*', element: <>, }, + /** + * Pages for Model Player + */ + { + path: '/playground', + handle: { labelKey: 'webui.menu.Playground' }, + Component: PlaygroundPage, + }, + { + path: '/model-store', + handle: { labelKey: 'webui.menu.ModelStore' }, + Component: ModelStorePage, + }, ], }, ]); diff --git a/react/src/components/BAIIcons/VLLMIcon.tsx b/react/src/components/BAIIcons/VLLMIcon.tsx new file mode 100644 index 0000000000..feaf304d7f --- /dev/null +++ b/react/src/components/BAIIcons/VLLMIcon.tsx @@ -0,0 +1,23 @@ +import { ReactComponent as logo } from './vllm-color.svg'; +import Icon from '@ant-design/icons'; +import { CustomIconComponentProps } from '@ant-design/icons/lib/components/Icon'; + +interface CustomIconProps + extends Omit { + size?: number; +} + +const VLLMIcon: React.FC = (props) => { + return ( + + ); +}; + +export default VLLMIcon; diff --git a/react/src/components/BAIIcons/vllm-color.svg b/react/src/components/BAIIcons/vllm-color.svg new file mode 100644 index 0000000000..54acc3de2d --- /dev/null +++ b/react/src/components/BAIIcons/vllm-color.svg @@ -0,0 +1 @@ +vLLM \ No newline at end of file diff --git a/react/src/components/ChatContent.tsx b/react/src/components/ChatContent.tsx new file mode 100644 index 0000000000..ff6f8f159c --- /dev/null +++ b/react/src/components/ChatContent.tsx @@ -0,0 +1,122 @@ +import { useTanQuery } from '../hooks/reactQueryAlias'; +import { ChatContentEndpointDetailQuery } from './__generated__/ChatContentEndpointDetailQuery.graphql'; +import { Model } from './lablupTalkativotUI/ChatUIModal'; +import LLMChatCard from './lablupTalkativotUI/LLMChatCard'; +import { ReloadOutlined } from '@ant-design/icons'; +import { Alert, Button } from 'antd'; +import graphql from 'babel-plugin-relay/macro'; +import _ from 'lodash'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useLazyLoadQuery } from 'react-relay/hooks'; + +interface ChatContentProps { + endpointId: string; + endpointUrl: string; + basePath: string; +} + +const ChatContent: React.FC = ({ + endpointId, + endpointUrl, + basePath, +}) => { + const { t } = useTranslation(); + + const { endpoint_token_list } = + useLazyLoadQuery( + graphql` + query ChatContentEndpointDetailQuery( + $endpointId: UUID! + $tokenListOffset: Int! + $tokenListLimit: Int! + ) { + endpoint_token_list( + limit: $tokenListLimit + offset: $tokenListOffset + endpoint_id: $endpointId + ) { + total_count + items { + id + token + endpoint_id + created_at + valid_until + } + } + } + `, + { + tokenListLimit: 100, + tokenListOffset: 0, + endpointId: endpointId as string, + }, + { + fetchPolicy: 'network-only', + }, + ); + + const newestValidToken = + _.orderBy(endpoint_token_list?.items, ['valid_until'], ['desc'])[0] + ?.token ?? ''; + + const { + data: modelsResult, + // error, + refetch, + } = useTanQuery<{ + data: Array; + }>({ + queryKey: ['models', endpointUrl], + queryFn: () => { + return fetch(new URL(basePath + '/models', endpointUrl).toString(), { + headers: { + Authorization: `BackendAI ${newestValidToken}`, + }, + }) + .then((res) => res.json()) + .catch((err) => { + console.log(err); + }); + }, + }); + + return ( + ({ + id: m.id, + name: m.id, + }))} + apiKey={newestValidToken} + fetchOnClient + style={{ flex: 1 }} + allowCustomModel={false} + alert={ + _.isEmpty(modelsResult?.data) && ( + } + onClick={() => { + refetch(); + }} + > + {t('button.Refresh')} + + } + /> + ) + } + modelId={modelsResult?.data?.[0].id ?? 'custom'} + modelToken={newestValidToken} + /> + ); +}; + +export default ChatContent; diff --git a/react/src/components/ImportFromHuggingFacePanel.tsx b/react/src/components/ImportFromHuggingFacePanel.tsx new file mode 100644 index 0000000000..2f3638499b --- /dev/null +++ b/react/src/components/ImportFromHuggingFacePanel.tsx @@ -0,0 +1,61 @@ +import Flex from '../components/Flex'; +import BAICard from './BAICard'; +import { App, Button, Input, theme } from 'antd'; +import type { GetProps } from 'antd'; +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +const ImportFromHuggingFacePanel: React.FC = () => { + const { t } = useTranslation(); + const { token } = theme.useToken(); + const { message } = App.useApp(); + const [search, setSearch] = useState(''); + type SearchProps = GetProps; + + const { Search } = Input; + const onSearch: SearchProps['onSearch'] = (value, _e, info) => { + // TODO: download model from hugging face by URL + setSearch(value); + }; + + return ( + + + { + message.info({ + key: 'import-from-hugging-face', + content: 'Only available for administrators.', + }); + }} + // FIXME: Temporary use hardcoded color for the button background + style={{ color: token.colorBgBase, backgroundColor: '#FF7A00' }} + > + Download + + } + size="large" + onSearch={onSearch} + /> + + + ); +}; + +export default ImportFromHuggingFacePanel; diff --git a/react/src/components/MainLayout/WebUISider.tsx b/react/src/components/MainLayout/WebUISider.tsx index 5e4eb9c145..026ad52b8a 100644 --- a/react/src/components/MainLayout/WebUISider.tsx +++ b/react/src/components/MainLayout/WebUISider.tsx @@ -16,6 +16,7 @@ import WebUILink from '../WebUILink'; import { PluginPage, WebUIPluginType } from './MainLayout'; import { ApiOutlined, + AppstoreOutlined, BarChartOutlined, CloudUploadOutlined, ControlOutlined, @@ -24,6 +25,7 @@ import { FileDoneOutlined, HddOutlined, InfoCircleOutlined, + MessageOutlined, SolutionOutlined, ToolOutlined, UserOutlined, @@ -85,67 +87,86 @@ const WebUISider: React.FC = (props) => { const primaryColors = usePrimaryColors(); const generalMenu = filterEmptyItem([ + // { + // label: {t('webui.menu.Summary')}, + // icon: , + // key: 'summary', + // }, + // { + // label: {t('webui.menu.Sessions')}, + // icon: , + // key: 'job', + // }, { - label: {t('webui.menu.Summary')}, - icon: , - key: 'summary', - }, - { - label: {t('webui.menu.Sessions')}, - icon: , - key: 'job', - }, - supportServing && { - label: {t('webui.menu.Serving')}, - icon: , - key: 'serving', - }, - { - label: {t('webui.menu.Import&Run')}, - icon: , - key: 'import', - }, - { - label: {t('webui.menu.Data&Storage')}, - icon: , - key: 'data', - }, - supportUserCommittedImage && { label: ( - - {t('webui.menu.MyEnvironments')} - + {t('webui.menu.Playground')} ), - icon: , - key: 'my-environment', + icon: , + key: 'playground', }, - !isHideAgents && { + { label: ( - - {t('webui.menu.AgentSummary')} - + {t('webui.menu.ModelStore')} ), - icon: , - key: 'agent-summary', + icon: , + key: 'model-store', }, - { + supportServing && { label: ( - {t('webui.menu.Statistics')} + {t('modelserving.menu.MyServices')} ), - icon: , - key: 'statistics', - }, - !!fasttrackEndpoint && { - label: t('webui.menu.FastTrack'), - icon: , - key: 'pipeline', - onClick: () => { - window.open(fasttrackEndpoint, '_blank', 'noopener noreferrer'); - }, + icon: , + key: 'serving', }, + // { + // label: {t('webui.menu.Import&Run')}, + // icon: , + // key: 'import', + // }, + // { + // label: ( + // {t('modelserving.menu.ModelList')} + // ), + // icon: , + // key: 'data', + // }, + // supportUserCommittedImage && { + // label: ( + // + // {t('webui.menu.MyEnvironments')} + // + // ), + // icon: , + // key: 'my-environment', + // }, + // !isHideAgents && { + // label: ( + // + // {t('webui.menu.AgentSummary')} + // + // ), + // icon: , + // key: 'agent-summary', + // }, + // { + // label: ( + // {t('webui.menu.Statistics')} + // ), + // icon: , + // key: 'statistics', + // }, + // !!fasttrackEndpoint && { + // label: t('webui.menu.FastTrack'), + // icon: , + // key: 'pipeline', + // onClick: () => { + // window.open(fasttrackEndpoint, '_blank', 'noopener noreferrer'); + // }, + // }, ]); - const adminMenu: MenuProps['items'] = [ + const adminMenu: MenuProps['items'] = []; + /*[ { label: {t('webui.menu.Users')}, icon: , @@ -168,8 +189,10 @@ const WebUISider: React.FC = (props) => { key: 'resource-policy', }, ]; + */ - const superAdminMenu: MenuProps['items'] = [ + const superAdminMenu: MenuProps['items'] = []; + /*[ { label: {t('webui.menu.Resources')}, icon: , @@ -196,9 +219,7 @@ const WebUISider: React.FC = (props) => { icon: , key: 'information', }, - ]; - - const pluginMap: Record = { + ]*/ const pluginMap: Record = { 'menuitem-user': generalMenu, 'menuitem-admin': adminMenu, 'menuitem-superadmin': superAdminMenu, @@ -255,7 +276,7 @@ const WebUISider: React.FC = (props) => { height: themeConfig?.logo?.size?.height || 24, cursor: 'pointer', }} - onClick={() => webuiNavigate(themeConfig?.logo?.href || '/summary')} + onClick={() => webuiNavigate(themeConfig?.logo?.href || '/serving')} /> } theme={currentSiderTheme} @@ -275,7 +296,7 @@ const WebUISider: React.FC = (props) => { height: themeConfig?.logo.sizeCollapsed?.height ?? 24, cursor: 'pointer', }} - onClick={() => webuiNavigate(themeConfig?.logo?.href || '/summary')} + onClick={() => webuiNavigate(themeConfig?.logo?.href || '/serving')} /> } logoTitle={themeConfig?.logo?.logoTitle || siteDescription || 'WebUI'} @@ -340,6 +361,7 @@ const WebUISider: React.FC = (props) => { ]} items={ // TODO: add plugin menu + /* currentUserRole === 'superadmin' ? [ { @@ -381,6 +403,8 @@ const WebUISider: React.FC = (props) => { }, ] : [] + */ + [] } /> diff --git a/react/src/components/ModelCardChat.tsx b/react/src/components/ModelCardChat.tsx new file mode 100644 index 0000000000..cd4728e443 --- /dev/null +++ b/react/src/components/ModelCardChat.tsx @@ -0,0 +1,111 @@ +import { useUpdatableState } from '../hooks'; +import ChatContent from './ChatContent'; +import { ModelCardChatQuery } from './__generated__/ModelCardChatQuery.graphql'; +import { Alert, Card, theme } from 'antd/lib'; +import graphql from 'babel-plugin-relay/macro'; +import _ from 'lodash'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useLazyLoadQuery } from 'react-relay'; + +// TODO: fetch endpoint list, filter endpoint by name and send it ot LLMChatCard + +interface ModelCardChatProps { + modelName?: string; + basePath?: string; +} + +const ModelCardChat: React.FC = ({ + modelName, + basePath = 'v1', +}) => { + const { t } = useTranslation(); + const { token } = theme.useToken(); + const [fetchKey, updateFetchKey] = useUpdatableState('first'); + + const { endpoint_list } = useLazyLoadQuery( + graphql` + query ModelCardChatQuery( + $offset: Int! + $limit: Int! + $filter: String + $projectID: UUID + ) { + endpoint_list( + offset: $offset + limit: $limit + project: $projectID + filter: $filter + ) { + items { + name + endpoint_id + url + model + status + } + } + } + `, + { + limit: 100, + offset: 0, + filter: `name ilike "%${modelName}%"`, + }, + { + fetchPolicy: 'network-only', + fetchKey, + }, + ); + + const healthyEndpoint = _.filter(endpoint_list?.items, (item) => { + return item?.status == 'HEALTHY'; + }); + + // FIXME: temporally render chat UI only if at least one endpoint is healthy. + return healthyEndpoint.length > 0 ? ( + + ) : ( + , + ]} + /> + ); +}; + +export default ModelCardChat; diff --git a/react/src/components/ModelCardModal.tsx b/react/src/components/ModelCardModal.tsx index 9e22386ab7..80f149b120 100644 --- a/react/src/components/ModelCardModal.tsx +++ b/react/src/components/ModelCardModal.tsx @@ -1,27 +1,33 @@ import { useBackendAIImageMetaData } from '../hooks'; +import { useUpdatableState } from '../hooks'; import BAIModal, { BAIModalProps } from './BAIModal'; import Flex from './Flex'; +import ModelCardChat from './ModelCardChat'; import ModelCloneModal from './ModelCloneModal'; +import ModelTryContent from './ModelTryContent'; import ResourceNumber from './ResourceNumber'; import { ModelCardModalFragment$key } from './__generated__/ModelCardModalFragment.graphql'; -import { BankOutlined, CopyOutlined, FileOutlined } from '@ant-design/icons'; +import { BankOutlined, FileOutlined } from '@ant-design/icons'; import { Alert, Button, Card, Col, Descriptions, + Divider, Empty, Grid, Row, Tag, Typography, + Tabs, theme, + Skeleton, } from 'antd'; import graphql from 'babel-plugin-relay/macro'; import dayjs from 'dayjs'; -import _ from 'lodash'; -import { Cog, FolderX } from 'lucide-react'; +import _, { head } from 'lodash'; +import { FolderX } from 'lucide-react'; import Markdown from 'markdown-to-jsx'; import React, { Suspense, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -29,6 +35,7 @@ import { useFragment } from 'react-relay'; interface ModelCardModalProps extends BAIModalProps { modelCardModalFrgmt?: ModelCardModalFragment$key | null; + // basePath?: string; onRequestClose: () => void; } const ModelCardModal: React.FC = ({ @@ -130,269 +137,341 @@ const ModelCardModal: React.FC = ({ , ]} > - {model_card?.error_msg ? ( - - - - - ) : ( - <> - - - {model_card?.category && ( - - {model_card?.category} - - )} - {model_card?.task && ( - }> + - {model_card?.task} - - )} - {model_card?.label && - _.map(model_card?.label, (label) => ( - - {label} - - ))} - {model_card?.license && ( - } - bordered={false} - color="geekblue" - style={{ marginRight: 0 }} + + + + + + + + + ), + }, + { + key: 'modelcard', + label: t('modelStore.ModelCard'), + children: model_card?.error_msg ? ( + + + + + ) : ( + <> + - {model_card?.license} - - )} - - - {/* */} - - - - - - - - {!!model_card?.description ? ( - <> - - {t('modelStore.Description')} - - + {model_card?.category && ( + + {model_card?.category} + + )} + {model_card?.task && ( + + {model_card?.task} + + )} + {model_card?.label && + _.map(model_card?.label, (label) => ( + + {label} + + ))} + {model_card?.license && ( + } + bordered={false} + color="geekblue" + style={{ marginRight: 0 }} + > + {model_card?.license} + + )} + + + {/* */} + {/* + - ), - }} - > - {model_card?.description} - - - - ) : null} - - {_.map( - _.filter( - _.castArray(model_card?.framework), - (v) => !_.isEmpty(v), + {t('modelStore.CloneToFolder')} + */} + + + + + + {!!model_card?.description ? ( + <> + + {t('modelStore.Description')} + + + ( + + ), + }} + > + {model_card?.description} + + + + ) : null} + + {_.map( + _.filter( + _.castArray(model_card?.framework), + (v) => !_.isEmpty(v), + ), + (framework, index) => { + const targetImageKey = framework?.replace( + /\s*\d+\s*$/, + '', + ); + const imageInfo = _.find( + metadata?.imageInfo, + (imageInfo) => + imageInfo?.name === targetImageKey, + ); + const uniqueKey = `${framework}-${index}`; + return imageInfo?.icon ? ( + + {framework + {framework} + + ) : ( + + {framework} + + ); + }, + )} + ), - (framework, index) => { - const targetImageKey = framework?.replace( - /\s*\d+\s*$/, - '', - ); - const imageInfo = _.find( - metadata?.imageInfo, - (imageInfo) => - imageInfo?.name === targetImageKey, - ); - const uniqueKey = `${framework}-${index}`; - return imageInfo?.icon ? ( - - {framework - {framework} - - ) : ( - - {framework} - - ); - }, - )} - - ), - }, - { - key: 'created', - label: t('modelStore.Created'), - children: dayjs(model_card?.created_at).format('lll'), - }, - { - key: 'last_modified', - label: t('modelStore.LastModified'), - children: dayjs(model_card?.modified_at).format('lll'), - }, - { - key: 'min_resource', - label: t('modelStore.MinResource'), - children: ( - - {model_card?.min_resource && - _.map( - JSON.parse(model_card?.min_resource), - (value, type) => { - return ( - - ); - }, - )} - - ), - }, - ]} - /> - - - {!!model_card?.readme ? ( - - - - README.md + }, + { + key: 'created', + label: t('modelStore.Created'), + children: dayjs(model_card?.created_at).format( + 'lll', + ), + }, + { + key: 'last_modified', + label: t('modelStore.LastModified'), + children: dayjs(model_card?.modified_at).format( + 'lll', + ), + }, + { + key: 'min_resource', + label: t('modelStore.MinResource'), + children: ( + + {model_card?.min_resource && + _.map( + JSON.parse(model_card?.min_resource), + (value, type) => { + return ( + + ); + }, + )} + + ), + }, + ]} + /> - } - styles={{ - body: { - padding: token.paddingLG, - overflow: 'auto', - height: screen.lg ? 'calc(100vh - 287px)' : undefined, - minHeight: 200, - }, - }} - > - {model_card?.readme || ''} - - - ) : null} - - - )} + + {!!model_card?.readme ? ( + + + + README.md + + } + styles={{ + body: { + padding: token.paddingLG, + overflow: 'auto', + height: screen.lg + ? 'calc(100vh - 287px)' + : undefined, + minHeight: 200, + }, + }} + > + {model_card?.readme || ''} + + + ) : null} + + + ), + }, + ]} + /> = ({ + modelName, + modelStorageHost, + minAIAcclResource, + title, + ...props +}) => { + const { t } = useTranslation(); + const { token } = theme.useToken(); + const baiClient = useSuspendedBackendaiClient(); + const baiRequestWithPromise = useBaiSignedRequestWithPromise(); + const currentDomain = useCurrentDomainValue(); + const currentProject = useCurrentProjectValue(); + const currentResourceGroupByProject = useCurrentResourceGroupValue(); + const [fetchKey, updateFetchKey] = useUpdatableState('first'); + const { upsertNotification } = useSetBAINotification(); + + const { data: allFolderList } = useSuspenseTanQuery({ + queryKey: ['allFolderList', fetchKey, currentProject.id], + queryFn: () => { + const search = new URLSearchParams(); + search.set('group_id', currentProject.id); + return baiRequestWithPromise({ + method: 'GET', + url: `/folders?${search.toString()}`, + }) as Promise; + }, + staleTime: 1000, + }); + + const { data: accessibleStorageHostList } = useSuspenseTanQuery({ + queryKey: ['accessibleStorageHostList', fetchKey, currentProject.id], + queryFn: () => { + return baiRequestWithPromise({ + method: 'GET', + url: `/folders/_/hosts`, + }); + }, + staleTime: 1000, + }); + + const lowestUsageHost = _.minBy( + _.map(accessibleStorageHostList?.allowed, (host) => ({ + host, + volume_info: accessibleStorageHostList?.volume_info?.[host], + })), + 'volume_info.usage.percentage', + )?.host; + + const myModelStoreList = _.filter( + allFolderList, + (vFolder) => + vFolder.ownership_type === 'user' && vFolder.usage_mode === 'model', + ); + + const filteredModelStoreList = myModelStoreList.filter((vFolder) => + vFolder.name.includes(modelName || ''), + ); + + const mutationToClone = useTanMutation< + { + bgtask_id: string; + id: string; + }, + { type?: string; title?: string; message?: string }, + { + input: any; + name: string; + } + >({ + // @ts-ignore + mutationFn: ({ input, name }: { input: any; name: string }) => { + return baiClient.vfolder.clone(input, name); + }, + }); + + const { data: availableRuntimes } = useSuspenseTanQuery<{ + runtimes: { name: string; human_readable_name: string }[]; + }>({ + queryKey: ['baiClient.modelService.runtime.list'], + queryFn: () => { + return baiClient.isManagerVersionCompatibleWith('24.03.5') + ? baiRequestWithPromise({ + method: 'GET', + url: `/services/_/runtimes`, + }) + : Promise.resolve({ + runtimes: [ + { + name: 'custom', + human_readable_name: 'Custom (Default)', + }, + ], + }); + }, + staleTime: 1000, + }); + + const mutationToCreateService = useTanMutation< + unknown, + { + message?: string; + }, + ServiceLauncherFormValue + >({ + mutationFn: (values) => { + const environ: { [key: string]: string } = {}; + if (values.envvars) { + values.envvars.forEach((v) => (environ[v.variable] = v.value)); + } + const body: ServiceCreateType = { + name: values.serviceName, + desired_session_count: values.desiredRoutingCount, + image: + `${values.environments.image?.registry}/${values.environments.image?.name}:${values.environments.image?.tag}` as string, + architecture: values.environments.image?.architecture as string, + // FIXME: hardcode this part by selected option (vLLM, NIM, Custom) + // ...getImageInfoFromInputInCreating( + // checkManualImageAllowed( + // baiClient._config.allow_manual_image_name_for_session, + // values.environments?.manual, + // ), + // values, + // ), + runtime_variant: values.runtimeVariant, + group: baiClient.current_group, // current Project Group, + domain: currentDomain, // current Domain Group, + cluster_size: values.cluster_size, + cluster_mode: values.cluster_mode, + open_to_public: values.openToPublic, + config: { + model: values.vFolderID, + model_version: 1, // FIXME: hardcoded. change it with option later + model_mount_destination: '/models', // FIXME: hardcoded. change it with option later + environ, // FIXME: hardcoded. change it with option later + scaling_group: 'nvidia-H100', // FIXME: hardcoded. change it with option later as well, values.resourceGroup, + resources: { + // FIXME: manually convert to string since server-side only allows [str,str] tuple + cpu: values.resource.cpu.toString(), + mem: values.resource.mem, + ...(values.resource.accelerator > 0 + ? { + [values.resource.acceleratorType]: + // FIXME: manually convert to string since server-side only allows [str,str] tuple + values.resource.accelerator.toString(), + } + : undefined), + }, + resource_opts: { + shmem: + compareNumberWithUnits(values.resource.mem, '4g') > 0 && + compareNumberWithUnits(values.resource.shmem, '1g') < 0 + ? '1g' + : values.resource.shmem, + }, + }, + }; + return baiSignedRequestWithPromise({ + method: 'POST', + url: '/services', + body, + client: baiClient, + }); + }, + }); + + const getServiceInputByRuntimeVariant = ( + runtimeVariant: string, + vfolderID: string, + ): ServiceLauncherFormValue => { + const model = modelName?.includes('stable-diffusion-3-medium') + ? 'stable-diffusion-3m' + : modelName; + return { + serviceName: `${model}-${generateRandomString(4)}`, + desiredRoutingCount: 1, + // FIXME: hard-coded images for vLLM, NIM, Custom + environments: { + image: { + registry: 'cr.backend.ai', + name: (() => { + if (modelName?.includes('stable-diffusion')) { + return 'testing/ngc-pytorch'; + } + switch (runtimeVariant) { + case 'nim': + return 'testing/ngc-nim'; + case 'vllm': + case 'custom': + default: + return 'testing/vllm'; + } + })(), + tag: (() => { + if (modelName?.includes('stable-diffusion')) { + return '24.07-pytorch2.4-py310-cuda12.5'; + } + switch (runtimeVariant) { + case 'vllm': + case 'custom': + default: + return '0.6.2-cuda12.1-ubuntu22.04'; + case 'nim': + return 'ngc-nim:1.0.0-llama3.8b-h100x1-fp16'; + } + })(), + architecture: 'x86_64', + base_image_name: undefined, + digest: undefined, + humanized_name: undefined, + id: undefined, + installed: undefined, + labels: undefined, + namespace: undefined, + resource_limits: undefined, + tags: undefined, + version: undefined, + }, + environment: '', + version: '', + }, + // FIXME: temporally hard-coded runtime variant + runtimeVariant: modelName?.includes('stable-diffusion') + ? 'custom' + : runtimeVariant, + cluster_size: 1, + cluster_mode: 'single-node', + openToPublic: true, + resourceGroup: currentResourceGroupByProject as string, + resource: { + cpu: 4, + mem: '32g', + accelerator: minAIAcclResource, + acceleratorType: 'cuda.shares', + shmem: '1g', + }, + vFolderID: vfolderID, // TODO: add cloned folder result + modelMountDestination: '/models', + modelDefinitionPath: '', + vfoldersAliasMap: {}, + envvars: [], + enabledAutomaticShmem: false, + }; + }; + + const cloneOrCreateModelService = (runtimeVariant: string) => { + let modelId = 'vllm-model'; + switch (runtimeVariant) { + case 'vllm': + default: + break; + case 'nim': + case 'custom': + modelId = 'custom'; + break; + } + if (filteredModelStoreList.length < 1) { + mutationToClone.mutate( + { + input: { + // FIXME: hardcoded + cloneable: true, + permission: 'wd', // write-delete permission + target_host: modelStorageHost, // lowestUsageHost, // clone to accessible and lowest usage storage host + target_name: `${modelName}-1`, + usage_mode: 'model', + }, + name: `${modelName}`, + }, + { + onSuccess: (data) => { + upsertNotification({ + key: `modelStore.clone. + ${modelName}-1`, + open: true, + onClose: () => { + upsertNotification({ + key: `modelStore.clone. + ${modelName}-1`, + open: false, + backgroundTask: { + percent: 0, + status: 'pending', + }, + to: '', + toText: '', + }); + }, + extraDescription: '(1 / 2)', + backgroundTask: { + status: 'pending', + percent: 50, + taskId: data.bgtask_id, + statusDescriptions: { + pending: 'Downloading model is in progress...', // t('data.folders.FolderClonePending'), + resolved: 'Successfully downloaded model.', // t('data.folders.FolderCloned'), + rejected: + 'Downloading model failed. Please check storage quota and try again.', // t('data.folders.FolderCloneFailed'), + }, + onResolve: () => { + mutationToCreateService.mutate( + getServiceInputByRuntimeVariant('vllm', `${modelName}-1`), + { + onSuccess: (result: any) => { + upsertNotification({ + key: result?.endpoint_id, + open: true, + message: 'Starting model service...', + duration: 0, + backgroundTask: { + promise: new Promise((resolve, reject) => { + let progress = 0; + const interval = setInterval(async () => { + try { + progress += 5; + upsertNotification({ + key: result?.endpoint_id, + backgroundTask: { + status: 'pending', + percent: progress > 100 ? 100 : progress, + }, + }); + const routingStatus = + await baiRequestWithPromise({ + method: 'GET', + url: `/services/${result?.endpoint_id}`, + }); + if (routingStatus.active_routes.length > 0) { + clearInterval(interval); + return resolve(); + } + if (progress >= 100) { + throw new Error( + 'Model service failed to start. Please check the service status.', + ); + } + } catch (error) { + clearInterval(interval); + return reject(); + } + }, 5000); + }), + statusDescriptions: { + pending: 'Model service is starting...', + resolved: 'Model service is now ready!', + rejected: + 'Model service failed to start. Please check the service status.', + }, + status: 'pending', + percent: 0, + onResolve: () => { + upsertNotification({ + duration: 0, + key: result?.endpoint_id, + backgroundTask: { + status: 'resolved', + percent: 100, + }, + message: '', + to: `/playground?endpointId=${result?.endpoint_id}&modelId=vllm-model`, // PATH to playground page + toText: 'Play your model now!', + }); + }, + onFailed: () => { + upsertNotification({ + key: result?.endpoint_id, + duration: 0, + backgroundTask: { + status: 'rejected', + percent: 99, + }, + message: '', + to: `/serving/${result?.endpoint_id}`, + toText: 'Go to service detail page', + }); + }, + }, + }); + }, + onError: () => { + // TODO: show a notification to go to service detail page + }, + }, + ); + }, + }, + }); + }, + onError: () => { + // TODO: show a notification for clone error + }, + }, + ); + } else { + mutationToCreateService.mutate( + getServiceInputByRuntimeVariant( + runtimeVariant, + filteredModelStoreList[0].id, + ), + { + onSuccess: (result: any) => { + upsertNotification({ + key: result?.endpoint_id, + open: true, + message: 'Starting model service...', + duration: 0, + backgroundTask: { + promise: new Promise((resolve, reject) => { + let progress = 0; + const interval = setInterval(async () => { + try { + progress += 5; + upsertNotification({ + key: result?.endpoint_id, + backgroundTask: { + status: 'pending', + percent: progress > 100 ? 100 : progress, + }, + }); + const routingStatus = await baiRequestWithPromise({ + method: 'GET', + url: `/services/${result?.endpoint_id}`, + }); + if (routingStatus.active_routes.length > 0) { + clearInterval(interval); + return resolve(); + } + if (progress >= 100) { + throw new Error( + 'Model service failed to start. Please check the service status.', + ); + } + } catch (error) { + clearInterval(interval); + return reject(); + } + }, 5000); + }), + statusDescriptions: { + pending: 'Model service is starting...', + resolved: 'Model service is now ready!', + rejected: + 'Model service failed to start. Please check the service status.', + }, + status: 'pending', + percent: 0, + onResolve: () => { + upsertNotification({ + duration: 0, + key: result?.endpoint_id, + backgroundTask: { + status: 'resolved', + percent: 100, + }, + message: '', + to: `/playground?endpointId=${result?.endpoint_id}&modelId=${modelId}`, // PATH to playground page + toText: 'Play your model now!', + }); + }, + onFailed: () => { + upsertNotification({ + duration: 0, + key: result?.endpoint_id, + backgroundTask: { + status: 'rejected', + percent: 99, + }, + message: '', + to: `/serving/${result?.endpoint_id}`, + toText: 'Go to service detail page', + }); + }, + }, + }); + }, + onError: () => { + // TODO: show a notification to go to service detail page + }, + }, + ); + } + }; + + return ( + <> + {title && ( + + {title} + + )} + + + + + ); +}; + +export default ModelTryContent; diff --git a/react/src/components/lablupTalkativotUI/ChatMessage.tsx b/react/src/components/lablupTalkativotUI/ChatMessage.tsx index 6f3104e68f..ae429fc186 100644 --- a/react/src/components/lablupTalkativotUI/ChatMessage.tsx +++ b/react/src/components/lablupTalkativotUI/ChatMessage.tsx @@ -103,8 +103,9 @@ const ChatMessage: React.FC<{ direction="column" style={{ borderRadius: token.borderRadius, - borderColor: token.colorBorderSecondary, - borderWidth: token.lineWidth, + border: _.isEmpty(message.experimental_attachments) + ? `${token.lineWidth}px solid ${token.colorBorderSecondary}` + : 'none', padding: '1em', paddingTop: 0, paddingBottom: 0, diff --git a/react/src/components/lablupTalkativotUI/LLMChatCard.tsx b/react/src/components/lablupTalkativotUI/LLMChatCard.tsx index 8ccb89afcf..d7305f7fa7 100644 --- a/react/src/components/lablupTalkativotUI/LLMChatCard.tsx +++ b/react/src/components/lablupTalkativotUI/LLMChatCard.tsx @@ -268,6 +268,7 @@ const LLMChatCard: React.FC = ({ width: '100%', display: 'flex', flexDirection: 'column', + minHeight: '50vh', }} styles={{ body: { @@ -292,7 +293,7 @@ const LLMChatCard: React.FC = ({ = ({ ...props }) => { const { token } = theme.useToken(); const { t } = useTranslation(); // Set the initial list to have two items - const { list, remove, getKey, push } = useDynamicList(['0', '1']); + const { list, remove, getKey, push } = useDynamicList(['0']); const [isSynchronous, setSynchronous] = useState(false); diff --git a/react/src/components/lablupTalkativotUI/VirtualChatMessageList.tsx b/react/src/components/lablupTalkativotUI/VirtualChatMessageList.tsx index 755436f90c..df802971be 100644 --- a/react/src/components/lablupTalkativotUI/VirtualChatMessageList.tsx +++ b/react/src/components/lablupTalkativotUI/VirtualChatMessageList.tsx @@ -59,7 +59,7 @@ const VirtualChatMessageList: React.FC = ({ } enableExtraHover={m.role === 'user'} extra={ - m.role !== 'user' ? ( + m.role !== 'user' && m.content ? ( void; + onFailed?: (notification: NotificationState) => void; taskId?: string; percent?: number; status: 'pending' | 'rejected' | 'resolved'; @@ -71,6 +73,7 @@ export const useBAINotificationEffect = () => { listeningPromiseKeysRef.current.push(notification.key); notification.backgroundTask?.promise .then(() => { + notification?.backgroundTask?.onResolve?.(notification); upsertNotification({ key: notification.key, // message: notification.message, @@ -79,10 +82,11 @@ export const useBAINotificationEffect = () => { backgroundTask: { status: 'resolved', }, - duration: CLOSING_DURATION, + duration: 0, // CLOSING_DURATION, }); }) .catch((e) => { + notification?.backgroundTask?.onFailed?.(notification); upsertNotification({ key: notification.key, description: @@ -148,6 +152,8 @@ export const useBAINotificationEffect = () => { listeningTaskIdsRef.current, notification.backgroundTask?.taskId, ); + notification?.backgroundTask?.onResolve?.(notification); + notification?.backgroundTask?.onFailed?.(notification); sse.close(); if (_.startsWith(_.toString(notification.key), 'image-rescan:')) { const event = new CustomEvent('image-rescanned'); @@ -164,6 +170,8 @@ export const useBAINotificationEffect = () => { }); }); const failHandler = (e: any) => { + notification?.backgroundTask?.onResolve?.(notification); + notification?.backgroundTask?.onFailed?.(notification); listeningTaskIdsRef.current = _.without( listeningTaskIdsRef.current, notification.backgroundTask?.taskId, @@ -188,6 +196,8 @@ export const useBAINotificationEffect = () => { sse.addEventListener('bgtask_failed', failHandler); sse.addEventListener('task_failed', (e) => { const data = JSON.parse(e['data']); + notification?.backgroundTask?.onResolve?.(notification); + notification?.backgroundTask?.onFailed?.(notification); upsertNotification({ key: notification.key, message: notification.message, @@ -204,6 +214,8 @@ export const useBAINotificationEffect = () => { }); sse.addEventListener('bgtask_cancelled', (e) => { + notification?.backgroundTask?.onResolve?.(notification); + notification?.backgroundTask?.onFailed?.(notification); listeningTaskIdsRef.current = _.without( listeningTaskIdsRef.current, notification.backgroundTask?.taskId, diff --git a/react/src/pages/ModelStoreListPage.tsx b/react/src/pages/ModelStoreListPage.tsx index 7235ade9e0..7e964435d2 100644 --- a/react/src/pages/ModelStoreListPage.tsx +++ b/react/src/pages/ModelStoreListPage.tsx @@ -1,4 +1,5 @@ import Flex from '../components/Flex'; +import ImportFromHuggingFacePanel from '../components/ImportFromHuggingFacePanel'; import ModelCardModal from '../components/ModelCardModal'; import TextHighlighter from '../components/TextHighlighter'; import { ModelCardModalFragment$key } from '../components/__generated__/ModelCardModalFragment.graphql'; @@ -15,6 +16,7 @@ import { Tag, theme, Typography, + Image, } from 'antd'; import { createStyles } from 'antd-style'; import graphql from 'babel-plugin-relay/macro'; @@ -43,7 +45,13 @@ const ModelStoreListPage: React.FC = () => { const [selectedCategories, setSelectedCategories] = useState([]); const [selectedTasks, setSelectedTasks] = useState([]); const [selectedLabels, setSelectedLabels] = useState([]); - + const [paginationState] = useState<{ + current: number; + pageSize: number; + }>({ + current: 1, + pageSize: 100, + }); const { styles } = useStyles(); const [currentModelInfo, setCurrentModelInfo] = @@ -71,6 +79,10 @@ const ModelStoreListPage: React.FC = () => { license min_resource error_msg @since(version: "24.03.7") + vfolder { + id + name + } ...ModelCardModalFragment } } @@ -91,8 +103,6 @@ const ModelStoreListPage: React.FC = () => { }, ); - // const filterInfo = _.map - const fieldsValues = useMemo(() => { const result: { task: string[]; @@ -126,6 +136,7 @@ const ModelStoreListPage: React.FC = () => { gap="lg" style={{ padding: token.paddingLG }} > + { edge?.node) .filter((info) => { @@ -229,6 +240,24 @@ const ModelStoreListPage: React.FC = () => { _.includes(selectedTasks, info?.task)) && passSearchFilter ); + }) + .sort((a, b) => { + const specialNames = [ + 'gemma-2-27b-it', + 'stable-diffusion-3-medium', + ]; + const aIndex = specialNames.indexOf(a?.name || ''); + const bIndex = specialNames.indexOf(b?.name || ''); + + if (aIndex !== -1 && bIndex !== -1) { + return aIndex - bIndex; + } else if (aIndex !== -1) { + return -1; + } else if (bIndex !== -1) { + return 1; + } else { + return 0; + } })} renderItem={(item) => ( { style={{ height: '100%', }} - size="small" - > - - {item?.description && ( - - - {item?.description} - - - )} - {item?.category && ( - - - {item?.category} - - - )} - {item?.task && ( - - - {item?.task} - - - )} - {item?.label && - _.map(item?.label, (label) => ( - + children={ + + + + - {label} + {item?.description} - - ))} - {item?.error_msg && ( - - {item.error_msg} - - } - type="error" - showIcon - /> - )} - - + + + + {item?.category && ( + + + {item?.category} + + + )} + {item?.task && ( + + + {item?.task} + + + )} + {item?.label && + _.map(item?.label, (label) => ( + + + {label} + + + ))} + {item?.error_msg && ( + + {item.error_msg} + + } + type="error" + showIcon + /> + )} + + + } + > )} /> diff --git a/react/src/pages/ModelStorePage.tsx b/react/src/pages/ModelStorePage.tsx new file mode 100644 index 0000000000..9ce3c38e8e --- /dev/null +++ b/react/src/pages/ModelStorePage.tsx @@ -0,0 +1,17 @@ +import { Skeleton, theme } from 'antd'; +import React, { Suspense } from 'react'; + +const ModelStoreListPage = React.lazy(() => import('./ModelStoreListPage')); +const ModelStorePage: React.FC = () => { + const { token } = theme.useToken(); + + return ( + } + > + + + ); +}; + +export default ModelStorePage; diff --git a/react/src/pages/Page401.tsx b/react/src/pages/Page401.tsx index ca83319b92..920d3e9bc4 100644 --- a/react/src/pages/Page401.tsx +++ b/react/src/pages/Page401.tsx @@ -38,7 +38,7 @@ const Page401 = () => { diff --git a/react/src/pages/Page404.tsx b/react/src/pages/Page404.tsx index 63c5a2b1a0..c8726be068 100644 --- a/react/src/pages/Page404.tsx +++ b/react/src/pages/Page404.tsx @@ -38,7 +38,7 @@ const Page404 = () => { diff --git a/react/src/pages/ServingPage.tsx b/react/src/pages/ServingPage.tsx index 78a89a85e0..f77c78f82b 100644 --- a/react/src/pages/ServingPage.tsx +++ b/react/src/pages/ServingPage.tsx @@ -49,11 +49,6 @@ const ServingPage: React.FC = ({ ...props }) => { return ( { - setCurTabKey(key as TabKey); - }} - tabList={tabList} styles={{ body: { padding: 0, @@ -62,12 +57,13 @@ const ServingPage: React.FC = ({ ...props }) => { }, }} > - {curTabKey === 'services' ? ( - } - > - - + } + > + + + {/* {curTabKey === 'services' ? ( + ) : null} {curTabKey === 'chatting' && baiClient._config.enableLLMPlayground ? ( = ({ ...props }) => { > - ) : null} + ) : null} */} ); diff --git a/react/src/pages/VFolderListPage.tsx b/react/src/pages/VFolderListPage.tsx index f3d8f10069..d6ea1f9a25 100644 --- a/react/src/pages/VFolderListPage.tsx +++ b/react/src/pages/VFolderListPage.tsx @@ -27,16 +27,14 @@ const StorageStatusPanelFallback = React.lazy(() => ); type TabKey = - | 'general' - | 'data' - | 'automount' - | 'model' - | 'model-store' - | 'trash-bin'; + // | 'general' + // | 'data' + // | 'automount' + 'model' | 'model-store' | 'trash-bin'; interface VFolderListPageProps {} -const tabParam = withDefault(StringParam, 'general'); +const tabParam = withDefault(StringParam, 'model-store'); const VFolderListPage: React.FC = (props) => { const { t } = useTranslation(); @@ -87,6 +85,7 @@ const VFolderListPage: React.FC = (props) => { }[curTabKey]; const tabList = filterEmptyItem([ + /* { key: 'general', tab: t('data.Folders'), @@ -99,6 +98,7 @@ const VFolderListPage: React.FC = (props) => { key: 'automount', tab: t('data.AutomountFolders'), }, + */ { key: 'model', tab: t('data.Models'), diff --git a/resources/i18n/de.json b/resources/i18n/de.json index 91f3e3b6e3..4029120b31 100644 --- a/resources/i18n/de.json +++ b/resources/i18n/de.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "Geben sie ihre E-Mail Adresse ein", "DisMatchUserEmail": "Die E-Mail-Adresse des Benutzers stimmt nicht überein", "MyEnvironments": "Meine Umgebungen", - "ResourcePolicy": "Ressourcenpolitik" + "ResourcePolicy": "Ressourcenpolitik", + "Playground": "Spielplatz", + "ModelStore": "Modellladen" }, "YouAreOffline": "Offline: Keine Verbindung zu einem Netzwerk.", "YouAreOnline": "Sie sind jetzt online", @@ -1705,7 +1707,10 @@ "category": "Kategorie", "CloneToFolder": "In einen Ordner klonen", "FolderAlreadyExists": "Ein Ordner mit diesem Namen existiert bereits.", - "FinetuneModel": "Feinabstimmung des Modells" + "FinetuneModel": "Feinabstimmung des Modells", + "Experience": "Erfahrung", + "ModelCard": "Modellkarte", + "ImportFromHuggingFace": "Import aus Hugging Face" }, "table": { "SettingTable": "Sitzordnung bei Tisch", @@ -1756,5 +1761,12 @@ "hour": "Stunde", "day": "day", "week": "Woche" + }, + "modelserving": { + "menu": { + "ModelServices": "Modelldienstleistungen", + "ModelList": "Modellliste", + "MyServices": "Meine Dienste" + } } } diff --git a/resources/i18n/el.json b/resources/i18n/el.json index 5c7c778246..a1a1e3cbda 100644 --- a/resources/i18n/el.json +++ b/resources/i18n/el.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "Συμπληρώστε το email σας", "DisMatchUserEmail": "Το email χρήστη δεν ταιριάζει", "MyEnvironments": "Τα περιβάλλοντα μου", - "ResourcePolicy": "Πολιτική Πόρων" + "ResourcePolicy": "Πολιτική Πόρων", + "Playground": "Παιδική χαρά", + "ModelStore": "Κατάστημα μοντέλων" }, "YouAreOffline": "Offline: Δεν είναι συνδεδεμένος σε κανένα δίκτυο.", "YouAreOnline": "Είστε πλέον συνδεδεμένοι", @@ -1705,7 +1707,10 @@ "category": "Κατηγορία", "CloneToFolder": "Κλωνοποίηση σε φάκελο", "FolderAlreadyExists": "Ένας φάκελος με αυτό το όνομα υπάρχει ήδη.", - "FinetuneModel": "Μοντέλο Finetune" + "FinetuneModel": "Μοντέλο Finetune", + "Experience": "Εμπειρία", + "ModelCard": "Κάρτα μοντέλου", + "ImportFromHuggingFace": "Εισαγωγή από το πρόσωπο που αγκαλιάζει" }, "table": { "SettingTable": "Ρύθμιση πίνακα", @@ -1756,5 +1761,12 @@ "hour": "ώρα", "day": "day", "week": "εβδομάδα" + }, + "modelserving": { + "menu": { + "ModelServices": "Υπηρεσίες μοντέλου", + "ModelList": "Λίστα μοντέλων", + "MyServices": "Οι υπηρεσίες μου" + } } } diff --git a/resources/i18n/en.json b/resources/i18n/en.json index a861332a66..3ec31463aa 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -64,7 +64,9 @@ "InvalidBlankEmail": "Enter your e-mail", "DisMatchUserEmail": "User E-mail does not match", "MyEnvironments": "My Environments", - "ResourcePolicy": "Resource Policy" + "ResourcePolicy": "Resource Policy", + "Playground": "Playground", + "ModelStore": "Model Store" }, "YouAreOffline": "Offline: Not connected to any networks.", "YouAreOnline": "You are now online", @@ -565,7 +567,8 @@ "Expand": "Expand", "Clear": "Clear", "Apply": "Apply", - "CopySomething": "Copy {{name}}" + "CopySomething": "Copy {{name}}", + "GoBackToModelServicesPage": "Go back to model services page" }, "agent": { "Endpoint": "Endpoint", @@ -1713,7 +1716,10 @@ "category": "Category", "CloneToFolder": "Clone to a folder", "FolderAlreadyExists": "A folder with this name already exists.", - "FinetuneModel": "Finetune Model" + "FinetuneModel": "Finetune Model", + "Experience": "Experience", + "ModelCard": "Model Card", + "ImportFromHuggingFace": "Import From Hugging Face" }, "tourguide": { "NeoSessionLauncher": { @@ -1760,5 +1766,12 @@ "hour": "hour", "day": "day", "week": "week" + }, + "modelserving": { + "menu": { + "ModelServices": "Model Services", + "ModelList": "Model List", + "MyServices": "My Services" + } } } diff --git a/resources/i18n/es.json b/resources/i18n/es.json index 128622e793..1e19f87494 100644 --- a/resources/i18n/es.json +++ b/resources/i18n/es.json @@ -1674,7 +1674,9 @@ "InvalidBlankEmail": "Introduce tu correo electrónico", "DisMatchUserEmail": "El correo electrónico del usuario no coincide", "MyEnvironments": "Mis ambientes", - "ResourcePolicy": "Política de recursos" + "ResourcePolicy": "Política de recursos", + "Playground": "Patio de juegos", + "ModelStore": "Tienda de modelos" }, "NetworkSoftTimeout": "El servidor está tardando más en responder. Por favor, espere un momento" }, @@ -1707,7 +1709,10 @@ "category": "Categoría", "CloneToFolder": "Clonar en una carpeta", "FolderAlreadyExists": "Ya existe una carpeta con este nombre.", - "FinetuneModel": "Modelo de ajuste fino" + "FinetuneModel": "Modelo de ajuste fino", + "Experience": "Experiencia", + "ModelCard": "Tarjeta modelo", + "ImportFromHuggingFace": "Importar desde la cara abrazada" }, "table": { "SettingTable": "Ajuste de la tabla", @@ -1758,5 +1763,12 @@ "hour": "hora", "day": "day", "week": "semana" + }, + "modelserving": { + "menu": { + "ModelServices": "Servicios modelo", + "ModelList": "Lista de modelos", + "MyServices": "Mis servicios" + } } } diff --git a/resources/i18n/fi.json b/resources/i18n/fi.json index bb7af91ce5..9c0d361aff 100644 --- a/resources/i18n/fi.json +++ b/resources/i18n/fi.json @@ -1672,7 +1672,9 @@ "InvalidBlankEmail": "Syötä sähköpostiosoitteesi", "DisMatchUserEmail": "Käyttäjän sähköpostiosoite ei täsmää", "MyEnvironments": "Omat ympäristöt", - "ResourcePolicy": "Resurssipolitiikka" + "ResourcePolicy": "Resurssipolitiikka", + "Playground": "Leikkikenttä", + "ModelStore": "Mallikauppa" }, "NetworkSoftTimeout": "Palvelimen vastaus kestää kauemmin. Odota hetki" }, @@ -1705,7 +1707,10 @@ "Version": "Versio", "CloneToFolder": "Kloonaa kansioon", "FolderAlreadyExists": "Tämän niminen kansio on jo olemassa.", - "FinetuneModel": "Finetune malli" + "FinetuneModel": "Finetune malli", + "Experience": "Kokea", + "ModelCard": "Mallikortti", + "ImportFromHuggingFace": "Tuo halaavista kasvoista" }, "table": { "SettingTable": "Kattaus", @@ -1755,5 +1760,12 @@ "hour": "tunnin", "day": "day", "week": "viikko" + }, + "modelserving": { + "menu": { + "ModelServices": "Mallipalvelut", + "ModelList": "Malliluettelo", + "MyServices": "Omat palvelut" + } } } diff --git a/resources/i18n/fr.json b/resources/i18n/fr.json index 3b48649e92..379a8fc0dc 100644 --- a/resources/i18n/fr.json +++ b/resources/i18n/fr.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "Entrer votre Email", "DisMatchUserEmail": "L'e-mail de l'utilisateur ne correspond pas", "MyEnvironments": "Mes environnements", - "ResourcePolicy": "Politique des ressources" + "ResourcePolicy": "Politique des ressources", + "Playground": "Aire de jeux", + "ModelStore": "Magasin de modèles" }, "YouAreOffline": "Hors ligne : N'est connecté à aucun réseau.", "YouAreOnline": "Vous êtes maintenant en ligne", @@ -1705,7 +1707,10 @@ "category": "Catégorie", "CloneToFolder": "Cloner dans un dossier", "FolderAlreadyExists": "Un dossier portant ce nom existe déjà.", - "FinetuneModel": "Modèle de réglage fin" + "FinetuneModel": "Modèle de réglage fin", + "Experience": "Expérience", + "ModelCard": "Carte modèle", + "ImportFromHuggingFace": "Importer depuis un visage câlin" }, "table": { "SettingTable": "Paramètre de table", @@ -1756,5 +1761,12 @@ "hour": "heure", "day": "day", "week": "semaine" + }, + "modelserving": { + "menu": { + "ModelServices": "Services de modèles", + "ModelList": "Liste des modèles", + "MyServices": "Mes prestations" + } } } diff --git a/resources/i18n/id.json b/resources/i18n/id.json index c55d137862..7ab792fb71 100644 --- a/resources/i18n/id.json +++ b/resources/i18n/id.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "Masukkan email Anda", "DisMatchUserEmail": "Email Pengguna tidak cocok", "MyEnvironments": "Lingkungan Saya", - "ResourcePolicy": "Kebijakan Sumber Daya" + "ResourcePolicy": "Kebijakan Sumber Daya", + "Playground": "Tempat bermain", + "ModelStore": "Toko Model" }, "YouAreOffline": "Offline: Tidak terhubung ke jaringan apa pun.", "YouAreOnline": "Anda sekarang online", @@ -1705,7 +1707,10 @@ "category": "Kategori", "CloneToFolder": "Mengkloning ke folder", "FolderAlreadyExists": "Folder dengan nama ini sudah ada.", - "FinetuneModel": "Penyempurnaan Model" + "FinetuneModel": "Penyempurnaan Model", + "Experience": "Pengalaman", + "ModelCard": "Kartu Model", + "ImportFromHuggingFace": "Impor Dari Memeluk Wajah" }, "table": { "SettingTable": "Penataan meja", @@ -1756,5 +1761,12 @@ "hour": "jam", "day": "day", "week": "pekan" + }, + "modelserving": { + "menu": { + "ModelServices": "Layanan Model", + "ModelList": "Daftar Model", + "MyServices": "Layanan Saya" + } } } diff --git a/resources/i18n/it.json b/resources/i18n/it.json index fd8f072619..4965ea624b 100644 --- a/resources/i18n/it.json +++ b/resources/i18n/it.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "Inserisci il tuo indirizzo email", "DisMatchUserEmail": "L'e-mail dell'utente non corrisponde", "MyEnvironments": "I miei ambienti", - "ResourcePolicy": "Politica delle risorse" + "ResourcePolicy": "Politica delle risorse", + "Playground": "Parco giochi", + "ModelStore": "Negozio di modelli" }, "YouAreOffline": "Non in linea: Non è collegato ad alcuna rete.", "YouAreOnline": "Ora sei online", @@ -1705,7 +1707,10 @@ "category": "Categoria", "CloneToFolder": "Clonare in una cartella", "FolderAlreadyExists": "Una cartella con questo nome esiste già.", - "FinetuneModel": "Modello di messa a punto" + "FinetuneModel": "Modello di messa a punto", + "Experience": "Esperienza", + "ModelCard": "Scheda modello", + "ImportFromHuggingFace": "Importa da viso abbracciato" }, "table": { "SettingTable": "Impostazione della tabella", @@ -1756,5 +1761,12 @@ "hour": "ora", "day": "day", "week": "settimana" + }, + "modelserving": { + "menu": { + "ModelServices": "Servizi modello", + "ModelList": "Elenco dei modelli", + "MyServices": "I miei servizi" + } } } diff --git a/resources/i18n/ja.json b/resources/i18n/ja.json index a1cc356da1..73f936fe9f 100644 --- a/resources/i18n/ja.json +++ b/resources/i18n/ja.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "メールアドレスを入力", "DisMatchUserEmail": "ユーザーの電子メールが一致しません", "MyEnvironments": "私の環境", - "ResourcePolicy": "リソースポリシー" + "ResourcePolicy": "リソースポリシー", + "Playground": "遊び場", + "ModelStore": "模型店" }, "YouAreOffline": "オフライン:どのネットワークにも接続されていない。", "YouAreOnline": "あなたは今オンラインです", @@ -1705,7 +1707,10 @@ "category": "カテゴリー", "CloneToFolder": "フォルダへのクローン", "FolderAlreadyExists": "この名前のフォルダはすでに存在する。", - "FinetuneModel": "ファインチューンモデル" + "FinetuneModel": "ファインチューンモデル", + "Experience": "経験", + "ModelCard": "モデルカード", + "ImportFromHuggingFace": "ハグフェイスからインポート" }, "table": { "SettingTable": "テーブルセッティング", @@ -1756,5 +1761,12 @@ "hour": "時間", "day": "日", "week": "週" + }, + "modelserving": { + "menu": { + "ModelServices": "モデルサービス", + "ModelList": "機種一覧", + "MyServices": "私のサービス" + } } } diff --git a/resources/i18n/ko.json b/resources/i18n/ko.json index dc783239f0..2e9c109759 100644 --- a/resources/i18n/ko.json +++ b/resources/i18n/ko.json @@ -65,7 +65,9 @@ "InvalidBlankPassword": "패스워드를 입력하세요.", "DisMatchUserEmail": "사용자 E-mail이 일치하지 않습니다.", "MyEnvironments": "나의 실행 환경", - "ResourcePolicy": "자원 정책" + "ResourcePolicy": "자원 정책", + "Playground": "플레이그라운드", + "ModelStore": "모델 스토어" }, "YouAreOffline": "오프라인: 네트워크에 연결되어 있지 않습니다.", "YouAreOnline": "온라인 상태가 되었습니다", @@ -1707,7 +1709,10 @@ "CloneInfo": "새로운 사용자 폴더로 복제됩니다.", "CloneToFolder": "폴더로 복제", "FolderAlreadyExists": "같은 이름의 폴더가 존재합니다.", - "FinetuneModel": "모델 파인튜닝" + "FinetuneModel": "모델 파인튜닝", + "Experience": "사용해보기", + "ModelCard": "모델 카드", + "ImportFromHuggingFace": "Hugging Face 에서 가져오기" }, "table": { "SettingTable": "테이블 설정", @@ -1758,5 +1763,12 @@ "hour": "시간", "day": "일", "week": "주" + }, + "modelserving": { + "menu": { + "ModelServices": "모델 서비스", + "ModelList": "모델 리스트", + "MyServices": "실행중인 서비스" + } } } diff --git a/resources/i18n/mn.json b/resources/i18n/mn.json index f47c018975..3cd313750e 100644 --- a/resources/i18n/mn.json +++ b/resources/i18n/mn.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "Имэйлээ оруулна уу", "DisMatchUserEmail": "Хэрэглэгчийн имэйл таарахгүй байна", "MyEnvironments": "Миний орчин", - "ResourcePolicy": "Нөөцийн бодлого" + "ResourcePolicy": "Нөөцийн бодлого", + "Playground": "Тоглоомын талбай", + "ModelStore": "Загварын дэлгүүр" }, "YouAreOffline": "Офлайн: Ямар ч сүлжээнд холбогдоогүй.", "YouAreOnline": "Та одоо онлайн байна", @@ -1705,7 +1707,10 @@ "category": "Ангилал", "CloneToFolder": "Хавтас руу клон хийх", "FolderAlreadyExists": "Ийм нэртэй хавтас аль хэдийн байна.", - "FinetuneModel": "Finetune загвар" + "FinetuneModel": "Finetune загвар", + "Experience": "Туршлага", + "ModelCard": "Загварын карт", + "ImportFromHuggingFace": "Тэврэх нүүрээс импортлох" }, "table": { "SettingTable": "Хүснэгтийн тохиргоо", @@ -1756,5 +1761,12 @@ "hour": "цаг", "day": "day", "week": "долоо хоног" + }, + "modelserving": { + "menu": { + "ModelServices": "Загвар үйлчилгээ", + "ModelList": "Загварын жагсаалт", + "MyServices": "Миний үйлчилгээ" + } } } diff --git a/resources/i18n/ms.json b/resources/i18n/ms.json index 3a23655c01..7b2e539944 100644 --- a/resources/i18n/ms.json +++ b/resources/i18n/ms.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "Masukkan emel anda", "DisMatchUserEmail": "E-mel Pengguna tidak sepadan", "MyEnvironments": "Persekitaran Saya", - "ResourcePolicy": "Dasar Sumber" + "ResourcePolicy": "Dasar Sumber", + "Playground": "Taman permainan", + "ModelStore": "Kedai Model" }, "YouAreOffline": "Luar talian: Tidak disambungkan ke mana-mana rangkaian.", "YouAreOnline": "Anda kini dalam talian", @@ -1705,7 +1707,10 @@ "category": "kategori", "CloneToFolder": "Klon ke folder", "FolderAlreadyExists": "Folder dengan nama ini sudah wujud.", - "FinetuneModel": "Model Finetune" + "FinetuneModel": "Model Finetune", + "Experience": "Pengalaman", + "ModelCard": "Kad Model", + "ImportFromHuggingFace": "Import Dari Muka Berpeluk" }, "table": { "SelectColumnToDisplay": "Pilih untuk lajur untuk dipaparkan", @@ -1756,5 +1761,12 @@ "hour": "jam", "day": "day", "week": "minggu" + }, + "modelserving": { + "menu": { + "ModelServices": "Perkhidmatan Model", + "ModelList": "Senarai Model", + "MyServices": "Perkhidmatan Saya" + } } } diff --git a/resources/i18n/pl.json b/resources/i18n/pl.json index 49fa0ae652..3b5cd83650 100644 --- a/resources/i18n/pl.json +++ b/resources/i18n/pl.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "Wprowadź swój email", "DisMatchUserEmail": "Adres e-mail użytkownika nie jest zgodny", "MyEnvironments": "Moje Środowiska", - "ResourcePolicy": "Polityka zasobów" + "ResourcePolicy": "Polityka zasobów", + "Playground": "Plac gier i zabaw", + "ModelStore": "Sklep modelarski" }, "YouAreOffline": "Offline: Brak połączenia z siecią.", "YouAreOnline": "Jesteś teraz online", @@ -1705,7 +1707,10 @@ "category": "Kategoria", "CloneToFolder": "Klonowanie do folderu", "FolderAlreadyExists": "Folder o tej nazwie już istnieje.", - "FinetuneModel": "Dostosuj model" + "FinetuneModel": "Dostosuj model", + "Experience": "Doświadczenie", + "ModelCard": "Karta Modelowa", + "ImportFromHuggingFace": "Importuj z przytulającej twarzy" }, "table": { "SettingTable": "Nakrycie stołu", @@ -1756,5 +1761,12 @@ "hour": "godzina", "day": "day", "week": "tydzień" + }, + "modelserving": { + "menu": { + "ModelServices": "Usługi modelarskie", + "ModelList": "Lista modeli", + "MyServices": "Moje usługi" + } } } diff --git a/resources/i18n/pt-BR.json b/resources/i18n/pt-BR.json index 8cf39c15b3..3781d7ed91 100644 --- a/resources/i18n/pt-BR.json +++ b/resources/i18n/pt-BR.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "Digite seu e-mail", "DisMatchUserEmail": "E-mail do usuário não corresponde", "MyEnvironments": "Meus ambientes", - "ResourcePolicy": "Política de Recursos" + "ResourcePolicy": "Política de Recursos", + "Playground": "Parque infantil", + "ModelStore": "Loja de modelos" }, "YouAreOffline": "Desligado: Não está ligado a nenhuma rede.", "YouAreOnline": "Agora você está online", @@ -1705,7 +1707,10 @@ "category": "Categoria", "CloneToFolder": "Clonar para uma pasta", "FolderAlreadyExists": "Já existe uma pasta com este nome.", - "FinetuneModel": "Modelo de ajuste fino" + "FinetuneModel": "Modelo de ajuste fino", + "Experience": "Experiência", + "ModelCard": "Cartão Modelo", + "ImportFromHuggingFace": "Importar do rosto abraçado" }, "table": { "SettingTable": "Configuração de mesa", @@ -1756,5 +1761,12 @@ "hour": "hora", "day": "day", "week": "semana" + }, + "modelserving": { + "menu": { + "ModelServices": "Serviços de modelo", + "ModelList": "Lista de modelos", + "MyServices": "Meus serviços" + } } } diff --git a/resources/i18n/pt.json b/resources/i18n/pt.json index 997cf704de..52f4af7269 100644 --- a/resources/i18n/pt.json +++ b/resources/i18n/pt.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "Digite seu e-mail", "DisMatchUserEmail": "E-mail do usuário não corresponde", "MyEnvironments": "Meus ambientes", - "ResourcePolicy": "Política de Recursos" + "ResourcePolicy": "Política de Recursos", + "Playground": "Parque infantil", + "ModelStore": "Loja de modelos" }, "YouAreOffline": "Desligado: Não está ligado a nenhuma rede.", "YouAreOnline": "Agora você está online", @@ -1705,7 +1707,10 @@ "CloneInfo": "Ele será clonado como sua pasta de tipo de usuário.", "CloneToFolder": "Clonar para uma pasta", "FolderAlreadyExists": "Já existe uma pasta com este nome.", - "FinetuneModel": "Modelo de ajuste fino" + "FinetuneModel": "Modelo de ajuste fino", + "Experience": "Experiência", + "ModelCard": "Cartão Modelo", + "ImportFromHuggingFace": "Importar do rosto abraçado" }, "table": { "SettingTable": "Configuração de mesa", @@ -1756,5 +1761,12 @@ "hour": "hora", "day": "day", "week": "semana" + }, + "modelserving": { + "menu": { + "ModelServices": "Serviços de modelo", + "ModelList": "Lista de modelos", + "MyServices": "Meus serviços" + } } } diff --git a/resources/i18n/ru.json b/resources/i18n/ru.json index 7a6592a45b..d877a30ab4 100644 --- a/resources/i18n/ru.json +++ b/resources/i18n/ru.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "Введите адрес электронной почты", "DisMatchUserEmail": "Электронная почта пользователя не совпадает", "MyEnvironments": "Моя среда", - "ResourcePolicy": "Ресурсная политика" + "ResourcePolicy": "Ресурсная политика", + "Playground": "Детская площадка", + "ModelStore": "Модельный магазин" }, "YouAreOffline": "Не в сети: Не подключен ни к одной сети.", "YouAreOnline": "Вы сейчас в сети", @@ -1705,7 +1707,10 @@ "category": "Категория", "CloneToFolder": "Клонирование в папку", "FolderAlreadyExists": "Папка с таким именем уже существует.", - "FinetuneModel": "Точная настройка модели" + "FinetuneModel": "Точная настройка модели", + "Experience": "Опыт", + "ModelCard": "Модель карты", + "ImportFromHuggingFace": "Импорт из обнимающего лица" }, "table": { "SettingTable": "Сервировка стола", @@ -1756,5 +1761,12 @@ "hour": "час", "day": "day", "week": "неделя" + }, + "modelserving": { + "menu": { + "ModelServices": "Модельные услуги", + "ModelList": "Список моделей", + "MyServices": "Мои услуги" + } } } diff --git a/resources/i18n/th.json b/resources/i18n/th.json index 5fa2078538..f54fc3ab0e 100644 --- a/resources/i18n/th.json +++ b/resources/i18n/th.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "ใส่อีเมลของคุณ", "DisMatchUserEmail": "อีเมลผู้ใช้ไม่ตรงกัน", "MyEnvironments": "สภาพแวดล้อมของฉัน", - "ResourcePolicy": "นโยบายทรัพยากร" + "ResourcePolicy": "นโยบายทรัพยากร", + "Playground": "สนามเด็กเล่น", + "ModelStore": "ร้านโมเดล" }, "YouAreOffline": "ออฟไลน์: ไม่ได้เชื่อมต่อกับเครือข่ายใดๆ", "YouAreOnline": "คุณออนไลน์แล้ว", @@ -1689,7 +1691,10 @@ "category": "หมวดหมู่", "CloneToFolder": "โคลนไปยังโฟลเดอร์", "FolderAlreadyExists": "มีโฟลเดอร์ชื่อนี้อยู่แล้ว", - "FinetuneModel": "ปรับแต่งโมเดล" + "FinetuneModel": "ปรับแต่งโมเดล", + "Experience": "ประสบการณ์", + "ModelCard": "การ์ดโมเดล", + "ImportFromHuggingFace": "นำเข้าจากกอดใบหน้า" }, "tourguide": { "NeoSessionLauncher": { @@ -1736,5 +1741,12 @@ "hour": "ชั่วโมง", "day": "day", "week": "สัปดาห์" + }, + "modelserving": { + "menu": { + "ModelServices": "บริการโมเดล", + "ModelList": "รายการรุ่น", + "MyServices": "บริการของฉัน" + } } } diff --git a/resources/i18n/tr.json b/resources/i18n/tr.json index 400ae355f8..5319cda759 100644 --- a/resources/i18n/tr.json +++ b/resources/i18n/tr.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "E-postanızı giriniz", "DisMatchUserEmail": "Kullanıcı E-postası eşleşmiyor", "MyEnvironments": "Ortamlarım", - "ResourcePolicy": "Kaynak Politikası" + "ResourcePolicy": "Kaynak Politikası", + "Playground": "Bahçesi", + "ModelStore": "Model Mağazası" }, "YouAreOffline": "Çevrimdışı: Herhangi bir ağa bağlı değil.", "YouAreOnline": "artık çevrimiçisin", @@ -1704,7 +1706,10 @@ "category": "Kategori", "CloneToFolder": "Bir klasöre klonlama", "FolderAlreadyExists": "Bu isimde bir klasör zaten mevcut.", - "FinetuneModel": "İnce Ayar Modeli" + "FinetuneModel": "İnce Ayar Modeli", + "Experience": "Deneyim", + "ModelCard": "Model Kartı", + "ImportFromHuggingFace": "Sarılma Yüzünden İçe Aktar" }, "table": { "SettingTable": "Tablo Ayarı", @@ -1755,5 +1760,10 @@ "hour": "saat", "day": "day", "week": "hafta" + }, + "modelserving": { + "menu": { + "MyServices": "Hizmetlerim" + } } } diff --git a/resources/i18n/vi.json b/resources/i18n/vi.json index 4104b6610e..b648f27acd 100644 --- a/resources/i18n/vi.json +++ b/resources/i18n/vi.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "Nhập email của bạn", "DisMatchUserEmail": "E-mail của người dùng không khớp", "MyEnvironments": "Môi trường của tôi", - "ResourcePolicy": "Chính sách tài nguyên" + "ResourcePolicy": "Chính sách tài nguyên", + "Playground": "Sân chơi", + "ModelStore": "Cửa hàng mô hình" }, "YouAreOffline": "Ngoại tuyến: Không kết nối với bất kỳ mạng nào.", "YouAreOnline": "Bạn hiện đang trực tuyến", @@ -1705,7 +1707,10 @@ "category": "Loại", "CloneToFolder": "Sao chép vào một thư mục", "FolderAlreadyExists": "Một thư mục có tên này đã tồn tại.", - "FinetuneModel": "Mô hình Finetune" + "FinetuneModel": "Mô hình Finetune", + "Experience": "Kinh nghiệm", + "ModelCard": "Thẻ mẫu", + "ImportFromHuggingFace": "Nhập Từ Ôm Mặt" }, "table": { "SettingTable": "Cài đặt bảng", @@ -1756,5 +1761,11 @@ "hour": "giờ", "day": "day", "week": "tuần" + }, + "modelserving": { + "menu": { + "ModelList": "Danh sách mẫu", + "MyServices": "Dịch vụ của tôi" + } } } diff --git a/resources/i18n/zh-CN.json b/resources/i18n/zh-CN.json index 6749d68cba..621ac718eb 100644 --- a/resources/i18n/zh-CN.json +++ b/resources/i18n/zh-CN.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "输入你的电子邮箱", "DisMatchUserEmail": "用户电子邮件不匹配", "MyEnvironments": "我的环境", - "ResourcePolicy": "资源政策" + "ResourcePolicy": "资源政策", + "Playground": "操场", + "ModelStore": "模型店" }, "YouAreOffline": "脱机:未连接任何网络。", "YouAreOnline": "您现在在线", @@ -1706,7 +1708,10 @@ "category": "类别", "CloneToFolder": "克隆到文件夹", "FolderAlreadyExists": "该名称的文件夹已经存在。", - "FinetuneModel": "微调模型" + "FinetuneModel": "微调模型", + "Experience": "经验", + "ModelCard": "型号卡", + "ImportFromHuggingFace": "从拥抱脸导入" }, "table": { "SettingTable": "桌面设置", @@ -1757,5 +1762,11 @@ "hour": "小时", "day": "day", "week": "星期" + }, + "modelserving": { + "menu": { + "ModelList": "型号列表", + "MyServices": "我的服务" + } } } diff --git a/resources/i18n/zh-TW.json b/resources/i18n/zh-TW.json index d0e87d4cc2..922067a502 100644 --- a/resources/i18n/zh-TW.json +++ b/resources/i18n/zh-TW.json @@ -65,7 +65,9 @@ "InvalidBlankEmail": "輸入你的電子郵件信箱", "DisMatchUserEmail": "使用者電子郵件不符", "MyEnvironments": "我的環境", - "ResourcePolicy": "資源政策" + "ResourcePolicy": "資源政策", + "Playground": "操場", + "ModelStore": "模型店" }, "YouAreOffline": "脱机:未连接任何网络。", "YouAreOnline": "您現在在線", @@ -1706,7 +1708,10 @@ "category": "類別", "CloneToFolder": "克隆到文件夹", "FolderAlreadyExists": "该名称的文件夹已经存在。", - "FinetuneModel": "微調模型" + "FinetuneModel": "微調模型", + "Experience": "經驗", + "ModelCard": "型號卡", + "ImportFromHuggingFace": "從擁抱臉導入" }, "table": { "SettingTable": "桌面設定", @@ -1757,5 +1762,11 @@ "hour": "小時", "day": "day", "week": "星期" + }, + "modelserving": { + "menu": { + "ModelList": "型號列表", + "MyServices": "我的服務" + } } } diff --git a/resources/images/model-player/Llama-2-13b-chat-hf.jpeg b/resources/images/model-player/Llama-2-13b-chat-hf.jpeg new file mode 100644 index 0000000000..022f7dc51f Binary files /dev/null and b/resources/images/model-player/Llama-2-13b-chat-hf.jpeg differ diff --git a/resources/images/model-player/Meta-Llama-3-8B-Instruct.jpeg b/resources/images/model-player/Meta-Llama-3-8B-Instruct.jpeg new file mode 100644 index 0000000000..a92c1d78a7 Binary files /dev/null and b/resources/images/model-player/Meta-Llama-3-8B-Instruct.jpeg differ diff --git a/resources/images/model-player/Mistral-7B-Instruct-v0.1.jpeg b/resources/images/model-player/Mistral-7B-Instruct-v0.1.jpeg new file mode 100644 index 0000000000..e46e758467 Binary files /dev/null and b/resources/images/model-player/Mistral-7B-Instruct-v0.1.jpeg differ diff --git a/resources/images/model-player/Mixtral-8x7B-Instruct-v0.1.jpeg b/resources/images/model-player/Mixtral-8x7B-Instruct-v0.1.jpeg new file mode 100644 index 0000000000..34ac27aa93 Binary files /dev/null and b/resources/images/model-player/Mixtral-8x7B-Instruct-v0.1.jpeg differ diff --git a/resources/images/model-player/Qwen2-7B-Instruct.jpeg b/resources/images/model-player/Qwen2-7B-Instruct.jpeg new file mode 100644 index 0000000000..b1dcfe79bf Binary files /dev/null and b/resources/images/model-player/Qwen2-7B-Instruct.jpeg differ diff --git a/resources/images/model-player/Yi-1.5-9B-Chat.jpeg b/resources/images/model-player/Yi-1.5-9B-Chat.jpeg new file mode 100644 index 0000000000..10f424840f Binary files /dev/null and b/resources/images/model-player/Yi-1.5-9B-Chat.jpeg differ diff --git a/resources/images/model-player/bert-large-cased.jpeg b/resources/images/model-player/bert-large-cased.jpeg new file mode 100644 index 0000000000..02b347925c Binary files /dev/null and b/resources/images/model-player/bert-large-cased.jpeg differ diff --git a/resources/images/model-player/calm3-22b-chat.jpeg b/resources/images/model-player/calm3-22b-chat.jpeg new file mode 100644 index 0000000000..39ed22d82d Binary files /dev/null and b/resources/images/model-player/calm3-22b-chat.jpeg differ diff --git a/resources/images/model-player/clip-vit-large-patch14.jpeg b/resources/images/model-player/clip-vit-large-patch14.jpeg new file mode 100644 index 0000000000..949af54d17 Binary files /dev/null and b/resources/images/model-player/clip-vit-large-patch14.jpeg differ diff --git a/resources/images/model-player/efficientnet-b0.jpeg b/resources/images/model-player/efficientnet-b0.jpeg new file mode 100644 index 0000000000..d7f3594573 Binary files /dev/null and b/resources/images/model-player/efficientnet-b0.jpeg differ diff --git a/resources/images/model-player/falcon-7b-instruct.jpeg b/resources/images/model-player/falcon-7b-instruct.jpeg new file mode 100644 index 0000000000..033f64bb5a Binary files /dev/null and b/resources/images/model-player/falcon-7b-instruct.jpeg differ diff --git a/resources/images/model-player/flan-t5-xxl.jpeg b/resources/images/model-player/flan-t5-xxl.jpeg new file mode 100644 index 0000000000..10ce65ae2e Binary files /dev/null and b/resources/images/model-player/flan-t5-xxl.jpeg differ diff --git a/resources/images/model-player/gemma-1.1-2b-it.jpeg b/resources/images/model-player/gemma-1.1-2b-it.jpeg new file mode 100644 index 0000000000..30984bd9e2 Binary files /dev/null and b/resources/images/model-player/gemma-1.1-2b-it.jpeg differ diff --git a/resources/images/model-player/gemma-2-27b-it.jpeg b/resources/images/model-player/gemma-2-27b-it.jpeg new file mode 100644 index 0000000000..6ca94dac74 Binary files /dev/null and b/resources/images/model-player/gemma-2-27b-it.jpeg differ diff --git a/resources/images/model-player/hf-logo.png b/resources/images/model-player/hf-logo.png new file mode 100644 index 0000000000..49e2841dd5 Binary files /dev/null and b/resources/images/model-player/hf-logo.png differ diff --git a/resources/images/model-player/hugging-face-background.jpg b/resources/images/model-player/hugging-face-background.jpg new file mode 100644 index 0000000000..8636743fe1 Binary files /dev/null and b/resources/images/model-player/hugging-face-background.jpg differ diff --git a/resources/images/model-player/llama-3.1-8b-it.jpeg b/resources/images/model-player/llama-3.1-8b-it.jpeg new file mode 100644 index 0000000000..6beba14772 Binary files /dev/null and b/resources/images/model-player/llama-3.1-8b-it.jpeg differ diff --git a/resources/images/model-player/sdxl-turbo.jpeg b/resources/images/model-player/sdxl-turbo.jpeg new file mode 100644 index 0000000000..9452ba3a6a Binary files /dev/null and b/resources/images/model-player/sdxl-turbo.jpeg differ diff --git a/resources/images/model-player/stable-diffusion-2.jpeg b/resources/images/model-player/stable-diffusion-2.jpeg new file mode 100644 index 0000000000..363b7a2c40 Binary files /dev/null and b/resources/images/model-player/stable-diffusion-2.jpeg differ diff --git a/resources/images/model-player/stable-diffusion-3-medium.jpeg b/resources/images/model-player/stable-diffusion-3-medium.jpeg new file mode 100644 index 0000000000..f824c41c2c Binary files /dev/null and b/resources/images/model-player/stable-diffusion-3-medium.jpeg differ diff --git a/resources/images/model-player/stable-diffusion-v1-5.jpeg b/resources/images/model-player/stable-diffusion-v1-5.jpeg new file mode 100644 index 0000000000..8afb7ed5c7 Binary files /dev/null and b/resources/images/model-player/stable-diffusion-v1-5.jpeg differ diff --git a/resources/images/model-player/talkativot-UI.jpeg b/resources/images/model-player/talkativot-UI.jpeg new file mode 100644 index 0000000000..e5f6390063 Binary files /dev/null and b/resources/images/model-player/talkativot-UI.jpeg differ diff --git a/resources/images/model-player/whisper-large-v3.jpeg b/resources/images/model-player/whisper-large-v3.jpeg new file mode 100644 index 0000000000..0e30907e72 Binary files /dev/null and b/resources/images/model-player/whisper-large-v3.jpeg differ diff --git a/src/backend-ai-app.ts b/src/backend-ai-app.ts index c92c60e16d..fac7a07f83 100644 --- a/src/backend-ai-app.ts +++ b/src/backend-ai-app.ts @@ -20,6 +20,7 @@ export const navigate = '/experiment', '/data', '/my-environment', + '/model-store', '/statistics', '/usersettings', '/agent', @@ -48,7 +49,7 @@ export const navigate = } let page; if (['/', 'build', '/build', 'app', '/app'].includes(path)) { - page = 'summary'; + page = 'playground'; } else if (path[0] === '/') { page = path.slice(1); } else { diff --git a/src/components/backend-ai-error-view.ts b/src/components/backend-ai-error-view.ts index 447b03acb9..5b83d095db 100644 --- a/src/components/backend-ai-error-view.ts +++ b/src/components/backend-ai-error-view.ts @@ -70,8 +70,8 @@ export default class BackendAIErrorView extends BackendAIPage { * @param {string} url - page to redirect from the current page. */ _moveTo(url = '') { - const page = url !== '' ? url : 'summary'; - globalThis.history.pushState({}, '', '/summary'); + const page = url !== '' ? url : 'playground'; + globalThis.history.pushState({}, '', '/playground'); store.dispatch(navigate(decodeURIComponent('/' + page), {})); document.dispatchEvent( new CustomEvent('react-navigate', { @@ -97,8 +97,8 @@ export default class BackendAIErrorView extends BackendAIPage { unelevated fullwidth id="go-to-summary" - label="${_t('button.GoBackToSummaryPage')}" - @click="${() => this._moveTo('summary')}" + label="${_t('button.GoBackToModelServicesPage')}" + @click="${() => this._moveTo('playground')}" > diff --git a/src/components/backend-ai-permission-denied-view.ts b/src/components/backend-ai-permission-denied-view.ts index 503a11adba..eb2a89ed04 100644 --- a/src/components/backend-ai-permission-denied-view.ts +++ b/src/components/backend-ai-permission-denied-view.ts @@ -113,8 +113,8 @@ export default class BackendAIPermissionDeniedView extends BackendAIPage { * @param {string} url - page to redirect from the current page. */ _moveTo(url = '') { - const page = url !== '' ? url : 'summary'; - globalThis.history.pushState({}, '', '/summary'); + const page = url !== '' ? url : 'playground'; + globalThis.history.pushState({}, '', '/playground'); store.dispatch(navigate(decodeURIComponent('/' + page), {})); } diff --git a/src/components/backend-ai-webui.ts b/src/components/backend-ai-webui.ts index df6e289260..70cd4515f7 100644 --- a/src/components/backend-ai-webui.ts +++ b/src/components/backend-ai-webui.ts @@ -162,6 +162,8 @@ export default class BackendAIWebUI extends connect(store)(LitElement) { 'settings', 'maintenance', 'serving', + 'playground', + 'model-store', 'service', 'service/start', 'service/update', diff --git a/version.json b/version.json index ae38cc4889..830faf2fb5 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{ "package": "24.09.0-alpha.1", "buildNumber": "6111", "buildDate": "240703.130731", "revision": "d8cea1cb" } +{ "package": "25.1.0-alpha.1", "buildNumber": "6441", "buildDate": "250105.230113", "revision": "c18dd4be5" }