Compare commits

...
13 Commits
Author SHA1 Message Date
jialin 66e8f83395 fix: did not cache workers and clusters in model files 2025-12-31 10:23:15 +08:00
jialin 41ebceca8f chore: static iconfont files 2025-12-29 15:40:36 +08:00
jialin 632685672c fix: wheel event passive 2025-12-29 15:28:40 +08:00
jialin bd2e4bb633 docs: hygon notes 2025-12-29 09:36:48 +08:00
jialin 835633d44b fix: help menu item clickable area 2025-12-26 18:18:32 +08:00
EVGENY Mandjialin 75773e6b00 Update models.ts 2025-12-26 18:16:33 +08:00
EVGENY Mandjialin a21b9b5244 Update users.ts 2025-12-26 18:16:02 +08:00
EVGENY Mandjialin 38bf76dc14 Update common.ts 2025-12-26 18:15:29 +08:00
EVGENY Mandjialin 9461341104 Update clusters.ts 2025-12-26 18:15:05 +08:00
jialin 5b5b2c6364 fix: chat compare switch model meta 2025-12-26 17:29:36 +08:00
jialin a006263e51 fix: add worker to default cluster 2025-12-26 12:08:30 +08:00
jialin febdd38f7c fix: set submitloading as false in catch 2025-12-26 09:48:10 +08:00
jialin 1b4d335b09 fix: store hideModel value in global session 2025-12-25 20:36:48 +08:00
36 changed files with 302 additions and 242 deletions
+13
View File
@@ -344,3 +344,16 @@ textarea:hover {
.text-success {
color: var(--ant-color-success);
}
.desc-fill {
font-weight: 600;
color: var(--ant-color-text);
background-color: var(--ant-color-fill-content);
line-height: 1.5;
padding: 2px 6px;
border-radius: 4px;
}
.line-6 {
line-height: 24px;
}
+13
View File
@@ -20,17 +20,30 @@ export const getRequestId = () => {
return store.get(requestIdAtom);
};
// store for cluster list: res.items from api
export const clusterListAtom = atom<
{
label: string;
value: number;
provider: string;
state: string;
is_default: boolean;
workers: number;
ready_workers: number;
gpus: number;
}[]
>([]);
// store for worker list: res.items from api
export const workerListAtom = atom<
{
label: string;
value: number;
cluster_id: number;
state: string;
id: number;
labels: Record<string, any>;
name: string;
}[]
>([]);
+1
View File
@@ -54,3 +54,4 @@ export const userSettingsHelperAtom = atom(
});
}
);
export const hideModalTemporarilyAtom = atom<boolean>(false);
+15 -3
View File
@@ -1,8 +1,8 @@
@font-face {
font-family: iconfont; /* Project id 4613488 */
src: url('iconfont.woff2?t=1763436184844') format('woff2'),
url('iconfont.woff?t=1763436184844') format('woff'),
url('iconfont.ttf?t=1763436184844') format('truetype');
src: url('iconfont.woff2?t=1766993792323') format('woff2'),
url('iconfont.woff?t=1766993792323') format('woff'),
url('iconfont.ttf?t=1766993792323') format('truetype');
}
.iconfont {
@@ -13,6 +13,18 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-cloud::before {
content: "\e6bc";
}
.icon-server02::before {
content: "\e6bd";
}
.icon-drag_handle::before {
content: "\e6b9";
}
.icon-basic::before {
content: "\e6bb";
}
File diff suppressed because one or more lines are too long
@@ -5,6 +5,27 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "46296038",
"name": "cloud",
"font_class": "cloud",
"unicode": "e6bc",
"unicode_decimal": 59068
},
{
"icon_id": "46296037",
"name": "server",
"font_class": "server02",
"unicode": "e6bd",
"unicode_decimal": 59069
},
{
"icon_id": "46272083",
"name": "drag_handle",
"font_class": "drag_handle",
"unicode": "e6b9",
"unicode_decimal": 59065
},
{
"icon_id": "46055620",
"name": "basic",
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -2
View File
@@ -1,8 +1,8 @@
import { createFromIconfontCN } from '@ant-design/icons';
// import './iconfont/iconfont.js';
import './iconfont/iconfont.js';
const IconFont = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/c/font_4613488_xvmo08q7urm.js'
scriptUrl: ''
});
export default IconFont;
+5 -2
View File
@@ -168,14 +168,17 @@ export default function useOverlayScroller(data?: {
// add wheel event
const handleWheelEvent = () => {
scrollElementRef.current?.addEventListener?.('wheel', handleWheelCallback);
scrollElementRef.current?.addEventListener?.('wheel', handleWheelCallback, {
passive: true
});
};
// remove wheel event
const removeWheelEvent = () => {
scrollElementRef.current?.removeEventListener?.(
'wheel',
handleWheelCallback
handleWheelCallback,
{ passive: true }
);
};
+9 -16
View File
@@ -202,23 +202,16 @@ export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
items: helpList.map((item) => ({
key: item.key,
label: (
<span className="flex flex-center">
<a
className="flex flex-center gap-8"
href={item.url}
target="_blank"
rel="noreferrer"
>
{item.icon}
<a
className="m-l-8 "
href={item.url}
target="_blank"
rel="noreferrer"
>
{item.label}
</a>
</span>
),
onClick() {
if (item.key === 'version') {
showVersion();
}
}
{item.label}
</a>
)
}))
};
+1 -2
View File
@@ -19,8 +19,7 @@ export default {
'backend.mode.yaml': 'YAML Mode',
'backend.form.healthCheckPath': 'Health Check Path',
'backend.form.defaultExecuteCommand': 'Default Execution Command',
'backend.form.defaultExecuteCommand.tips':
'{{model_path}}, {{port}}, {{worker_ip}} and {{model_name}} are placeholders that will be substituted with the actual values during deployment.',
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}', '{{'port'}}', '{{'worker_ip'}}' and '{{'model_name'}}' are placeholders that will be substituted with the actual values during deployment.`,
'backend.form.defaultBackendParameters': 'Default Backend Parameters',
'backend.form.versionConfig': 'Versions Config',
'backend.form.addParameter': 'Add Parameter',
+1 -2
View File
@@ -51,8 +51,7 @@ export default {
'If multiple outbound IPs exist, specify the one you want the worker to use. Please double-check with <span class="bold-text">hostname -I | xargs -n1</span>.',
'clusters.addworker.nvidiaNotes-02':
'If a model directory already exists on the worker, you can specify the path to mount it.',
'clusters.addworker.hygonNotes': `If the <span class="bold-text">/opt/hyhal</span> directory does not exist, create a symbolic link to the Hygon installation path.
Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
'clusters.addworker.hygonNotes': `If <span class="bold-text">/opt/hyhal</span> or <span class="bold-text">/opt/dtk</span> does not exist, create symbolic links pointing to the corresponding Hygon installation paths, for example: <span class="desc-fill">ln -s /path/to/hyhal /opt/hyhal</span> <span class="desc-fill">ln -s /path/to/dtk /opt/dtk</span>.`,
'clusters.addworker.corexNotes': `If the <span class="bold-text">/lib/modules</span> directory does not exist, create a symbolic link to the Iluvatar installation path:
<span class="bold-text">ln -s /path/to/corex /lib/modules</span>. Same applies to the <span class="bold-text">/usr/local/corex</span> directory.`,
'clusters.addworker.metaxNotes': `If the <span class="bold-text">/opt/mxdriver</span> directory does not exist, create a symbolic link to the MetaX installation path:
+1 -2
View File
@@ -19,8 +19,7 @@ export default {
'backend.mode.yaml': 'YAML Mode',
'backend.form.healthCheckPath': 'Health Check Path',
'backend.form.defaultExecuteCommand': 'Default Execution Command',
'backend.form.defaultExecuteCommand.tips':
'{{model_path}}, {{port}}, {{worker_ip}} and {{model_name}} are placeholders that will be substituted with the actual values during deployment.',
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}', '{{'port'}}', '{{'worker_ip'}}' and '{{'model_name'}}' are placeholders that will be substituted with the actual values during deployment.`,
'backend.form.defaultBackendParameters': 'Default Backend Parameters',
'backend.form.versionConfig': 'Versions Config',
'backend.form.addParameter': 'Add Parameter',
+2 -3
View File
@@ -51,8 +51,7 @@ export default {
'If multiple outbound IPs exist, specify the one you want the worker to use. Please double-check with <span class="bold-text">hostname -I | xargs -n1</span>.',
'clusters.addworker.nvidiaNotes-02':
'If a model directory already exists on the worker, you can specify the path to mount it.',
'clusters.addworker.hygonNotes': `If the <span class="bold-text">/opt/hyhal</span> directory does not exist, create a symbolic link to the Hygon installation path.
Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
'clusters.addworker.hygonNotes': `If <span class="bold-text">/opt/hyhal</span> or <span class="bold-text">/opt/dtk</span> does not exist, create symbolic links pointing to the corresponding Hygon installation paths, for example: <span class="desc-fill">ln -s /path/to/hyhal /opt/hyhal</span> <span class="desc-fill">ln -s /path/to/dtk /opt/dtk</span>.`,
'clusters.addworker.corexNotes': `If the <span class="bold-text">/lib/modules</span> directory does not exist, create a symbolic link to the Iluvatar installation path:
<span class="bold-text">ln -s /path/to/corex /lib/modules</span>. Same applies to the <span class="bold-text">/usr/local/corex</span> directory.`,
'clusters.addworker.metaxNotes': `If the <span class="bold-text">/opt/mxdriver</span> directory does not exist, create a symbolic link to the MetaX installation path:
@@ -165,7 +164,7 @@ Same applies to the <span class="bold-text">/opt/dtk</span> directory.`,
// 43. 'cluster.provider.comingsoon': 'Coming soon',
// 44. 'clusters.addworker.nvidiaNotes-01': 'If multiple outbound IPs exist, specify the one you want the worker to use. Please double-check with <span class="bold-text">hostname -I | xargs -n1</span>.',
// 45. 'clusters.addworker.nvidiaNotes-02': 'If a model directory already exists on the worker, you can specify the path to mount it.',
// 46. 'clusters.addworker.hygonNotes': 'If the <span class="bold-text">/opt/hyhal</span> directory does not exist, create a symbolic link to the Hygon installation path. Same applies to the <span class="bold-text">/opt/dtk</span> directory.',
// 46. 'clusters.addworker.hygonNotes': `If <span class="bold-text">/opt/hyhal</span> or <span class="bold-text">/opt/dtk</span> does not exist, create symbolic links pointing to the corresponding Hygon installation paths, for example: <span class="desc-fill">ln -s /path/to/hyhal /opt/hyhal</span> <span class="desc-fill">ln -s /path/to/dtk /opt/dtk</span>.`,
// 47. 'clusters.addworker.corexNotes': 'If the <span class="bold-text">/lib/modules</span> directory does not exist, create a symbolic link to the Iluvatar installation path: <span class="bold-text">ln -s /path/to/corex /lib/modules</span>. Same applies to the <span class="bold-text">/usr/local/corex</span> directory.',
// 48. 'clusters.addworker.metaxNotes': 'If the <span class="bold-text">/opt/mxdriver</span> directory does not exist, create a symbolic link to the MetaX installation path: <span class="bold-text">ln -s /path/to/metax /opt/mxdriver</span>. Same applies to the <span class="bold-text">/opt/maca</span> directory.',
// 49. 'clusters.addworker.cambriconNotes': 'If the <span class="bold-text">/usr/local/neuware</span> directory does not exist, create a symbolic link to the Cambricon installation path: <span class="bold-text">ln -s /path/to/neuware /usr/local/neuware</span>.'
+1 -2
View File
@@ -19,8 +19,7 @@ export default {
'backend.mode.yaml': 'Режим YAML',
'backend.form.healthCheckPath': 'Путь проверки здоровья',
'backend.form.defaultExecuteCommand': 'Команда выполнения по умолчанию',
'backend.form.defaultExecuteCommand.tips':
'{{model_path}}, {{port}}, {{worker_ip}} и {{model_name}} заполняются реальными значениями во время запуска',
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}', '{{'port'}}', '{{'worker_ip'}}' и '{{'model_name'}}' заполняются реальными значениями во время запуска`,
'backend.form.defaultBackendParameters': 'Параметры бэкенда по умолчанию',
'backend.form.versionConfig': 'Конфигурация версий',
'backend.form.addParameter': 'Добавить параметр',
+18 -37
View File
@@ -51,8 +51,7 @@ export default {
'Если существует несколько исходящих IP-адресов, укажите тот, который должен использовать воркер. Пожалуйста, перепроверьте с помощью <span class="bold-text">hostname -I | xargs -n1</span>.',
'clusters.addworker.nvidiaNotes-02':
'Если директория с моделями уже существует на воркере, вы можете указать путь для её монтирования.',
'clusters.addworker.hygonNotes':
'Если директория <span class="bold-text">/opt/hyhal</span> не существует, создайте символическую ссылку на путь установки Hygon: <span class="bold-text">/opt/hyhal</span>. Аналогично для директории <span class="bold-text">/opt/dtk</span>.',
'clusters.addworker.hygonNotes': `If <span class="bold-text">/opt/hyhal</span> or <span class="bold-text">/opt/dtk</span> does not exist, create symbolic links pointing to the corresponding Hygon installation paths, for example: <span class="desc-fill">ln -s /path/to/hyhal /opt/hyhal</span> <span class="desc-fill">ln -s /path/to/dtk /opt/dtk</span>.`,
'clusters.addworker.corexNotes':
'Если директория <span class="bold-text">/lib/modules</span> не существует, создайте символическую ссылку на путь установки Iluvatar: <span class="bold-text">ln -s /path/to/corex /lib/modules</span>. Аналогично для директории <span class="bold-text">/usr/local/corex</span>.',
'clusters.addworker.metaxNotes':
@@ -97,47 +96,29 @@ export default {
'напр. /data/cache (путь должен начинаться с /)',
'clusters.addworker.vendorNotes.title': 'Примечания для устройств {vendor}',
'clusters.button.genToken':
'Need to create a new token? Click <a href="{link}" target="_blank">here</a>.',
'clusters.addworker.amdNotes-01': `If the <span class="bold-text">/opt/rocm</span> directory does not exist, please create a symbolic link pointing to the ROCm installed path: <span class="bold-text">ln -s /path/to/rocm /opt/rocm</span>.`,
'Нужен новый токен? Нажмите <a href="{link}" target="_blank">здесь</a>.',
'clusters.addworker.amdNotes-01': `Если директория <span class="bold-text">/opt/rocm</span> не существует, создайте символическую ссылку на путь установки ROCm: <span class="bold-text">ln -s /путь/к/rocm /opt/rocm</span>.`,
'clusters.addworker.message.success_single':
'{count} new worker has been added to the cluster.',
'{count} новый воркер был добавлен в кластер.',
'clusters.addworker.message.success_multiple':
'{count} new workers have been added to the cluster.',
'clusters.create.serverUrl': 'Server URL',
'clusters.create.workerConfig': 'Worker Configuration',
'clusters.addworker.containerName': 'Worker Container Name',
'{count} новых воркеров были добавлены в кластер.',
'clusters.create.serverUrl': 'URL сервера',
'clusters.create.workerConfig': 'Конфигурация воркера',
'clusters.addworker.containerName': 'Имя контейнера воркера',
'clusters.addworker.containerName.tips':
'Specify a name for the worker container.',
'clusters.addworker.dataVolume': 'GPUStack Data Volume',
'Укажите имя для контейнера воркера.',
'clusters.addworker.dataVolume': 'Том данных GPUStack',
'clusters.addworker.dataVolume.tips':
'Specify a data storage path for GPUStack.',
'clusters.table.ip.internal': 'Internal',
'clusters.table.ip.external': 'External',
'Укажите путь для хранения данных GPUStack.',
'clusters.table.ip.internal': 'Внутренний',
'clusters.table.ip.external': 'Внешний',
'clusters.form.serverUrl.tips':
'Specify the server URL accessible from your cloud provider.',
'clusters.form.setDefault': 'Set as Default',
'clusters.form.setDefault.tips': 'Default for deployment.'
'Укажите URL сервера, доступный из вашего облачного провайдера.',
'clusters.form.setDefault': 'Установить по умолчанию',
'clusters.form.setDefault.tips':
'Использовать по умолчанию для развертывания.'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
// 1. 'clusters.addworker.amdNotes-01': `If the <span class="bold-text">/opt/rocm</span> directory does not exist, please create a symbolic link pointing to the ROCm installed path: <span class="bold-text">ln -s /path/to/rocm /opt/rocm</span>.`,
// 2. 'clusters.button.genToken': 'Need to create a new token? Click <a href="{link}" target="_blank">here</a>.',
// 3. 'clusters.addworker.cacheVolume': 'Model Cache Volume Mount',
// 4. 'clusters.addworker.cacheVolume.tips': 'If you want to customize the model cache directory, you can specify the path to mount it.',
// 5. 'clusters.addworker.message.success_single': '{count} new worker has been added to the cluster.',
// 6. 'clusters.addworker.message.success_multiple': '{count} new workers have been added to the cluster.',
// 7. 'clusters.create.serverUrl': 'Server URL',
// 8. 'clusters.create.workerConfig': 'Worker Configuration'
// 9. 'clusters.addworker.containerName': 'Worker Container Name',
// 10. 'clusters.addworker.containerName.tips':'Specify a name for the worker container.',
// 11. 'clusters.addworker.dataVolume': 'GPUStack Data Volume',
// 12. 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.',
// 10. 'clusters.table.ip.internal': 'Internal',
// 11. 'clusters.table.ip.external': 'External',
// 12. 'clusters.form.serverUrl.tips': 'Specify the server URL accessible from your cloud provider.'
// 13. 'clusters.addworker.specifyWorkerIP': 'Specify Worker IP <span class="text-tertiary">{type}</span>',
// 14. 'clusters.form.setDefault': 'Set as Default',
// 15. 'clusters.form.setDefault.tips': 'Default for deployment',
// 16. 'clusters.addworker.enterWorkerAddress': 'Enter worker external address',
// 17. 'clusters.addworker.enterWorkerAddress.error': 'Please enter the worker external address.',
// 1. 'clusters.addworker.hygonNotes': `If <span class="bold-text">/opt/hyhal</span> or <span class="bold-text">/opt/dtk</span> does not exist, create symbolic links pointing to the corresponding Hygon installation paths, for example: <span class="desc-fill">ln -s /path/to/hyhal /opt/hyhal</span> <span class="desc-fill">ln -s /path/to/dtk /opt/dtk</span>.`,
// ================================================================
+5 -8
View File
@@ -265,15 +265,12 @@ export default {
'common.form.rule.selectInput': 'Выберите или введите {name}',
'common.tag.experimental': 'Экспериментальный',
'common.title.example': 'Пример',
'common.button.dontshowagain': "Don't show again",
'common.sorter.tips.ascend': 'Click to sort ascending',
'common.sorter.tips.descend': 'Click to sort descending',
'common.sorter.tips.cancel': 'Click to cancel sorting'
'common.button.dontshowagain': "Больше не показывать",
'common.sorter.tips.ascend': 'Нажмите для сортировки по возрастанию',
'common.sorter.tips.descend': 'Нажмите для сортировки по убыванию',
'common.sorter.tips.cancel': 'Нажмите, чтобы отменить сортировку'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
// 1. 'common.button.dontshowagain': "Don't show again",
// 2. 'common.sorter.tips.ascend': 'Click to sort ascending',
// 3. 'common.sorter.tips.descend': 'Click to sort descending',
// 4. 'common.sorter.tips.cancel': 'Click to cancel sorting'
// ========== End of To-Do List ==========
+8 -14
View File
@@ -268,24 +268,18 @@ export default {
'Используйте следующий префикс пути и укажите имя модели либо в заголовке запроса <span class="bold-text">X-GPUStack-Model</span>, либо в поле model в теле запроса. Все запросы с этим префиксом пути будут перенаправлены в бэкенд вывода.',
'models.form.backendVersion.deprecated': 'Устаревший',
'models.accessSettings.public.desc':
'Accessible to anyone without authentication.',
'Доступно всем без аутентификации.',
'models.accessSettings.authed.tips':
'Accessible to all authenticated platform users.',
'Доступно всем аутентифицированным пользователям платформы.',
'models.accessSettings.allowedUsers.tips':
'Only designated users can access the model.',
'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`,
'Доступ к модели имеют только назначенные пользователи.',
'models.form.backendVersions.tips': `Чтобы использовать больше версий, перейдите на страницу {link} и отредактируйте бэкенд для добавления версий.`,
'models.catalog.nogpus.tips':
'No compatible GPUs are available in the selected cluster for this model.',
'models.form.modelfile.notfound': `The model file path you specified does not exist on the GPUStack server. It's recommended to place the model file at the same path on both the GPUStack server and GPUStack workers. This helps GPUStack make better decisions.`,
'models.form.readyWorkers': 'workers ready'
'В выбранном кластере нет доступных GPU, совместимых с этой моделью.',
'models.form.modelfile.notfound': `Указанный путь к файлу модели не существует на сервере GPUStack. Рекомендуется размещать файл модели по одному и тому же пути как на сервере GPUStack, так и на воркерах GPUStack. Это поможет системе принимать лучшие решения по распределению ресурсов.`,
'models.form.readyWorkers': 'воркеров готово'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
// 1. 'models.accessSettings.public.desc': 'Accessible to anyone without authentication.',
// 2. 'models.accessSettings.authed.tips': 'Accessible to all authenticated platform users.',
// 3. 'models.accessSettings.allowedUsers.tips': 'Only designated users can access the model.',
// 4. 'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`,
// 5. 'models.catalog.nogpus.tips': 'No compatible GPUs are available in the selected cluster for this model.',
// 6. 'models.form.modelfile.notfound': `The model file path you specified does not exist on the GPUStack server. It's recommended to place the model file at the same path on both the GPUStack server and GPUStack workers. This helps GPUStack make better decisions.`,
// 7. 'models.form.readyWorkers': 'workers ready'
// ========== End of To-Do List ==========
+2 -2
View File
@@ -40,9 +40,9 @@ export default {
'users.status.deactivate': 'Деактивировать аккаунт',
'users.status.inactiveAccount': 'Неактивный аккаунт',
'users.login.getInitialPassword':
'Run the following command on your server to retrieve the initial admin password.'
'Выполните следующую команду на вашем сервере, чтобы получить начальный пароль администратора.'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
// 1. 'users.login.getInitialPassword': 'Run the following command on your server to retrieve the initial admin password.'
// ========== End of To-Do List ==========
+1 -2
View File
@@ -19,8 +19,7 @@ export default {
'backend.mode.yaml': 'YAML 模式',
'backend.form.healthCheckPath': '健康检查路径',
'backend.form.defaultExecuteCommand': '默认执行命令',
'backend.form.defaultExecuteCommand.tips':
'{{model_path}}、{{port}}、{{worker_ip}} 和 {{model_name}} 都是占位符,在部署过程中会被替换为实际的值。',
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}'、'{{'port'}}'、'{{'worker_ip'}}' 和 '{{'model_name'}}' 都是占位符,在部署过程中会被替换为实际的值。`,
'backend.form.defaultBackendParameters': '默认后端参数',
'backend.form.versionConfig': '版本配置',
'backend.form.addParameter': '添加参数',
+3 -2
View File
@@ -49,8 +49,9 @@ export default {
'如果节点有多个出站 IP 地址,请填写 <span class="bold-text">WORKER_IP</span>,以确保使用指定的 IP。可通过命令 <span class="bold-text">hostname -I | xargs -n1</span> 进行确认。',
'clusters.addworker.nvidiaNotes-02':
'如果节点上已经存在模型目录,你可以指定该路径进行挂载。',
'clusters.addworker.hygonNotes':
'如果 <span class="bold-text">/opt/hyhal</span> 目录不存在,请创建指向海光安装路径的符号链接:<span class="bold-text">/opt/hyhal</span>。与 <span class="bold-text">/opt/dtk</span> 目录相同。',
'clusters.addworker.hygonNotes': `如果 <span class="bold-text">/opt/hyhal</span> 或 <span class="bold-text">/opt/dtk</span> 不存在,请创建指向对应海光安装路径的符号链接,例如:
<span class="desc-fill line-6">ln -s /path/to/hyhal /opt/hyhal</span>
<span class="desc-fill line-6">ln -s /path/to/dtk /opt/dtk</span>`,
'clusters.addworker.corexNotes':
'如果 <span class="bold-text">/lib/modules</span> 目录不存在,请创建指向天数智芯安装路径的符号链接:<span class="bold-text">ln -s /path/to/corex /lib/modules</span>。与 <span class="bold-text">/usr/local/corex</span> 目录相同。',
'clusters.addworker.metaxNotes':
@@ -164,6 +164,7 @@ const ClusterCreate = () => {
setCurrentStep((prev) => Math.min(prev + 1, steps.length - 1));
}
} catch (error) {
setSubmitLoading(false);
console.log('next error:', error);
}
};
+10 -13
View File
@@ -37,11 +37,7 @@ import {
K8sStepsFromCluter
} from './components/add-worker/config';
import PoolRows from './components/pool-rows';
import {
ClusterStatusValueMap,
ProviderType,
ProviderValueMap
} from './config';
import { ProviderType, ProviderValueMap } from './config';
import {
ClusterListItem,
CredentialListItem,
@@ -79,7 +75,8 @@ const Clusters: React.FC = () => {
useExpandedRowKeys(expandAtom);
const navigate = useNavigate();
const intl = useIntl();
const { handleAddWorker, AddWorkerModal, setStepList } = useAddWorker({});
const { handleAddWorker, checkDefaultCluster, AddWorkerModal, setStepList } =
useAddWorker({});
const [openAddModal, setOpenAddModal] = useState<{
open: boolean;
@@ -265,12 +262,12 @@ const Clusters: React.FC = () => {
dataSource.loadend &&
dataSource.dataList?.length > 0
) {
const targetCluster = dataSource.dataList.find(
(cluster) =>
cluster.state === ClusterStatusValueMap.Ready &&
!cluster.workers &&
!cluster.worker_pools?.length
);
const list = dataSource.dataList?.map((item) => ({
label: item.name,
value: item.id,
...item
}));
const targetCluster = checkDefaultCluster(list);
if (targetCluster) {
const actionMap = {
@@ -280,7 +277,7 @@ const Clusters: React.FC = () => {
};
handleSelect(
actionMap[targetCluster.provider as string],
targetCluster
targetCluster as ListItem
);
// reset session
setClusterSession(null);
@@ -17,7 +17,7 @@ interface AddWorkerContextProps {
token: string;
image: string;
server_url: string;
cluster_id: number;
cluster_id: number | null;
};
registerField: (key: SummaryDataKey) => () => void;
updateField: (key: SummaryDataKey, value: any) => void;
@@ -41,7 +41,7 @@ type AddWorkerProps = {
token: string;
image: string;
server_url: string;
cluster_id: number;
cluster_id: number | null;
[key: string]: any;
};
};
@@ -32,12 +32,12 @@ type AddWorkerProps = {
stepList: StepName[];
onClusterChange?: (value: number, row?: any) => void;
onCancel: () => void;
cluster_id: number;
cluster_id: number | null;
registrationInfo?: {
token: string;
image: string;
server_url: string;
cluster_id: number;
cluster_id: number | null;
};
};
@@ -64,12 +64,12 @@ const AddWorker: React.FC<AddWorkerProps> = (props) => {
token: string;
image: string;
server_url: string;
cluster_id: number;
cluster_id: number | null;
}>({
token: '',
image: '',
server_url: '',
cluster_id: 0
cluster_id: null
});
const handleOnClusterChange = async (value: number, row?: any) => {
@@ -21,12 +21,12 @@ const useAddWorker = (props: {
open: boolean;
provider: ProviderType;
title: string;
cluster_id: number;
cluster_id: number | null;
}>({
open: false,
provider: null,
title: '',
cluster_id: 0
cluster_id: null
});
const handleAddWorker = async (row: ClusterListItem) => {
@@ -43,7 +43,12 @@ const useAddWorker = (props: {
open: true,
title: title,
provider: row.provider as ProviderType,
cluster_id: row.id
cluster_id: [
ProviderValueMap.Docker,
ProviderValueMap.Kubernetes
].includes(row.provider as string)
? row.id
: null
});
} catch (error: any) {
message.error(error?.message || 'Failed to fetch cluster token');
@@ -67,6 +72,38 @@ const useAddWorker = (props: {
.filter((item) => item.state === ClusterStatusValueMap.Ready);
}, [clusterList]);
const checkDefaultCluster = (
clusterList: Global.BaseOption<number, ClusterListItem>[]
) => {
// select default and ready cluster first, digitalocean no READY state
let currentData = clusterList.find(
(item) => item.is_default && item.state === ClusterStatusValueMap.Ready
);
if (!currentData) {
// select docker ready cluster
currentData = clusterList.find(
(item) =>
item.provider === ProviderValueMap.Docker &&
item.state === ClusterStatusValueMap.Ready
);
}
if (!currentData) {
// select any ready cluster
currentData = clusterList.find(
(item) => item.state === ClusterStatusValueMap.Ready
);
}
if (!currentData) {
// maybe no ready a digitalocean cluster
currentData = clusterList[0];
}
return currentData || null;
};
const AddWorkerModal = (
<AddWorker
title={openAddWorker.title}
@@ -82,7 +119,7 @@ const useAddWorker = (props: {
open: false,
provider: null,
title: '',
cluster_id: 0
cluster_id: null
})
}
></AddWorker>
@@ -90,6 +127,7 @@ const useAddWorker = (props: {
return {
handleAddWorker,
checkDefaultCluster,
AddWorkerModal,
setStepList
};
@@ -1,4 +1,5 @@
import { clusterSessionAtom } from '@/atoms/clusters';
import { hideModalTemporarilyAtom } from '@/atoms/settings';
import IconFont from '@/components/icon-font';
import ScrollerModal from '@/components/scroller-modal/index';
import { PageAction } from '@/config';
@@ -60,7 +61,9 @@ export default function useAddResource(options?: { onCreated?: () => void }) {
const navigate = useNavigate();
const { setUserSettings, userSettings } = useUserSettings();
const [, setClusterSession] = useAtom(clusterSessionAtom);
const [hideModalTemporarily, setHideModalTemporarily] = useState(false);
const [hideModalTemporarily, setHideModalTemporarily] = useAtom(
hideModalTemporarilyAtom
);
const { fetchResource, resourceCount, resourceAtom } = useClusterList();
const [loadingStatus, setLoadingStatus] = useState({
@@ -153,6 +153,8 @@ export const useGenerateWorkerOptions = () => {
{ state: string; labels: Record<string, string>; cluster_id: number }
>[]
>([]);
const [, setClusterListAtom] = useAtom(clusterListAtom);
const [, setWorkerListAtom] = useAtom(workerListAtom);
const generateCascaderWorkerOptions = (
workerList: WorkerListItem[],
@@ -201,29 +203,31 @@ export const useGenerateWorkerOptions = () => {
const data = await getDataList();
const [workerList, clusterList] = data;
generateCascaderWorkerOptions(workerList, clusterList);
setWorkersList(
workerList.map((item) => ({
cluster_id: item.cluster_id,
state: item.state,
label: item.name,
value: item.id,
id: item.id,
labels: item.labels || {},
name: item.name
}))
);
setClusterList(
clusterList.map((item) => ({
label: item.name,
value: item.id,
provider: item.provider as string,
state: item.state,
is_default: item.is_default,
workers: item.workers,
ready_workers: item.ready_workers,
gpus: item.gpus
}))
);
const workerOptions = workerList.map((item) => ({
cluster_id: item.cluster_id,
state: item.state,
label: item.name,
value: item.id,
id: item.id,
labels: item.labels || {},
name: item.name
}));
const clusterOptions = clusterList.map((item) => ({
label: item.name,
value: item.id,
provider: item.provider as string,
state: item.state,
is_default: item.is_default,
workers: item.workers,
ready_workers: item.ready_workers,
gpus: item.gpus
}));
setWorkersList(workerOptions);
setClusterList(clusterOptions);
setWorkerListAtom(workerOptions);
setClusterListAtom(clusterOptions);
};
return {
getWorkerOptionList,
@@ -280,7 +284,12 @@ export default function useFormInitialValues() {
const list =
data.items?.map((item) => ({
label: item.name,
value: item.id
value: item.id,
cluster_id: item.cluster_id,
state: item.state,
id: item.id,
labels: item.labels || {},
name: item.name
})) || [];
setWorkerList(data.items);
setWorkerListAtom(list);
+2
View File
@@ -1,3 +1,4 @@
import { hideModalTemporarilyAtom } from '@/atoms/settings';
import { userAtom } from '@/atoms/user';
import { clearAtomStorage, clearStorageUserSettings } from '@/atoms/utils';
import { request } from '@umijs/max';
@@ -29,6 +30,7 @@ export const logout = async (userInfo?: any) => {
});
clearStorageUserSettings();
clearAtomStorage(userAtom);
clearAtomStorage(hideModalTemporarilyAtom);
if (res?.logout_url) {
window.location.href = res.logout_url;
@@ -14,6 +14,7 @@ interface ActiveModelsProps {
const ActiveModels: React.FC<ActiveModelsProps> = (props) => {
const { spans, modelSelections, setModelRefs } = props;
return (
<Row gutter={[16, 0]} style={{ height: '100%' }}>
{modelSelections.map((model, index) => (
@@ -21,7 +22,8 @@ const ActiveModels: React.FC<ActiveModelsProps> = (props) => {
span={spans.span}
key={`${model.value || 'empty'}-${model.uid}`}
style={{
height: spans.count < 4 ? 'calc(100% - 16px)' : 'calc(50% - 16px)'
height: spans.count < 4 ? 'calc(100% - 16px)' : 'calc(50% - 16px)',
overflow: 'hidden'
}}
>
<ModelItem
@@ -47,7 +47,8 @@ interface ModelItemProps {
}
const ModelItem: React.FC<ModelItemProps> = forwardRef((props, ref) => {
const { modelList, model, instanceId } = props;
const { modelList, ...restProps } = props;
const { model, instanceId } = restProps;
const {
globalParams,
setGlobalParams,
@@ -66,14 +67,20 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef((props, ref) => {
paramsConfig,
initialValues,
parameters
} = useInitLLmMeta(props, {
defaultValues: {
...llmInitialValues,
model: model
} = useInitLLmMeta(
{
...restProps,
modelList: modelFullList
},
defaultParamsConfig: ChatParamsConfig,
metaKeys: LLM_METAKEYS
});
{
defaultValues: {
...llmInitialValues,
model: model
},
defaultParamsConfig: ChatParamsConfig,
metaKeys: LLM_METAKEYS
}
);
const intl = useIntl();
const isApplyToAllModels = useRef(false);
const [systemMessage, setSystemMessage] = useState<string>('');
+54 -67
View File
@@ -12,13 +12,7 @@ import { generateRandomNumber } from '@/utils';
import { useSearchParams } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import _ from 'lodash';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState
} from 'react';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { ParamsSchema } from '../config/types';
import {
IMG_METAKEYS,
@@ -133,37 +127,34 @@ export const useInitLLmMeta = (
return fields?.join(',');
}, [paramsConfig]);
const handleOnModelChange = useCallback(
(val: string) => {
if (!val) return;
const model = modelList.find((item) => item.value === val);
const { form: initialData, meta } = extractLLMMeta(model?.meta);
setModelMeta(meta);
setInitialValues({
...initialData,
model: val
});
setParams({
...initialData,
model: val
});
const config = defaultParamsConfig.map((item) => {
return {
...item,
attrs:
item.name === 'max_tokens'
? { ...item.attrs, max: meta.max_tokens }
: {
...item.attrs
}
};
});
setParamsConfig(config);
},
[modelList, defaultParamsConfig]
);
const handleOnModelChange = useMemoizedFn((val: string) => {
if (!val) return;
const model = modelList.find((item) => item.value === val);
const { form: initialData, meta } = extractLLMMeta(model?.meta);
setModelMeta(meta);
setInitialValues({
...initialData,
model: val
});
setParams({
...initialData,
model: val
});
const config = defaultParamsConfig.map((item) => {
return {
...item,
attrs:
item.name === 'max_tokens'
? { ...item.attrs, max: meta.max_tokens }
: {
...item.attrs
}
};
});
setParamsConfig(config);
});
const handleOnValuesChange = useCallback(
const handleOnValuesChange = useMemoizedFn(
(changeValues: Record<string, any>, allValues: Record<string, any>) => {
if (changeValues.model) {
handleOnModelChange(changeValues.model);
@@ -172,8 +163,7 @@ export const useInitLLmMeta = (
setParams(allValues);
setInitialValues(allValues);
}
},
[handleOnModelChange]
}
);
useEffect(() => {
@@ -405,35 +395,32 @@ export const useInitImageMeta = (
return fields?.join(',');
}, [paramsConfig]);
const handleOnModelChange = useCallback(
(val: string) => {
if (!val) return;
const model = modelList.find((item) => item.value === val);
const { form: initialData, sizeOptions } = extractIMGMeta(model?.meta);
const newParamsConfig = generateImageParamsConfig(model, sizeOptions);
const handleOnModelChange = useMemoizedFn((val: string) => {
if (!val) return;
const model = modelList.find((item) => item.value === val);
const { form: initialData, sizeOptions } = extractIMGMeta(model?.meta);
const newParamsConfig = generateImageParamsConfig(model, sizeOptions);
if (!isOpenaiCompatible) {
setParamsConfig([...newParamsConfig, ...ImageAdvancedParamsConfig]);
} else {
setParamsConfig(newParamsConfig);
}
setBasicParamsConfig(newParamsConfig);
setImageSizeOptions(sizeOptions);
setModelMeta(model?.meta || {});
setInitialValues({
...initialData,
seed: parameters.seed,
model: val
});
setParams({
...initialData,
seed: parameters.seed,
model: val
});
updateCacheFormData(initialData);
},
[modelList, isOpenaiCompatible]
);
if (!isOpenaiCompatible) {
setParamsConfig([...newParamsConfig, ...ImageAdvancedParamsConfig]);
} else {
setParamsConfig(newParamsConfig);
}
setBasicParamsConfig(newParamsConfig);
setImageSizeOptions(sizeOptions);
setModelMeta(model?.meta || {});
setInitialValues({
...initialData,
seed: parameters.seed,
model: val
});
setParams({
...initialData,
seed: parameters.seed,
model: val
});
updateCacheFormData(initialData);
});
const handleOnValuesChange = useMemoizedFn(
(changeValues: Record<string, any>, allValues: Record<string, any>) => {
+9 -18
View File
@@ -5,10 +5,6 @@ import useTableFetch from '@/hooks/use-table-fetch';
import PageBox from '@/pages/_components/page-box';
import { queryClusterList } from '@/pages/cluster-management/apis';
import { DockerStepsFromWorker } from '@/pages/cluster-management/components/add-worker/config';
import {
ClusterStatusValueMap,
ProviderValueMap
} from '@/pages/cluster-management/config';
import { ClusterListItem } from '@/pages/cluster-management/config/types';
import useAddWorker from '@/pages/cluster-management/hooks/use-add-worker';
import useNoResourceResult from '@/pages/llmodels/hooks/use-no-resource-result';
@@ -82,10 +78,11 @@ const Workers: React.FC = () => {
open: false,
currentData: null
});
const { handleAddWorker, AddWorkerModal, setStepList } = useAddWorker({
clusterList: clusterData.list,
clusterLoading: clusterData.loading
});
const { handleAddWorker, checkDefaultCluster, AddWorkerModal, setStepList } =
useAddWorker({
clusterList: clusterData.list,
clusterLoading: clusterData.loading
});
const getClusterList = async () => {
try {
@@ -105,6 +102,7 @@ const Workers: React.FC = () => {
value: item.id,
id: item.id,
state: item.state,
is_default: item.is_default,
provider: item.provider
}));
setClusterData({
@@ -193,16 +191,9 @@ const Workers: React.FC = () => {
});
const handleOnAddWorker = () => {
let currentData = clusterData.list.find(
(item) =>
item.provider === ProviderValueMap.Docker &&
item.state === ClusterStatusValueMap.Ready
);
if (!currentData) {
currentData = clusterData.list[0];
}
if (currentData) {
handleAddWorker(currentData as ClusterListItem);
const targetCluster = checkDefaultCluster(clusterData.list);
if (targetCluster) {
handleAddWorker(targetCluster as ClusterListItem);
} else {
message.info(intl.formatMessage({ id: 'noresult.resources.cluster' }));
}