chore: ollama deprecated tips

This commit is contained in:
jialin
2025-05-08 11:09:05 +08:00
parent 1c87fef28d
commit 7f198b64f1
9 changed files with 315 additions and 84 deletions
+4 -2
View File
@@ -14,6 +14,7 @@ interface AlertInfoProps {
style?: React.CSSProperties; style?: React.CSSProperties;
contentStyle?: React.CSSProperties; contentStyle?: React.CSSProperties;
title?: React.ReactNode; title?: React.ReactNode;
maxHeight?: number;
} }
const TitleWrapper = styled.div` const TitleWrapper = styled.div`
@@ -39,7 +40,8 @@ const AlertInfo: React.FC<AlertInfoProps> = (props) => {
style, style,
title, title,
contentStyle, contentStyle,
icon icon,
maxHeight = 86
} = props; } = props;
return ( return (
@@ -65,7 +67,7 @@ const AlertInfo: React.FC<AlertInfoProps> = (props) => {
{title && ( {title && (
<TitleWrapper className="title-text">{title}</TitleWrapper> <TitleWrapper className="title-text">{title}</TitleWrapper>
)} )}
<OverlayScroller maxHeight={86} style={{ ...contentStyle }}> <OverlayScroller maxHeight={maxHeight} style={{ ...contentStyle }}>
<ContentWrapper <ContentWrapper
$hasTitle={!!title} $hasTitle={!!title}
className={classNames('content', type)} className={classNames('content', type)}
+11 -2
View File
@@ -102,7 +102,7 @@ export default {
'models.localpath.gguf.tips': 'models.localpath.gguf.tips':
' Specify the model file, e.g., /data/models/model.gguf.', ' Specify the model file, e.g., /data/models/model.gguf.',
'models.localpath.safe.tips': 'models.localpath.safe.tips':
'Specify the model directory that contains .safetensors and config.json files, e.g., /data/models/model/.', 'Specify the model directory that contains .safetensors and config.json files, e.g., /data/models/model.',
'models.localpath.chunks.tips': `Specify the first shard file of the model, e.g., /data/models/model-00001-of-00004.gguf.`, 'models.localpath.chunks.tips': `Specify the first shard file of the model, e.g., /data/models/model-00001-of-00004.gguf.`,
'models.form.replicas.tips': 'models.form.replicas.tips':
'Multiple replicas enable load balancing for { api } inference requests.', 'Multiple replicas enable load balancing for { api } inference requests.',
@@ -148,5 +148,14 @@ export default {
'models.table.apiAccessInfo.jinaCompatible': 'Jina Compatible', 'models.table.apiAccessInfo.jinaCompatible': 'Jina Compatible',
'models.table.apiAccessInfo.gotoCreate': 'Go to Create', 'models.table.apiAccessInfo.gotoCreate': 'Go to Create',
'models.search.parts': '{n} parts', 'models.search.parts': '{n} parts',
'models.search.evaluate.error': 'An error occurred during evaluation: ' 'models.search.evaluate.error': 'An error occurred during evaluation: ',
'models.ollama.deprecated.title': 'Ollama Model Source Will Be Removed',
'models.ollama.deprecated.current':
'<span class="bold-text">Current Version (v0.6.1): </span>Ollama models are currently available for use.',
'models.ollama.deprecated.upcoming':
'<span class="bold-text">Upcoming Version (v0.7.0): </span>The Ollama model source will be removed from the UI.',
'models.ollama.deprecated.following':
'<span class="bold-text">Following the v0.7.0 update,</span> all previously deployed models will continue to work as expected.',
'models.ollama.deprecated.issue':
'See the related issue: <a href="https://github.com/gpustack/gpustack/issues/1979" target="_blank">#1979 on GitHub</a>.'
}; };
+19 -2
View File
@@ -101,7 +101,7 @@ export default {
'models.localpath.gguf.tips': 'models.localpath.gguf.tips':
'モデルファイルを指定してください。例: /data/models/model.gguf。', 'モデルファイルを指定してください。例: /data/models/model.gguf。',
'models.localpath.safe.tips': 'models.localpath.safe.tips':
'config.jsonファイルを含む.safetensorsディレクトリを指定してください。例: /data/models/model/。', 'config.jsonファイルを含む.safetensorsディレクトリを指定してください。例: /data/models/model。',
'models.localpath.chunks.tips': `モデルの最初のシャードファイルを指定してください。例: /data/models/model-00001-of-00004.gguf。`, 'models.localpath.chunks.tips': `モデルの最初のシャードファイルを指定してください。例: /data/models/model-00001-of-00004.gguf。`,
'models.form.replicas.tips': 'models.form.replicas.tips':
'複数のレプリカにより、{api} 推論リクエストの負荷分散が可能になります。', '複数のレプリカにより、{api} 推論リクエストの負荷分散が可能になります。',
@@ -148,5 +148,22 @@ export default {
'models.table.apiAccessInfo.jinaCompatible': 'Jina互換', 'models.table.apiAccessInfo.jinaCompatible': 'Jina互換',
'models.table.apiAccessInfo.gotoCreate': '作成に移動', 'models.table.apiAccessInfo.gotoCreate': '作成に移動',
'models.search.parts': '{n} 部分', 'models.search.parts': '{n} 部分',
'models.search.evaluate.error': '評価中にエラーが発生しました: ' 'models.search.evaluate.error': '評価中にエラーが発生しました: ',
'models.ollama.deprecated.title': 'Ollama Model Source Will Be Removed',
'models.ollama.deprecated.current':
'<span class="bold-text">Current Version (v0.6.1): </span>Ollama models are currently available for use.',
'models.ollama.deprecated.upcoming':
'<span class="bold-text">Upcoming Version (v0.7.0): </span>The Ollama model source will be removed from the UI.',
'models.ollama.deprecated.following':
'<span class="bold-text">Following the v0.7.0 update,</span> all previously deployed models will continue to work as expected.',
'models.ollama.deprecated.issue':
'See the related issue: <a href="https://github.com/gpustack/gpustack/issues/1979" target="_blank">#1979 on GitHub</a>.'
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ==========
// 1. 'models.ollama.deprecated.title': 'Ollama Model Source Will Be Removed',
// 2. 'models.ollama.deprecated.current':'<span class="bold-text">Current Version (v0.6.1): </span>Ollama models are currently available for use.',
// 3. 'models.ollama.deprecated.upcoming': '<span class="bold-text">Upcoming Version (v0.7.0): </span>The Ollama model source will be removed from the UI.',
// 4. 'models.ollama.deprecated.following': '<span class="bold-text">Following the v0.7.0 update,</span> all previously deployed models will continue to work as expected.',
// 5. 'models.ollama.deprecated.issue': 'See the related issue: <a href="https://github.com/gpustack/gpustack/issues/1979" target="_blank">#1979 on GitHub</a>.'
// ========== End of To-Do List ==========
+15 -2
View File
@@ -149,9 +149,22 @@ export default {
'models.table.apiAccessInfo.jinaCompatible': 'Совместимо с Jina', 'models.table.apiAccessInfo.jinaCompatible': 'Совместимо с Jina',
'models.table.apiAccessInfo.gotoCreate': 'Перейти к созданию', 'models.table.apiAccessInfo.gotoCreate': 'Перейти к созданию',
'models.search.parts': '{n} частей', 'models.search.parts': '{n} частей',
'models.search.evaluate.error': 'Возникла ошибка при вычислении: ' 'models.search.evaluate.error': 'Возникла ошибка при вычислении: ',
'models.ollama.deprecated.title': 'Ollama Model Source Will Be Removed',
'models.ollama.deprecated.current':
'<span class="bold-text">Current Version (v0.6.1): </span>Ollama models are currently available for use.',
'models.ollama.deprecated.upcoming':
'<span class="bold-text">Upcoming Version (v0.7.0): </span>The Ollama model source will be removed from the UI.',
'models.ollama.deprecated.following':
'<span class="bold-text">Following the v0.7.0 update,</span> all previously deployed models will continue to work as expected.',
'models.ollama.deprecated.issue':
'See the related issue: <a href="https://github.com/gpustack/gpustack/issues/1979" target="_blank">#1979 on GitHub</a>.'
}; };
// ========== To-Do: Translate Keys (Remove After Translation) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
// 1. 'models.ollama.deprecated.title': 'Ollama Model Source Will Be Removed',
// 2. 'models.ollama.deprecated.current':'<span class="bold-text">Current Version (v0.6.1): </span>Ollama models are currently available for use.',
// 3. 'models.ollama.deprecated.upcoming': '<span class="bold-text">Upcoming Version (v0.7.0): </span>The Ollama model source will be removed from the UI.',
// 4. 'models.ollama.deprecated.following': '<span class="bold-text">Following the v0.7.0 update,</span> all previously deployed models will continue to work as expected.',
// 5. 'models.ollama.deprecated.issue': 'See the related issue: <a href="https://github.com/gpustack/gpustack/issues/1979" target="_blank">#1979 on GitHub</a>.'
// ========== End of To-Do List ========== // ========== End of To-Do List ==========
+11 -2
View File
@@ -100,7 +100,7 @@ export default {
'models.localpath.shared.tips.title': '分片的 GGUF 格式模型', 'models.localpath.shared.tips.title': '分片的 GGUF 格式模型',
'models.localpath.gguf.tips': '指向模型文件,例如 /data/models/model.gguf', 'models.localpath.gguf.tips': '指向模型文件,例如 /data/models/model.gguf',
'models.localpath.safe.tips': 'models.localpath.safe.tips':
'指向包含 .safetensors, config.json 文件的模型目录,例如 /data/models/model/', '指向包含 .safetensors, config.json 文件的模型目录,例如 /data/models/model',
'models.localpath.chunks.tips': 'models.localpath.chunks.tips':
'指向模型第一个分片文件,例如 /data/models/model-00001-of-00004.gguf', '指向模型第一个分片文件,例如 /data/models/model-00001-of-00004.gguf',
'models.form.replicas.tips': '多副本数实现 { api } 接口推理请求的负载均衡', 'models.form.replicas.tips': '多副本数实现 { api } 接口推理请求的负载均衡',
@@ -141,5 +141,14 @@ export default {
'models.table.apiAccessInfo.jinaCompatible': 'Jina 兼容', 'models.table.apiAccessInfo.jinaCompatible': 'Jina 兼容',
'models.table.apiAccessInfo.gotoCreate': '去创建', 'models.table.apiAccessInfo.gotoCreate': '去创建',
'models.search.parts': '{n} 个文件', 'models.search.parts': '{n} 个文件',
'models.search.evaluate.error': '评估过程中发生了错误:' 'models.search.evaluate.error': '评估过程中发生了错误:',
'models.ollama.deprecated.title': 'Ollama 模型源即将被移除',
'models.ollama.deprecated.current':
'<span class="bold-text">当前版本(v0.6.1):</span>Ollama 模型目前仍可使用。',
'models.ollama.deprecated.upcoming':
'<span class="bold-text">即将发布的版本(v0.7.0):</span>Ollama 模型源将在 UI 中被移除。',
'models.ollama.deprecated.following':
'<span class="bold-text">在 v0.7.0 更新后:</span> 所有先前部署的模型仍将正常运行。',
'models.ollama.deprecated.issue':
'参见 GitHub 上的问题 <a href="https://github.com/gpustack/gpustack/issues/1979" target="_blank">#1979</a>。'
}; };
+33 -4
View File
@@ -3,7 +3,7 @@ import PageTools from '@/components/page-tools';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import useBodyScroll from '@/hooks/use-body-scroll'; import useBodyScroll from '@/hooks/use-body-scroll';
import { IS_FIRST_LOGIN, writeState } from '@/utils/localstore/index'; import { IS_FIRST_LOGIN, writeState } from '@/utils/localstore/index';
import { SyncOutlined } from '@ant-design/icons'; import { DoubleRightOutlined, SyncOutlined } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useIntl, useNavigate } from '@umijs/max'; import { useIntl, useNavigate } from '@umijs/max';
import { Button, Input, Pagination, Select, Space, message } from 'antd'; import { Button, Input, Pagination, Select, Space, message } from 'antd';
@@ -22,6 +22,18 @@ const PageWrapper = styled.div`
margin-block: 32px 16px; margin-block: 32px 16px;
`; `;
const MoreWrapper = styled.div`
display: flex;
justify-content: center;
margin-block: 16px;
opacity: 1;
transition: opacity 0.3s;
&.loading {
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
`;
const Catalog: React.FC = () => { const Catalog: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll(); const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
@@ -41,7 +53,7 @@ const Catalog: React.FC = () => {
}); });
const [queryParams, setQueryParams] = useState({ const [queryParams, setQueryParams] = useState({
page: 1, page: 1,
perPage: 12, perPage: 30,
search: '', search: '',
categories: '' categories: ''
}); });
@@ -159,8 +171,6 @@ const Catalog: React.FC = () => {
const handleCreateModel = useCallback( const handleCreateModel = useCallback(
async (data: FormData) => { async (data: FormData) => {
try { try {
console.log('data:', data, openDeployModal);
const modelData = await createModel({ const modelData = await createModel({
data: { data: {
..._.omit(data, ['size', 'quantization']) ..._.omit(data, ['size', 'quantization'])
@@ -213,6 +223,13 @@ const Catalog: React.FC = () => {
}); });
}; };
const loadMore = () => {
fetchData({
...queryParams,
page: queryParams.page + 1
});
};
useEffect(() => { useEffect(() => {
fetchData(); fetchData();
}, []); }, []);
@@ -298,6 +315,18 @@ const Catalog: React.FC = () => {
activeId={-1} activeId={-1}
isFirst={isFirst} isFirst={isFirst}
></CatalogList> ></CatalogList>
{queryParams.page < dataSource.totalPage && (
<MoreWrapper className={dataSource.loading ? 'loading' : ''}>
<Button
onClick={loadMore}
size="middle"
type="text"
icon={<DoubleRightOutlined rotate={90} />}
>
{intl.formatMessage({ id: 'common.button.more' })}
</Button>
</MoreWrapper>
)}
<PageWrapper> <PageWrapper>
<Pagination <Pagination
hideOnSinglePage={queryParams.perPage === 100} hideOnSinglePage={queryParams.perPage === 100}
@@ -23,6 +23,7 @@ import CompatibilityAlert from './compatible-alert';
import DataForm from './data-form'; import DataForm from './data-form';
import HFModelFile from './hf-model-file'; import HFModelFile from './hf-model-file';
import ModelCard from './model-card'; import ModelCard from './model-card';
import OllamaTips from './ollama-tips';
import SearchModel from './search-model'; import SearchModel from './search-model';
import Separator from './separator'; import Separator from './separator';
import TitleWrapper from './title-wrapper'; import TitleWrapper from './title-wrapper';
@@ -360,6 +361,7 @@ const AddModal: FC<AddModalProps> = (props) => {
</ColWrapper> </ColWrapper>
</> </>
)} )}
<FormContext.Provider <FormContext.Provider
value={{ value={{
isGGUF: isGGUF, isGGUF: isGGUF,
@@ -402,6 +404,9 @@ const AddModal: FC<AddModalProps> = (props) => {
} }
> >
<> <>
{source === modelSourceMap.ollama_library_value && (
<OllamaTips></OllamaTips>
)}
{SEARCH_SOURCE.includes(source) && {SEARCH_SOURCE.includes(source) &&
deploymentType === 'modelList' && ( deploymentType === 'modelList' && (
<TitleWrapper> <TitleWrapper>
@@ -0,0 +1,128 @@
import AlertBlockInfo from '@/components/alert-info/block';
import IconFont from '@/components/icon-font';
import { useIntl } from '@umijs/max';
import { Button } from 'antd';
import { useState } from 'react';
import styled from 'styled-components';
const Wrapper = styled.div`
padding-inline: 24px;
`;
const Tips = styled.div`
margin-top: 8px !important;
ul {
margin: 0 !important;
padding: 0 !important;
margin-top: 8px !important;
li {
position: relative;
list-style: none;
line-height: 24px;
margin: 0 !important;
padding-inline: 16px 0 !important;
&::before {
content: '';
position: absolute;
left: 0px;
top: 0;
width: 6px;
height: 6px;
border-radius: 50%;
background-color: var(--ant-color-text-secondary);
margin-top: 8px;
}
}
}
.bold {
font-weight: 700;
color: var(--ant-color-text);
}
.notice {
margin-top: 8px !important;
}
`;
const TitleWrapper = styled.div`
display: flex;
align-items: center;
font-weight: 700;
color: var(--ant-color-text);
justify-content: space-between;
`;
const RenderMessage = () => {
const intl = useIntl();
return (
<Tips>
<ul>
<li>
<span
dangerouslySetInnerHTML={{
__html: intl.formatMessage({
id: 'models.ollama.deprecated.current'
})
}}
></span>
</li>
<li>
<span
dangerouslySetInnerHTML={{
__html: intl.formatMessage({
id: 'models.ollama.deprecated.upcoming'
})
}}
></span>
</li>
</ul>
<div className="notice">
<span
dangerouslySetInnerHTML={{
__html: intl.formatMessage({
id: 'models.ollama.deprecated.following'
})
}}
></span>
<div
dangerouslySetInnerHTML={{
__html: intl.formatMessage({ id: 'models.ollama.deprecated.issue' })
}}
></div>
</div>
</Tips>
);
};
const OllamaTips = () => {
const intl = useIntl();
const [maxHeight, setMaxHeight] = useState(1);
const handleShowMore = () => {
setMaxHeight(maxHeight === 1 ? 600 : 1);
};
return (
<Wrapper>
<AlertBlockInfo
contentStyle={{ paddingInline: 0 }}
maxHeight={maxHeight}
ellipsis={false}
message={<RenderMessage></RenderMessage>}
title={
<TitleWrapper>
<div>
{intl.formatMessage({ id: 'models.ollama.deprecated.title' })}
</div>
<Button
onClick={handleShowMore}
size="small"
type="text"
icon={<IconFont type="icon-down"></IconFont>}
></Button>
</TitleWrapper>
}
type={'warning'}
></AlertBlockInfo>
</Wrapper>
);
};
export default OllamaTips;
+89 -70
View File
@@ -9,6 +9,7 @@ import { useIntl } from '@umijs/max';
import { Form, Typography } from 'antd'; import { Form, Typography } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import React, { forwardRef, useImperativeHandle, useMemo } from 'react'; import React, { forwardRef, useImperativeHandle, useMemo } from 'react';
import OllamaTips from '../components/ollama-tips';
import { import {
localPathTipsList, localPathTipsList,
modelSourceMap, modelSourceMap,
@@ -38,6 +39,18 @@ const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
onOk(data); onOk(data);
}; };
const handleOnLocalPathBlur = (e: any) => {
let { value } = e.target;
// remove the last slash: windows or linux
const lastChar = value.slice(-1);
if (lastChar === '/' || lastChar === '\\') {
value = value.slice(0, -1);
}
form.setFieldsValue({
local_path: value
});
};
const renderLocalPathFields = () => { const renderLocalPathFields = () => {
return ( return (
<> <>
@@ -54,6 +67,7 @@ const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
<SealInput.Input <SealInput.Input
required required
label={intl.formatMessage({ id: 'models.form.filePath' })} label={intl.formatMessage({ id: 'models.form.filePath' })}
onBlur={handleOnLocalPathBlur}
description={<TooltipList list={localPathTipsList}></TooltipList>} description={<TooltipList list={localPathTipsList}></TooltipList>}
></SealInput.Input> ></SealInput.Input>
</Form.Item> </Form.Item>
@@ -119,84 +133,89 @@ const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
}, [props.source, intl]); }, [props.source, intl]);
return ( return (
<Form <div>
form={form} {source === modelSourceMap.ollama_library_value && (
onFinish={handleOk} <OllamaTips></OllamaTips>
preserve={false} )}
style={{ padding: '16px 24px' }} <Form
clearOnDestroy={true} form={form}
initialValues={{ onFinish={handleOk}
source: source preserve={false}
}} style={{ padding: '16px 24px' }}
> clearOnDestroy={true}
<Form.Item<FormData> initialValues={{
name="source" source: source
rules={[ }}
{
required: true,
message: getRuleMessage('select', 'models.form.source')
}
]}
> >
{
<SealSelect
disabled
label={intl.formatMessage({
id: 'models.form.source'
})}
options={sourceOptions}
required
></SealSelect>
}
</Form.Item>
{renderFieldsBySource}
<Form.Item
name="worker_id"
rules={[
{
required: true,
message: getRuleMessage('select', 'worker', false)
}
]}
>
{
<SealSelect
label="Worker"
options={workersList}
required
></SealSelect>
}
</Form.Item>
{source !== modelSourceMap.local_path_value && (
<Form.Item<FormData> <Form.Item<FormData>
name="local_dir" name="source"
rules={[ rules={[
{ {
required: false, required: true,
message: getRuleMessage( message: getRuleMessage('select', 'models.form.source')
'input',
'resources.modelfiles.form.localdir'
)
} }
]} ]}
> >
<SealInput.Input {
description={ <SealSelect
<span disabled
dangerouslySetInnerHTML={{ label={intl.formatMessage({
__html: intl.formatMessage({ id: 'models.form.source'
id: 'resources.modelfiles.form.localdir.tips' })}
}) options={sourceOptions}
}} required
></span> ></SealSelect>
} }
label={intl.formatMessage({
id: 'resources.modelfiles.form.localdir'
})}
></SealInput.Input>
</Form.Item> </Form.Item>
)} {renderFieldsBySource}
</Form> <Form.Item
name="worker_id"
rules={[
{
required: true,
message: getRuleMessage('select', 'worker', false)
}
]}
>
{
<SealSelect
label="Worker"
options={workersList}
required
></SealSelect>
}
</Form.Item>
{source !== modelSourceMap.local_path_value && (
<Form.Item<FormData>
name="local_dir"
rules={[
{
required: false,
message: getRuleMessage(
'input',
'resources.modelfiles.form.localdir'
)
}
]}
>
<SealInput.Input
description={
<span
dangerouslySetInnerHTML={{
__html: intl.formatMessage({
id: 'resources.modelfiles.form.localdir.tips'
})
}}
></span>
}
label={intl.formatMessage({
id: 'resources.modelfiles.form.localdir'
})}
></SealInput.Input>
</Form.Item>
)}
</Form>
</div>
); );
}); });