Compare commits
183
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
853095d951 | ||
|
|
99c95b87e5 | ||
|
|
39a6455e7b | ||
|
|
958c7ef201 | ||
|
|
542289a318 | ||
|
|
5c1bbe5bf7 | ||
|
|
f26abd4b5f | ||
|
|
2767f883fc | ||
|
|
e98651da2b | ||
|
|
84304eadb7 | ||
|
|
ab3739219e | ||
|
|
7cf4932f3a | ||
|
|
032f1eefef | ||
|
|
e35be94f7c | ||
|
|
e76e2ad85a | ||
|
|
cb19cd2476 | ||
|
|
c532732ce9 | ||
|
|
2bc10dbb2e | ||
|
|
b634a03ef9 | ||
|
|
ac64bb98ad | ||
|
|
aa49539210 | ||
|
|
1f5678f781 | ||
|
|
7adc089e94 | ||
|
|
6e1e9c07ee | ||
|
|
1d55382f3b | ||
|
|
e68bb0bc4e | ||
|
|
c8e14dade5 | ||
|
|
e4c1a3f5c2 | ||
|
|
cdd6ef9cb3 | ||
|
|
737d43fd16 | ||
|
|
9f5163531c | ||
|
|
df5cf0f4e5 | ||
|
|
e160b64697 | ||
|
|
7d36c6ac4a | ||
|
|
0820edf55f | ||
|
|
12e324893f | ||
|
|
4017c4dd33 | ||
|
|
17b65d80e1 | ||
|
|
47cb3321ab | ||
|
|
547b9f617b | ||
|
|
9e7bd78693 | ||
|
|
c53d60b628 | ||
|
|
9566e0cdb2 | ||
|
|
ed3e779d22 | ||
|
|
3e2133753d | ||
|
|
736a853192 | ||
|
|
08ef2ffa9a | ||
|
|
6cf512cccd | ||
|
|
6f56e614f3 | ||
|
|
8c06862705 | ||
|
|
48159171c2 | ||
|
|
d5d0749b72 | ||
|
|
dc4dd81e11 | ||
|
|
c736e2186d | ||
|
|
7a76bda484 | ||
|
|
778ea36a9d | ||
|
|
04e1e1e566 | ||
|
|
01f66085ab | ||
|
|
ddf723525a | ||
|
|
7b18a92388 | ||
|
|
b985dcee63 | ||
|
|
77e92432ed | ||
|
|
a7b33a928b | ||
|
|
1218c6d096 | ||
|
|
6337431ed4 | ||
|
|
2ba5118bc2 | ||
|
|
12649c7ccf | ||
|
|
5d96932870 | ||
|
|
e6cfa6cf77 | ||
|
|
55d16c5681 | ||
|
|
ab75685955 | ||
|
|
bd0bf0bf63 | ||
|
|
fd050b33d9 | ||
|
|
698090b17a | ||
|
|
6f96260993 | ||
|
|
98a6b19298 | ||
|
|
ded270ff25 | ||
|
|
c8860207e8 | ||
|
|
8d045ffd8b | ||
|
|
fd7437e74a | ||
|
|
807ba866d3 | ||
|
|
5e2808d7c4 | ||
|
|
a26a3ff8f9 | ||
|
|
a2e9f69b96 | ||
|
|
ac800a303a | ||
|
|
dd542a9f29 | ||
|
|
26ce456bde | ||
|
|
7ce1faaf6c | ||
|
|
878bdaa5a7 | ||
|
|
68a7e3f66a | ||
|
|
feccc81c5f | ||
|
|
620045d7a3 | ||
|
|
6dc28d61b0 | ||
|
|
754cc29793 | ||
|
|
ed525509ea | ||
|
|
2b6f26e6c8 | ||
|
|
09821aab85 | ||
|
|
636190b8e6 | ||
|
|
e420c22a45 | ||
|
|
993f6edcc4 | ||
|
|
bda8264788 | ||
|
|
ab8e792276 | ||
|
|
0e4be63b6b | ||
|
|
d3ebea9090 | ||
|
|
15a9e23243 | ||
|
|
eab7d9c77f | ||
|
|
4a86b71018 | ||
|
|
6350f7f11a | ||
|
|
ce6388253c | ||
|
|
d8f6064d5e | ||
|
|
9f5d95f565 | ||
|
|
0cabbeb81c | ||
|
|
76618aa3a1 | ||
|
|
b19db0f67d | ||
|
|
76f228bfe7 | ||
|
|
be40692773 | ||
|
|
42ac377c17 | ||
|
|
46f859cc3c | ||
|
|
f2bff0d1fb | ||
|
|
0bd98b9662 | ||
|
|
1ac2c02197 | ||
|
|
907187d53a | ||
|
|
faf6246fe6 | ||
|
|
0c16e2be7a | ||
|
|
3212882895 | ||
|
|
62f34ffcaf | ||
|
|
50601b0aff | ||
|
|
4c5d42cb13 | ||
|
|
85268b8917 | ||
|
|
973d2c4529 | ||
|
|
ab766b8c54 | ||
|
|
4945242c87 | ||
|
|
c4a72eb375 | ||
|
|
b12d11728d | ||
|
|
8f0e3a7576 | ||
|
|
4810ff2dbe | ||
|
|
a221b22be9 | ||
|
|
24b458530d | ||
|
|
caea43590a | ||
|
|
c3d7a0b85e | ||
|
|
5c6f28027d | ||
|
|
dc04529f5b | ||
|
|
2bb04a419d | ||
|
|
8e31b71668 | ||
|
|
f055453889 | ||
|
|
820a6cecdd | ||
|
|
c3a779e4c8 | ||
|
|
00008abf03 | ||
|
|
d9c6f2f780 | ||
|
|
9a4d77e869 | ||
|
|
2407416e33 | ||
|
|
234e42ccfa | ||
|
|
4426b8dc30 | ||
|
|
9541f49f39 | ||
|
|
9f7a99f370 | ||
|
|
12f0bb2f98 | ||
|
|
ac45a72e0e | ||
|
|
9705818b15 | ||
|
|
39374eafda | ||
|
|
110bc3205e | ||
|
|
57fec89f2d | ||
|
|
72e794c281 | ||
|
|
fadbcc3e44 | ||
|
|
9e30e964e6 | ||
|
|
fbb707d014 | ||
|
|
d2d82ee5ee | ||
|
|
9061fa08ba | ||
|
|
f29236518e | ||
|
|
6537de5224 | ||
|
|
0413d470a8 | ||
|
|
c01f4d3ce8 | ||
|
|
dd9d359bb0 | ||
|
|
b444c35643 | ||
|
|
abd0d52fad | ||
|
|
c8771dd3b9 | ||
|
|
6d16f56183 | ||
|
|
95ffe0350e | ||
|
|
e314f25a77 | ||
|
|
65ea65e9ae | ||
|
|
4ce975562e | ||
|
|
07e625f2bc | ||
|
|
d95fd8dc90 | ||
|
|
55028cf3a3 |
@@ -0,0 +1,57 @@
|
||||
# Agent Instructions
|
||||
|
||||
This project keeps a single source of truth for agent/contributor conventions in [`CLAUDE.md`](./CLAUDE.md). **Read [`CLAUDE.md`](./CLAUDE.md) and follow it.**
|
||||
|
||||
@CLAUDE.md
|
||||
|
||||
## Downstream fork workflow
|
||||
|
||||
This repo is a **downstream fork** that customizes the product appearance on top of upstream `gpustack/gpustack-ui`. The mirror chain is:
|
||||
|
||||
```
|
||||
upstream https://github.com/gpustack/gpustack-ui.git
|
||||
| (fetch)
|
||||
origin ssh://git@192.168.0.23:11022/root/gpustack-ui.git (private registry, also https://git.digiman.live)
|
||||
```
|
||||
|
||||
### Goal
|
||||
|
||||
Track upstream releases. When upstream changes, we pull it in, then apply our own appearance/customization changes on a dedicated branch so we keep our look-and-feel on top of the latest upstream product.
|
||||
|
||||
### Branch naming
|
||||
|
||||
Customization work lives on `v<upstream-version>-lofyer` branches (e.g. `v2.2.0-lofyer`). Each time upstream ships a new version we want to follow, create a new `v<version>-lofyer` branch from the corresponding upstream tag/branch and re-apply (or rebase) our customizations onto it.
|
||||
|
||||
### Syncing from upstream
|
||||
|
||||
`scripts/sync-github` mirrors upstream into the private `origin` (full mirror, force push of all branches + tags). It auto-configures the `upstream` remote on first run.
|
||||
|
||||
```bash
|
||||
# preview only, no push
|
||||
DRY_RUN=1 ./scripts/sync-github
|
||||
|
||||
# real sync (force-pushes every upstream branch + tag to origin)
|
||||
./scripts/sync-github
|
||||
|
||||
# also delete origin branches that no longer exist upstream (true mirror, destructive)
|
||||
PRUNE_BRANCHES=1 ./scripts/sync-github
|
||||
```
|
||||
|
||||
Env knobs: `UPSTREAM_URL`, `ORIGIN_REMOTE`, `UPSTREAM_REMOTE`, `PRUNE_BRANCHES`, `DRY_RUN`. After syncing, branch a fresh `v<version>-lofyer` off the updated upstream ref and apply the appearance changes there.
|
||||
|
||||
### Re-applying brand customizations
|
||||
|
||||
`scripts/rebrand` swaps the standalone brand word `GPUStack` for our brand (`MesaStack`) across user-facing text. It is the first appearance change to re-apply on every new `v<version>-lofyer` branch.
|
||||
|
||||
```bash
|
||||
# preview hits, no writes
|
||||
DRY_RUN=1 ./scripts/rebrand
|
||||
|
||||
# apply (default GPUStack -> MesaStack)
|
||||
./scripts/rebrand
|
||||
|
||||
# custom brand words
|
||||
FROM=GPUStack TO=AcmeStack ./scripts/rebrand
|
||||
```
|
||||
|
||||
It deliberately **does not** touch functional references — lowercase `gpustack` (npm pkg / URLs / paths / k8s namespace), ALL-CAPS `GPUSTACK_*` constants, JS identifiers like `getGPUStackPlugin`, and `X-*` HTTP headers — and carries a line-level skip list for backend-contract strings matched at runtime (see `SKIP_LINE_PATTERNS` in the script). Always review `git diff` afterwards. Logo images under `src/assets/images/` are NOT changed by the script — replace those PNGs separately when new brand assets are available.
|
||||
@@ -1,6 +1,6 @@
|
||||
# GPUStack UI
|
||||
# MesaStack UI
|
||||
|
||||
UI for [GPUStack](https://github.com/gpustack/gpustack).
|
||||
UI for [MesaStack](https://github.com/gpustack/gpustack).
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ export default defineConfig({
|
||||
antd: {
|
||||
style: 'less'
|
||||
},
|
||||
title: 'GPUStack',
|
||||
title: 'MesaStack',
|
||||
hash: true,
|
||||
access: {},
|
||||
model: {},
|
||||
|
||||
@@ -31,5 +31,6 @@ export default function createProxyTable(target?: string) {
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
return proxyTable;
|
||||
}
|
||||
|
||||
+73
-42
@@ -140,15 +140,6 @@ const baseRoutes = [
|
||||
access: 'canSeeOrgAdmin',
|
||||
component: './model-routes/index'
|
||||
},
|
||||
{
|
||||
name: 'usage',
|
||||
path: '/models/usage',
|
||||
key: 'usage',
|
||||
icon: 'icon-usage-outlined',
|
||||
selectedIcon: 'icon-usage-filled',
|
||||
defaultIcon: 'icon-usage-outlined',
|
||||
component: './usage/index'
|
||||
},
|
||||
{
|
||||
name: 'providers',
|
||||
path: '/models/providers',
|
||||
@@ -197,6 +188,7 @@ const baseRoutes = [
|
||||
icon: 'icon-files',
|
||||
selectedIcon: 'icon-files-filled',
|
||||
defaultIcon: 'icon-files',
|
||||
access: 'canSeeOrgAdmin',
|
||||
component: './resources/components/model-files'
|
||||
}
|
||||
]
|
||||
@@ -274,6 +266,16 @@ const baseRoutes = [
|
||||
path: '/resources',
|
||||
redirect: '/resources/workers'
|
||||
},
|
||||
{
|
||||
name: 'clusters',
|
||||
path: '/resources/clusters/list',
|
||||
key: 'clusters',
|
||||
icon: 'icon-cluster2-outline',
|
||||
selectedIcon: 'icon-cluster2-filled',
|
||||
defaultIcon: 'icon-cluster2-outline',
|
||||
component: './cluster-management/clusters',
|
||||
subMenu: ['/resources/clusters/detail', '/resources/clusters/create']
|
||||
},
|
||||
{
|
||||
name: 'workers',
|
||||
path: '/resources/workers',
|
||||
@@ -291,50 +293,65 @@ const baseRoutes = [
|
||||
selectedIcon: 'icon-gpu-filled',
|
||||
defaultIcon: 'icon-gpu1',
|
||||
component: './resources/components/gpus'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'clusterManagement',
|
||||
path: '/cluster-management',
|
||||
key: 'clusterManagement',
|
||||
access: 'canSeeOrgAdmin',
|
||||
routes: [
|
||||
{
|
||||
path: '/cluster-management',
|
||||
redirect: '/cluster-management/clusters/list'
|
||||
},
|
||||
{
|
||||
name: 'clusters',
|
||||
path: '/cluster-management/clusters/list',
|
||||
key: 'clusters',
|
||||
icon: 'icon-cluster2-outline',
|
||||
selectedIcon: 'icon-cluster2-filled',
|
||||
defaultIcon: 'icon-cluster2-outline',
|
||||
component: './cluster-management/clusters',
|
||||
subMenu: [
|
||||
'/cluster-management/clusters/detail',
|
||||
'/cluster-management/clusters/create'
|
||||
]
|
||||
name: 'credentials',
|
||||
path: '/resources/credentials',
|
||||
key: 'credentials',
|
||||
icon: 'icon-credential-outline',
|
||||
selectedIcon: 'icon-credential-filled',
|
||||
defaultIcon: 'icon-credential-outline',
|
||||
component: './cluster-management/credentials'
|
||||
},
|
||||
{
|
||||
name: 'clusterDetail',
|
||||
path: '/cluster-management/clusters/detail',
|
||||
path: '/resources/clusters/detail',
|
||||
key: 'clusterDetail',
|
||||
icon: 'icon-cluster2-outline',
|
||||
selectedIcon: 'icon-cluster2-filled',
|
||||
defaultIcon: 'icon-cluster2-outline',
|
||||
hideInMenu: true,
|
||||
component: './cluster-management/cluster-detail'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
// Cross-resource consumption (tokens + GPU/CPU instances + storage).
|
||||
// A folder so it matches the other top-level groups; more usage views can
|
||||
// graduate in here later.
|
||||
name: 'billingAndUsage',
|
||||
path: '/usage',
|
||||
key: 'usageGroup',
|
||||
icon: 'icon-usage-outlined',
|
||||
selectedIcon: 'icon-usage-filled',
|
||||
defaultIcon: 'icon-usage-outlined',
|
||||
routes: [
|
||||
{
|
||||
path: '/usage',
|
||||
redirect: '/usage/overview'
|
||||
},
|
||||
{
|
||||
name: 'credentials',
|
||||
path: '/cluster-management/credentials',
|
||||
key: 'credentials',
|
||||
icon: 'icon-credential-outline',
|
||||
selectedIcon: 'icon-credential-filled',
|
||||
defaultIcon: 'icon-credential-outline',
|
||||
component: './cluster-management/credentials'
|
||||
name: 'usage',
|
||||
path: '/usage/overview',
|
||||
key: 'usage',
|
||||
icon: 'icon-usage-outlined',
|
||||
selectedIcon: 'icon-usage-filled',
|
||||
defaultIcon: 'icon-usage-outlined',
|
||||
component: './usage/index'
|
||||
},
|
||||
{
|
||||
name: 'billing',
|
||||
path: '/usage/billing',
|
||||
key: 'billing',
|
||||
icon: 'icon-billing-outlined',
|
||||
selectedIcon: 'icon-billing-filled',
|
||||
defaultIcon: 'icon-billing-outlined',
|
||||
hideInMenu: process.env.ENABLE_ENTERPRISE === 'true',
|
||||
// OSS exposes the menu as a teaser for the enterprise billing
|
||||
// module. The page itself just renders an upsell notice — the real
|
||||
// billing UI lives in the enterprise plugin and shadows this route
|
||||
// via `routes.extensions.ts`.
|
||||
component: './billing'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -347,6 +364,20 @@ const baseRoutes = [
|
||||
path: '/access-control',
|
||||
redirect: '/access-control/users'
|
||||
},
|
||||
{
|
||||
name: 'organizations',
|
||||
path: '/access-control/organizations',
|
||||
key: 'organizations',
|
||||
icon: 'icon-org-outlined',
|
||||
selectedIcon: 'icon-org-filled',
|
||||
defaultIcon: 'icon-org-outlined',
|
||||
// OSS exposes the menu to platform admins as a teaser for the
|
||||
// enterprise multi-tenancy module. The page itself just renders
|
||||
// an upsell notice — the real CRUD UI lives in the enterprise
|
||||
// plugin and shadows this route via `routes.extensions.ts`.
|
||||
access: 'canSeeAdmin',
|
||||
component: './organizations'
|
||||
},
|
||||
{
|
||||
name: 'users',
|
||||
path: '/access-control/users',
|
||||
@@ -380,8 +411,8 @@ const baseRoutes = [
|
||||
},
|
||||
{
|
||||
name: 'profile',
|
||||
path: '/profile',
|
||||
key: 'profile',
|
||||
path: '/preferences',
|
||||
key: 'preferences',
|
||||
hideInMenu: true,
|
||||
component: './profile',
|
||||
icon: 'User'
|
||||
|
||||
+24
-10
@@ -1,13 +1,27 @@
|
||||
const child_process = require('child_process');
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
export const getBranchInfo = () => {
|
||||
const latestCommit = child_process
|
||||
.execSync('git rev-parse HEAD')
|
||||
.toString()
|
||||
.trim();
|
||||
const versionTag = child_process
|
||||
.execSync(`git tag --contains ${latestCommit}`)
|
||||
.toString()
|
||||
.trim();
|
||||
return { version: versionTag || '', commitId: latestCommit.slice(0, 7) };
|
||||
// git may be absent (source archive, bare container) or this tree may
|
||||
// not be a git checkout. Swallow the failure and fall back to the env
|
||||
// overrides below — losing build info shouldn't fail the build.
|
||||
let latestCommit = '';
|
||||
let versionTag = '';
|
||||
try {
|
||||
latestCommit = execSync('git rev-parse HEAD').toString().trim();
|
||||
versionTag = execSync(`git tag --contains ${latestCommit}`)
|
||||
.toString()
|
||||
.trim();
|
||||
} catch {
|
||||
// Not a git checkout / git unavailable; rely on env overrides.
|
||||
}
|
||||
// Respect explicit GPUSTACK_UI_* overrides so a wrapping build that
|
||||
// checks this source tree out as a sub-package can stamp its own
|
||||
// release tag and commit id onto the UI (otherwise the panel reports
|
||||
// the host tree's git HEAD, which the wrapper doesn't control).
|
||||
const overrideVersion = process.env.GPUSTACK_UI_VERSION?.trim();
|
||||
const overrideCommitId = process.env.GPUSTACK_UI_COMMIT_ID?.trim();
|
||||
return {
|
||||
version: overrideVersion || versionTag || '',
|
||||
commitId: overrideCommitId || latestCommit.slice(0, 7)
|
||||
};
|
||||
};
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
"@ant-design/pro-components": "3.1.0-0",
|
||||
"@antv/g6": "^5.0.51",
|
||||
"@braintree/sanitize-url": "^7.1.1",
|
||||
"@gpustack/core-ui": "^1.0.19",
|
||||
"@gpustack/core-ui": "^1.0.27",
|
||||
"@huggingface/gguf": "^0.1.7",
|
||||
"@huggingface/hub": "^0.15.1",
|
||||
"@huggingface/tasks": "^0.11.6",
|
||||
|
||||
@@ -5,6 +5,8 @@ export default (api: IApi) => {
|
||||
const info = JSON.parse(process.env.VERSION || '{}');
|
||||
const env = process.env.NODE_ENV;
|
||||
|
||||
$('html').attr('lang', 'en');
|
||||
|
||||
$('html').attr('data-env', env);
|
||||
|
||||
$('html').attr(
|
||||
|
||||
Generated
+5
-5
@@ -24,8 +24,8 @@ importers:
|
||||
specifier: ^7.1.1
|
||||
version: 7.1.2
|
||||
'@gpustack/core-ui':
|
||||
specifier: ^1.0.19
|
||||
version: 1.0.19(czdvzceysqw7iv6pct2ucnb23e)
|
||||
specifier: ^1.0.27
|
||||
version: 1.0.27(czdvzceysqw7iv6pct2ucnb23e)
|
||||
'@huggingface/gguf':
|
||||
specifier: ^0.1.7
|
||||
version: 0.1.18
|
||||
@@ -1484,8 +1484,8 @@ packages:
|
||||
resolution: {integrity: sha512-KWk80UPIzPmUg+P0rKh6TqspRw0G6eux1PuJr+zz47ftMaZ9QDwbGzHZbtzWkl5hgayM/qrKRutllRC7D/vVXQ==, tarball: https://registry.npmjs.org/@formatjs/intl-utils/-/intl-utils-2.3.0.tgz}
|
||||
deprecated: the package is rather renamed to @formatjs/ecma-abstract with some changes in functionality (primarily selectUnit is removed and we don't plan to make any further changes to this package
|
||||
|
||||
'@gpustack/core-ui@1.0.19':
|
||||
resolution: {integrity: sha512-EI2inYoTDdWYdxXXm0BM2DQ7chysnbIXICivBMeUxxhc8IkEcwaQfNqo/KUtA/bkqjDieRxFC1JlsDIIhbcL3A==, tarball: https://registry.npmjs.org/@gpustack/core-ui/-/core-ui-1.0.19.tgz}
|
||||
'@gpustack/core-ui@1.0.27':
|
||||
resolution: {integrity: sha512-m3ue0EHFKULla0mpnpxZwn9LVaGKS+HnuzQYSBECQa4vaP8MEEQsR7oFBtG8bhWUWYA9VzP6k7fjozDkP6TymA==, tarball: https://registry.npmjs.org/@gpustack/core-ui/-/core-ui-1.0.27.tgz}
|
||||
peerDependencies:
|
||||
'@ant-design/icons': '>=6.0.0'
|
||||
'@ant-design/pro-components': 3.1.0-0
|
||||
@@ -10808,7 +10808,7 @@ snapshots:
|
||||
|
||||
'@formatjs/intl-utils@2.3.0': {}
|
||||
|
||||
'@gpustack/core-ui@1.0.19(czdvzceysqw7iv6pct2ucnb23e)':
|
||||
'@gpustack/core-ui@1.0.27(czdvzceysqw7iv6pct2ucnb23e)':
|
||||
dependencies:
|
||||
'@ant-design/icons': 6.2.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@ant-design/pro-components': 3.1.0-0(antd@6.3.7(date-fns@2.30.0)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# rebrand: 把面向用户的品牌标识从 GPUStack 批量替换为 MesaStack(或自定义品牌)。
|
||||
#
|
||||
# 设计目标:上游每次更新后,在新的 v<version>-lofyer 分支上跑一次即可重新应用品牌定制。
|
||||
#
|
||||
# 只替换「独立的品牌词」FROM,并刻意跳过所有功能性引用:
|
||||
# - 小写 `gpustack` —— npm 包名 (@gpustack/core-ui)、URL、路径、k8s 命名空间、author(大小写敏感,天然不匹配)
|
||||
# - 全大写 `GPUSTACK` —— 常量/环境变量/全局 (GPUSTACK_API_BASE_URL, __GPUSTACK_*__, GPUSTACK_UI_*)(大小写敏感,不匹配)
|
||||
# - 代码标识符 —— getGPUStackPlugin / GPUStackVersionAtom / GPUStackPluginManager / GPUStackLogo 等
|
||||
# (FROM 紧邻字母时视为标识符的一部分,跳过)
|
||||
# - HTTP 头 X-GPUStack-* —— 后端契约 (如 X-GPUStack-Model),跳过
|
||||
#
|
||||
# 匹配规则:FROM 前后都不是字母(独立单词),且不是 `X-` 前缀的头名。
|
||||
#
|
||||
# 环境变量:
|
||||
# FROM 源品牌词(默认 GPUStack)
|
||||
# TO 目标品牌词(默认 MesaStack)
|
||||
# DRY_RUN 设为 1 时只预览将改动的行,不写文件
|
||||
#
|
||||
set -e
|
||||
|
||||
FROM="${FROM:-GPUStack}"
|
||||
TO="${TO:-MesaStack}"
|
||||
DRY_RUN="${DRY_RUN:-0}"
|
||||
|
||||
log() { echo -e "\033[1;34m[rebrand]\033[0m $*"; }
|
||||
|
||||
# 大小写敏感、单词边界、排除 X- 头前缀的 Perl 正则。
|
||||
# (?<![A-Za-z]) 前面不是字母 (?<!X-) 不是 X- 头 (?![A-Za-z]) 后面不是字母
|
||||
# FROM 为纯字母品牌词(无正则元字符),故直接拼接,不用 \Q\E
|
||||
# —— \Q\E 在「经变量插值进正则」时不会被求值,反而会破坏匹配。
|
||||
PATTERN="(?<![A-Za-z])(?<!X-)${FROM}(?![A-Za-z])"
|
||||
|
||||
# 行级排除:某些 FROM 出现在「与后端契约绑定的字符串」里,改了会破坏运行时逻辑,
|
||||
# 即使是独立单词也必须整行跳过。命中下列任一正则的行不替换。
|
||||
# 已知例外:
|
||||
# - llmodels/hooks/index.ts 用 startsWith() 比对后端返回的英文兼容性消息
|
||||
# ("... does not exist on the GPUStack server ..."),后端仍发 GPUStack,前端不能改。
|
||||
SKIP_LINE_PATTERNS=(
|
||||
'does not exist on the .*server. It'"'"'s recommended'
|
||||
)
|
||||
skip_line() {
|
||||
local line="$1" pat
|
||||
for pat in "${SKIP_LINE_PATTERNS[@]}"; do
|
||||
if echo "$line" | grep -qP "$pat"; then return 0; fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# 待处理的已跟踪文本文件(排除 lockfile 与本脚本自身)。
|
||||
mapfile -t files < <(
|
||||
git ls-files -- \
|
||||
'*.ts' '*.tsx' '*.js' '*.jsx' '*.json' '*.less' '*.html' '*.md' \
|
||||
| grep -v 'pnpm-lock.yaml'
|
||||
)
|
||||
|
||||
log "品牌替换: '${FROM}' -> '${TO}'"
|
||||
log "候选文件: ${#files[@]}"
|
||||
|
||||
# 把行级排除合并成一个 perl 正则,供预览与替换共用(经环境变量传入,免去转义)。
|
||||
SKIP_RE=""
|
||||
for pat in "${SKIP_LINE_PATTERNS[@]}"; do
|
||||
SKIP_RE="${SKIP_RE:+${SKIP_RE}|}(?:${pat})"
|
||||
done
|
||||
export REBRAND_SKIP_RE="${SKIP_RE}"
|
||||
export REBRAND_PATTERN="${PATTERN}"
|
||||
export REBRAND_TO="${TO}"
|
||||
|
||||
# 统计命中行(预览/确认用),已扣除被行级排除的行。
|
||||
log "命中行预览(最多 40 行):"
|
||||
grep -rnP "${PATTERN}" "${files[@]}" 2>/dev/null \
|
||||
| { [[ -n "${SKIP_RE}" ]] && grep -vP "${SKIP_RE}" || cat; } \
|
||||
| head -40 || true
|
||||
total=$(grep -rnP "${PATTERN}" "${files[@]}" 2>/dev/null \
|
||||
| { [[ -n "${SKIP_RE}" ]] && grep -vP "${SKIP_RE}" || cat; } | wc -l)
|
||||
log "命中总行数: ${total}"
|
||||
if [[ -n "${SKIP_RE}" ]]; then
|
||||
skipped=$(grep -rnP "${PATTERN}" "${files[@]}" 2>/dev/null | grep -cP "${SKIP_RE}" || true)
|
||||
log "行级排除(后端契约,保留 ${FROM}): ${skipped} 行"
|
||||
fi
|
||||
|
||||
if [[ "${DRY_RUN}" == "1" ]]; then
|
||||
log "DRY_RUN=1:未写入任何文件。"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 实际替换(in-place)。命中行级排除的行整行跳过。
|
||||
changed=0
|
||||
for f in "${files[@]}"; do
|
||||
if grep -qP "${PATTERN}" "$f" 2>/dev/null; then
|
||||
perl -i -pe '
|
||||
my $skip = $ENV{REBRAND_SKIP_RE};
|
||||
next if length($skip) && /$skip/;
|
||||
s/$ENV{REBRAND_PATTERN}/$ENV{REBRAND_TO}/g;
|
||||
' "$f"
|
||||
changed=$((changed + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
log "已修改文件数: ${changed}"
|
||||
log "完成。请用 'git diff' 复核改动。"
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# sync-github: 从上游 GitHub 拉取最新源码,并全量镜像同步到 192.168.0.23 私有源。
|
||||
#
|
||||
# 默认行为(全量镜像 / 强制):
|
||||
# 1. 确保存在 upstream remote 指向 GitHub(不存在则自动添加,URL 不一致则更新)。
|
||||
# 2. 从 upstream 抓取所有分支与 tag(--prune 清理已删除的远端引用)。
|
||||
# 3. 将上游每个分支强制推送到私有源同名分支(force push)。
|
||||
# 4. 将上游所有 tag 强制推送到私有源。
|
||||
#
|
||||
# 可用环境变量:
|
||||
# UPSTREAM_URL 上游 GitHub 仓库地址(默认 https://github.com/gpustack/gpustack-ui.git)
|
||||
# ORIGIN_REMOTE 私有源 remote 名称(默认 origin)
|
||||
# UPSTREAM_REMOTE 上游 remote 名称(默认 upstream)
|
||||
# PRUNE_BRANCHES 设为 1 时,删除私有源上「上游已不存在」的分支(真·镜像,破坏性,默认关闭)
|
||||
# DRY_RUN 设为 1 时,仅打印将要执行的推送动作,不实际推送
|
||||
#
|
||||
set -e
|
||||
|
||||
UPSTREAM_URL="${UPSTREAM_URL:-https://github.com/gpustack/gpustack-ui.git}"
|
||||
ORIGIN_REMOTE="${ORIGIN_REMOTE:-origin}"
|
||||
UPSTREAM_REMOTE="${UPSTREAM_REMOTE:-upstream}"
|
||||
PRUNE_BRANCHES="${PRUNE_BRANCHES:-0}"
|
||||
DRY_RUN="${DRY_RUN:-0}"
|
||||
|
||||
log() { echo -e "\033[1;34m[sync-github]\033[0m $*"; }
|
||||
warn() { echo -e "\033[1;33m[sync-github]\033[0m $*" >&2; }
|
||||
|
||||
run() {
|
||||
if [[ "${DRY_RUN}" == "1" ]]; then
|
||||
echo " (dry-run) git $*"
|
||||
else
|
||||
git "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# 1. 确保 upstream remote 指向 GitHub。
|
||||
if git remote get-url "${UPSTREAM_REMOTE}" >/dev/null 2>&1; then
|
||||
current_url=$(git remote get-url "${UPSTREAM_REMOTE}")
|
||||
if [[ "${current_url}" != "${UPSTREAM_URL}" ]]; then
|
||||
log "更新 ${UPSTREAM_REMOTE} 地址: ${current_url} -> ${UPSTREAM_URL}"
|
||||
git remote set-url "${UPSTREAM_REMOTE}" "${UPSTREAM_URL}"
|
||||
fi
|
||||
else
|
||||
log "添加 upstream remote: ${UPSTREAM_REMOTE} -> ${UPSTREAM_URL}"
|
||||
git remote add "${UPSTREAM_REMOTE}" "${UPSTREAM_URL}"
|
||||
fi
|
||||
|
||||
origin_url=$(git remote get-url "${ORIGIN_REMOTE}")
|
||||
log "上游 (拉取): ${UPSTREAM_URL}"
|
||||
log "私有源 (推送): ${origin_url}"
|
||||
|
||||
# 2. 抓取上游所有分支与 tag。
|
||||
log "抓取上游分支与 tag..."
|
||||
git fetch --prune --tags "${UPSTREAM_REMOTE}"
|
||||
|
||||
# 3. 逐个分支强制推送到私有源。
|
||||
log "强制同步分支到私有源..."
|
||||
upstream_branches=$(git for-each-ref --format='%(refname:strip=3)' "refs/remotes/${UPSTREAM_REMOTE}/" | grep -v '^HEAD$')
|
||||
|
||||
for branch in ${upstream_branches}; do
|
||||
log " -> ${branch}"
|
||||
run push --force "${ORIGIN_REMOTE}" \
|
||||
"refs/remotes/${UPSTREAM_REMOTE}/${branch}:refs/heads/${branch}"
|
||||
done
|
||||
|
||||
# 4. 强制同步所有 tag。
|
||||
log "强制同步 tag 到私有源..."
|
||||
run push --force --tags "${ORIGIN_REMOTE}"
|
||||
|
||||
# 5. 可选:删除私有源上、上游已不存在的分支(真·镜像)。
|
||||
if [[ "${PRUNE_BRANCHES}" == "1" ]]; then
|
||||
warn "PRUNE_BRANCHES=1:将删除私有源上上游已不存在的分支"
|
||||
origin_branches=$(git ls-remote --heads "${ORIGIN_REMOTE}" | sed 's@.*refs/heads/@@')
|
||||
for branch in ${origin_branches}; do
|
||||
if ! echo "${upstream_branches}" | grep -qx "${branch}"; then
|
||||
warn " 删除私有源分支: ${branch}"
|
||||
run push "${ORIGIN_REMOTE}" --delete "${branch}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
log "同步完成。"
|
||||
+7
-1
@@ -3,6 +3,7 @@ import { applyAccessExtensions } from './access.extensions';
|
||||
export default (initialState: {
|
||||
currentUser?: Global.UserInfo;
|
||||
hasKubernetesCluster?: boolean;
|
||||
hasResourceEvents?: boolean;
|
||||
}) => {
|
||||
const isPlatformAdmin = !!(
|
||||
initialState &&
|
||||
@@ -20,6 +21,10 @@ export default (initialState: {
|
||||
// role-based default so a transient network blip can't lock anyone
|
||||
// out of the menu.
|
||||
const hasKubernetesCluster = initialState?.hasKubernetesCluster;
|
||||
// Having run GPU/CPU instances or storage (any resource_events) also unlocks
|
||||
// GPU Service / the full Usage page — a user who used it keeps seeing it even
|
||||
// without a current cluster. MaaS-only users (no cluster, no events) don't.
|
||||
const hasResourceEvents = !!initialState?.hasResourceEvents;
|
||||
|
||||
// Predicate roles, top-down by strictness:
|
||||
// * `canSeeAdmin` — strictly platform admin (`users.is_admin`).
|
||||
@@ -41,7 +46,8 @@ export default (initialState: {
|
||||
return applyAccessExtensions({
|
||||
canSeeAdmin: isPlatformAdmin,
|
||||
canSeeOrgAdmin: isPlatformAdmin,
|
||||
canSeeGpuService: isPlatformAdmin || hasKubernetesCluster !== false,
|
||||
canSeeGpuService:
|
||||
isPlatformAdmin || hasKubernetesCluster !== false || hasResourceEvents,
|
||||
canManageCurrentOrg: false,
|
||||
canSeeUser,
|
||||
canDelete: true,
|
||||
|
||||
+87
-7
@@ -1,10 +1,12 @@
|
||||
import { userSettingsHelperAtom } from '@/atoms/settings';
|
||||
import { GPUStackVersionAtom, UpdateCheckAtom } from '@/atoms/user';
|
||||
import { GPUStackVersionAtom, UpdateCheckAtom, userAtom } from '@/atoms/user';
|
||||
import { setAtomStorage } from '@/atoms/utils';
|
||||
import { DEFAULT_ENTER_PAGE, GPUSTACK_API_BASE_URL } from '@/config/settings';
|
||||
import { COLOR_PRIMARY } from '@/config/theme/constants';
|
||||
import { queryClusterList } from '@/pages/cluster-management/apis';
|
||||
import { ProviderValueMap } from '@/pages/cluster-management/config';
|
||||
import { queryResourceEvents } from '@/pages/usage/apis/resource';
|
||||
import { getGPUStackPlugin } from '@/plugins';
|
||||
import { enterprisePluginReady } from '@/plugins/enterprise-ready';
|
||||
import { GPUStackPluginManager } from '@/plugins/manager';
|
||||
import { requestConfig } from '@/request-config';
|
||||
@@ -15,6 +17,7 @@ import {
|
||||
} from '@/services/profile/apis';
|
||||
import { fetchSystemConfig } from '@/services/system/query-system-config';
|
||||
import { isOnline } from '@/utils';
|
||||
import { installTenantFetch } from '@/utils/install-fetch';
|
||||
import {
|
||||
IS_FIRST_LOGIN,
|
||||
readState,
|
||||
@@ -24,6 +27,8 @@ import '@gpustack/core-ui/style.css';
|
||||
import { RequestConfig, history, request as umiRequest } from '@umijs/max';
|
||||
import { message } from 'antd';
|
||||
|
||||
installTenantFetch();
|
||||
|
||||
// only for the first login and access from http://localhost
|
||||
|
||||
const checkDefaultPage = async (userInfo: any) => {
|
||||
@@ -47,7 +52,12 @@ const checkDefaultPage = async (userInfo: any) => {
|
||||
const HAS_K8S_CLUSTER_KEY = 'hasKubernetesCluster';
|
||||
const probeHasKubernetesCluster = async (): Promise<boolean | undefined> => {
|
||||
try {
|
||||
const res = await queryClusterList({ page: -1 });
|
||||
const res = await queryClusterList(
|
||||
{ page: -1 },
|
||||
{
|
||||
skipErrorHandler: true
|
||||
}
|
||||
);
|
||||
const value = (res?.items ?? []).some(
|
||||
(c) => c?.provider === ProviderValueMap.Kubernetes
|
||||
);
|
||||
@@ -69,12 +79,49 @@ const probeHasKubernetesCluster = async (): Promise<boolean | undefined> => {
|
||||
}
|
||||
};
|
||||
|
||||
// Probes whether the caller has ANY resource-usage events (GPU/CPU instance or
|
||||
// storage lifecycle). Used alongside the cluster probe so a user who has run
|
||||
// GPU instances still sees GPU Service / the full Usage page even if they
|
||||
// currently have no Kubernetes cluster. Mirrored into sessionStorage for the
|
||||
// access extensions; any failure → undefined ("unknown — don't restrict").
|
||||
const HAS_RESOURCE_EVENTS_KEY = 'hasResourceEvents';
|
||||
const probeHasResourceEvents = async (): Promise<boolean | undefined> => {
|
||||
try {
|
||||
// No date range = "ever"; scope is clamped to the caller server-side.
|
||||
const res = await queryResourceEvents(
|
||||
{ perPage: 1 },
|
||||
{
|
||||
skipErrorHandler: true
|
||||
}
|
||||
);
|
||||
const value = (res?.pagination?.total ?? 0) > 0;
|
||||
try {
|
||||
window.sessionStorage.setItem(
|
||||
HAS_RESOURCE_EVENTS_KEY,
|
||||
JSON.stringify(value)
|
||||
);
|
||||
} catch {
|
||||
// sessionStorage may be unavailable; predicate treats missing as unknown.
|
||||
}
|
||||
return value;
|
||||
} catch (error) {
|
||||
console.error('probeHasResourceEvents error', error);
|
||||
try {
|
||||
window.sessionStorage.removeItem(HAS_RESOURCE_EVENTS_KEY);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
// runtime configuration
|
||||
export async function getInitialState(): Promise<{
|
||||
fetchUserInfo: () => Promise<Global.UserInfo>;
|
||||
currentUser?: Global.UserInfo;
|
||||
pluginData?: Record<string, any>;
|
||||
hasKubernetesCluster?: boolean;
|
||||
hasResourceEvents?: boolean;
|
||||
}> {
|
||||
const { location } = history;
|
||||
|
||||
@@ -119,6 +166,36 @@ export async function getInitialState(): Promise<{
|
||||
getUpdateCheck();
|
||||
fetchSystemConfig();
|
||||
}
|
||||
// Only commit a substantive user object. A truthy-but-empty
|
||||
// `data` (e.g. server responded 200 with an empty body) would
|
||||
// otherwise look like "logged in" to every `currentUser`
|
||||
// reader and the access seam — break out instead and let the
|
||||
// caller treat the request as failed.
|
||||
if (data && typeof data === 'object' && Object.keys(data).length > 0) {
|
||||
// Commit the identity to atom storage (and so to localStorage)
|
||||
// before returning. The access function — memoized on
|
||||
// `initialState` and run once per commit — reads identity from
|
||||
// localStorage; without this preemptive write the predicate
|
||||
// sees the prior session's identity on its first evaluation
|
||||
// after login, and stays stale until the next identity change
|
||||
// (which usually doesn't come without a manual refresh).
|
||||
try {
|
||||
setAtomStorage(userAtom, data);
|
||||
} catch (err) {
|
||||
console.error('userAtom commit error:', err);
|
||||
}
|
||||
// Fire `onUserFetched` so plugins maintaining identity-scoped
|
||||
// caches can seed them under the new identity before any
|
||||
// caller commits this user to `initialState`. Errors here are
|
||||
// swallowed and logged — fetchUserInfo must still return.
|
||||
try {
|
||||
await getGPUStackPlugin()?.login?.onUserFetched?.(data, {
|
||||
request: umiRequest
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('onUserFetched plugin hook error:', err);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
} catch (error: any) {
|
||||
const data = error?.response?.data;
|
||||
@@ -158,16 +235,19 @@ export async function getInitialState(): Promise<{
|
||||
getAppVersionInfo();
|
||||
|
||||
if (![DEFAULT_ENTER_PAGE.login].includes(location.pathname)) {
|
||||
const [userInfo, hasKubernetesCluster] = await Promise.all([
|
||||
fetchUserInfo(),
|
||||
probeHasKubernetesCluster()
|
||||
]);
|
||||
const [userInfo, hasKubernetesCluster, hasResourceEvents] =
|
||||
await Promise.all([
|
||||
fetchUserInfo(),
|
||||
probeHasKubernetesCluster(),
|
||||
probeHasResourceEvents()
|
||||
]);
|
||||
checkDefaultPage(userInfo);
|
||||
return {
|
||||
fetchUserInfo,
|
||||
currentUser: userInfo,
|
||||
pluginData,
|
||||
hasKubernetesCluster
|
||||
hasKubernetesCluster,
|
||||
hasResourceEvents
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>AlibabaCloud</title><path d="M14.752 4.64h5.274C22.242 4.64 24 6.475 24 8.691V15.8a3.947 3.947 0 01-3.974 3.975h-5.274l1.299-1.835 3.822-1.222c.688-.23 1.146-.918 1.146-1.605v-5.81c0-.687-.458-1.375-1.146-1.605L16.05 6.475l-1.3-1.835zM2.98 15.111c0 .688.46 1.376 1.147 1.606l3.822 1.146 1.3 1.835H3.974A3.947 3.947 0 010 15.723V8.69c0-2.216 1.758-4.05 3.975-4.05h5.273L7.95 6.474 4.127 7.697c-.688.23-1.146.918-1.146 1.606v5.808z" fill="#FF6A00"></path><path d="M16.051 11.213H8.025v1.835h8.026v-1.835z" fill="#FF6A00"></path></svg>
|
||||
|
After Width: | Height: | Size: 656 B |
@@ -0,0 +1 @@
|
||||
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>BaiLian</title><path d="M6.336 8.919v6.162l5.335-3.083L6.337 8.92z" fill="#1C54E3"></path><path d="M21.394 5.288s-.006-.006-.01-.006L17.01 2.754 6.336 8.92l5.335 3.082 9.701-5.6.016-.01a.635.635 0 00.006-1.1v-.003z" fill="#AA9AFF"></path><path d="M21.71 12.465a.62.62 0 00-.316.085s-.006 0-.009.003l-4.375 2.528 5.05 2.915h.006a2.06 2.06 0 00.28-1.04v-3.855a.637.637 0 00-.636-.636z" fill="#00EAD1"></path><path d="M22.06 17.996l-5.05-2.915L6.34 21.242l4.27 2.465s.016.006.022.012a2.102 2.102 0 002.093 0c.006-.003.016-.006.022-.012l8.538-4.93c.003 0 .006-.003.01-.006.321-.183.589-.45.775-.772h-.006l-.004-.003z" fill="#00CEC9"></path><path d="M11.672 11.998l-5.336 3.083-1.444.832-3.605 2.083H1.28c.173.303.416.555.709.738l.078.044.016.01.02.012 4.232 2.442 10.671-6.161-5.335-3.082z" fill="#00EAD1"></path><path d="M12.74.29c-.1-.06-.208-.107-.315-.148-.02-.006-.038-.016-.057-.022a2.121 2.121 0 00-.7-.12c-.233 0-.457.038-.668.11l-.031.01a2.196 2.196 0 00-.372.17L2.068 5.222s-.003 0-.006.003c-.324.183-.592.451-.781.773h.006l5.049 2.918L17.01 2.758 12.74.29z" fill="#7347FF"></path><path d="M1.287 6.001H1.28A2.06 2.06 0 001 7.041v9.915c0 .378.1.735.28 1.043h.007l5.049-2.918V8.919l-5.05-2.918z" fill="#0423DA"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -3,6 +3,7 @@
|
||||
.ant-layout-sider-children {
|
||||
border-inline: none;
|
||||
border-radius: 0;
|
||||
padding-inline-end: 0;
|
||||
padding-block-end: 8px;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ export const fromClusterCreationAtom = atom(false);
|
||||
export const clusterSessionAtom = atom<{
|
||||
firstAddWorker: boolean;
|
||||
firstAddCluster: boolean;
|
||||
presetClusterType?: 'model' | 'gpu';
|
||||
// Provider to preselect when the create flow opens — set by the
|
||||
// empty-state CTA on feature pages that need a specific provider
|
||||
// (e.g. GPU Service can only schedule on Kubernetes, so its
|
||||
|
||||
@@ -55,3 +55,10 @@ export const userSettingsHelperAtom = atom(
|
||||
}
|
||||
);
|
||||
export const hideModalTemporarilyAtom = atom<boolean>(false);
|
||||
|
||||
export const collapsedMenuGroupsAtom = atomWithStorage<string[]>(
|
||||
'collapsedMenuGroups',
|
||||
[],
|
||||
undefined,
|
||||
{ getOnInit: true }
|
||||
);
|
||||
|
||||
+39
-23
@@ -32,10 +32,7 @@ export const UpdateCheckAtom = atom<{
|
||||
latest_version: ''
|
||||
});
|
||||
|
||||
export const initialPasswordAtom = atomWithStorage<string>(
|
||||
'initialPassword',
|
||||
''
|
||||
);
|
||||
export const initialPasswordAtom = atom<string>('');
|
||||
|
||||
// Namespace the server creates for an Org's resources on each Kubernetes
|
||||
// cluster. The format must match the backend's ``get_namespace_name``
|
||||
@@ -90,20 +87,33 @@ const getStoredCurrentOrgId = (): number | null => {
|
||||
// cluster-owner fallback.
|
||||
const ORG_CACHE_KEYS = ['organizationList', 'allOrganizations'] as const;
|
||||
|
||||
const lookupOrgNamespace = (id: number | null): string | null => {
|
||||
export interface CachedOrg {
|
||||
id: number;
|
||||
name?: string;
|
||||
// The platform Org (single global tenant). Its models are NOT
|
||||
// namespaced in ``/v1/models`` — they appear under their bare name.
|
||||
is_platform?: boolean;
|
||||
}
|
||||
|
||||
// Resolve the cached Org record for an owner/principal id by scanning both
|
||||
// org caches. ``organizationList`` (the caller's member orgs) is checked
|
||||
// alongside the admin-only ``allOrganizations`` so member sessions resolve
|
||||
// too. Id types vary between localStorage payloads (some writers stringify,
|
||||
// others persist as a JSON number), so compare as strings — strict equality
|
||||
// would silently miss those cases.
|
||||
export const getOrgById = (
|
||||
id: number | string | null | undefined
|
||||
): CachedOrg | null => {
|
||||
if (id == null) return null;
|
||||
// Normalise both sides to strings — the stored id type varies between
|
||||
// localStorage payloads (some writers stringify, others persist as a
|
||||
// JSON number); strict equality would silently miss those cases.
|
||||
const target = String(id);
|
||||
for (const key of ORG_CACHE_KEYS) {
|
||||
try {
|
||||
const raw = localStorage.getItem(key);
|
||||
if (!raw) continue;
|
||||
const list = JSON.parse(raw) as Array<{ id: number; name?: string }>;
|
||||
const list = JSON.parse(raw) as CachedOrg[];
|
||||
if (!Array.isArray(list)) continue;
|
||||
const match = list.find((item) => String(item?.id) === target);
|
||||
if (match?.name) return `gpustack-${match.name}`;
|
||||
if (match) return match;
|
||||
} catch {
|
||||
// ignore malformed cache; continue checking other keys
|
||||
}
|
||||
@@ -111,17 +121,23 @@ const lookupOrgNamespace = (id: number | null): string | null => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export const getAllOrganizations = (): Array<{ id: number; name?: string }> => {
|
||||
const allOrganizations = localStorage.getItem('allOrganizations');
|
||||
if (!allOrganizations) return [];
|
||||
try {
|
||||
const list = JSON.parse(allOrganizations) as Array<{
|
||||
id: number;
|
||||
name?: string;
|
||||
}>;
|
||||
if (!Array.isArray(list)) return [];
|
||||
return list;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
// Bare Org *name* (e.g. ``org1``) for an owner/principal id, or null.
|
||||
export const getOrgNameById = (
|
||||
id: number | string | null | undefined
|
||||
): string | null => {
|
||||
return getOrgById(id)?.name ?? null;
|
||||
};
|
||||
|
||||
const lookupOrgNamespace = (id: number | null): string | null => {
|
||||
const name = getOrgNameById(id);
|
||||
return name ? `gpustack-${name}` : null;
|
||||
};
|
||||
|
||||
// The Org the caller is currently acting under, or null in the admin-"All"
|
||||
// context. The org-switcher reloads the page on switch, so the list pages
|
||||
// always show this org's resources — which is how ``/v1/models`` namespaces
|
||||
// their model ids (``{org}/{name}``). Callers reconstructing that id use this
|
||||
// as the fallback owner when a row carries no explicit ``owner_principal_id``.
|
||||
export const getCurrentOrg = (): CachedOrg | null => {
|
||||
return getOrgById(getStoredCurrentOrgId());
|
||||
};
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { GPUStackVersionAtom } from '@/atoms/user';
|
||||
import { getAtomStorage } from '@/atoms/utils';
|
||||
import VersionInfo, { modalConfig } from '@/components/version-info';
|
||||
import externalLinks from '@/constants/external-links';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Divider, Modal, Typography } from 'antd';
|
||||
import { Divider, Typography } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import styled from 'styled-components';
|
||||
|
||||
@@ -33,20 +31,10 @@ const useStyles = createStyles(({ token, css }) => ({
|
||||
|
||||
const Footer: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const { styles } = useStyles();
|
||||
|
||||
const showVersion = () => {
|
||||
modal.info({
|
||||
...modalConfig,
|
||||
width: 460,
|
||||
content: <VersionInfo intl={intl} />
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextHolder}
|
||||
<div className={styles.footer}>
|
||||
<div className="footer-content">
|
||||
<div className="footer-content-left">
|
||||
@@ -63,18 +51,7 @@ const Footer: React.FC = () => {
|
||||
</Typography.Link>
|
||||
</CompanyWrapper>
|
||||
<Divider orientation="vertical" />
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
href={externalLinks.documentation}
|
||||
target="_blank"
|
||||
>
|
||||
{intl.formatMessage({ id: 'common.button.help' })}
|
||||
</Button>
|
||||
<Divider orientation="vertical" />
|
||||
<Button type="link" size="small" onClick={showVersion}>
|
||||
{getAtomStorage(GPUStackVersionAtom)?.version}
|
||||
</Button>
|
||||
<span>{getAtomStorage(GPUStackVersionAtom)?.version}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4613488 */
|
||||
src: url('iconfont.woff2?t=1780035199453') format('woff2'),
|
||||
url('iconfont.woff?t=1780035199453') format('woff'),
|
||||
url('iconfont.ttf?t=1780035199453') format('truetype');
|
||||
src: url('iconfont.woff2?t=1780907696624') format('woff2'),
|
||||
url('iconfont.woff?t=1780907696624') format('woff'),
|
||||
url('iconfont.ttf?t=1780907696624') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,18 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-billing-outlined:before {
|
||||
content: "\e70d";
|
||||
}
|
||||
|
||||
.icon-billing-filled:before {
|
||||
content: "\e70e";
|
||||
}
|
||||
|
||||
.icon-fingerprint:before {
|
||||
content: "\e70c";
|
||||
}
|
||||
|
||||
.icon-database-outlined:before {
|
||||
content: "\e70a";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,27 @@
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "47798379",
|
||||
"name": "request_quote_24dp_1F1F1F_FILL0_wght300_GRAD0_opsz",
|
||||
"font_class": "billing-outlined",
|
||||
"unicode": "e70d",
|
||||
"unicode_decimal": 59149
|
||||
},
|
||||
{
|
||||
"icon_id": "47798378",
|
||||
"name": "request_quote_24dp_1F1F1F_FILL1_wght300_GRAD0_opsz",
|
||||
"font_class": "billing-filled",
|
||||
"unicode": "e70e",
|
||||
"unicode_decimal": 59150
|
||||
},
|
||||
{
|
||||
"icon_id": "47761129",
|
||||
"name": "fingerprint",
|
||||
"font_class": "fingerprint",
|
||||
"unicode": "e70c",
|
||||
"unicode_decimal": 59148
|
||||
},
|
||||
{
|
||||
"icon_id": "47654402",
|
||||
"name": "database_24dp_1F1F1F_FILL0_wght300_GRAD0_opsz24 (1",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+16
-37
@@ -171,8 +171,6 @@ body {
|
||||
}
|
||||
|
||||
.ant-table .ant-table-container table {
|
||||
// border-spacing: 0 20px;
|
||||
|
||||
.ant-table-thead th.ant-table-column-sort {
|
||||
background-color: transparent;
|
||||
|
||||
@@ -241,8 +239,8 @@ body {
|
||||
height: 100%;
|
||||
|
||||
.ant-pro-sider-footer {
|
||||
padding-block: 2px 0;
|
||||
padding-left: 6px;
|
||||
padding-block: 4px 0;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.ant-pro-sider-actions-list-item {
|
||||
@@ -259,46 +257,26 @@ body {
|
||||
border-block-end: none;
|
||||
}
|
||||
|
||||
.ant-pro-sider-logo-collapsed {
|
||||
padding-left: 12px;
|
||||
cursor: e-resize;
|
||||
|
||||
.collapse-wrap {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 48px;
|
||||
width: 64px;
|
||||
top: -16px;
|
||||
left: -16px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.collapse-wrap {
|
||||
display: block;
|
||||
.ant-pro-sider-logo {
|
||||
.collapse-btn {
|
||||
color: var(--ant-color-text-tertiary);
|
||||
:hover {
|
||||
color: var(--ant-color-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-pro-sider .ant-layout-sider-children {
|
||||
// border-right: 1px solid var(--ant-color-split);
|
||||
.ant-pro-sider-logo-collapsed {
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-table-content table {
|
||||
.ant-table-tbody {
|
||||
.ant-table-row {
|
||||
border-radius: var(--table-td-radius);
|
||||
.ant-table .ant-table-tbody {
|
||||
.ant-table-row {
|
||||
border-radius: var(--table-td-radius);
|
||||
|
||||
> td {
|
||||
background-color: unset;
|
||||
border-bottom: 1px solid var(--ant-color-split);
|
||||
}
|
||||
> td {
|
||||
border-bottom: 1px solid var(--ant-color-split);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,7 +325,6 @@ body {
|
||||
.ant-pro-layout-container {
|
||||
overflow-x: auto;
|
||||
min-height: 100vh;
|
||||
// background-color: var(--ant-color-bg-container);
|
||||
}
|
||||
|
||||
.ant-pro-sider {
|
||||
@@ -812,6 +789,8 @@ body {
|
||||
}
|
||||
|
||||
.ant-pro-sider-logo + div {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ export function useQueryDataList<
|
||||
Response = Array<ListItem>
|
||||
>(option: {
|
||||
key: string;
|
||||
manual?: boolean;
|
||||
responseType?: 'array' | 'object';
|
||||
fetchList: (
|
||||
params: Params,
|
||||
@@ -38,6 +39,7 @@ export function useQueryDataList<
|
||||
fetchList,
|
||||
getLabel,
|
||||
getValue,
|
||||
manual = true,
|
||||
responseType = 'array',
|
||||
errorMsg
|
||||
} = option;
|
||||
@@ -70,7 +72,7 @@ export function useQueryDataList<
|
||||
return responseType === 'array' ? res.items || [] : res;
|
||||
},
|
||||
{
|
||||
manual: true,
|
||||
manual: manual,
|
||||
debounceWait: option.debounceWait || 300,
|
||||
onSuccess: () => {},
|
||||
onError: (error) => {
|
||||
@@ -105,16 +107,18 @@ export function useQueryDataList<
|
||||
export function useQueryData<Detail, Params = any>(option: {
|
||||
key: string;
|
||||
delay?: number;
|
||||
manual?: boolean;
|
||||
fetchDetail: (params: Params, options?: any) => Promise<Detail>;
|
||||
getData?: (response: Detail, params?: any) => any;
|
||||
errorMsg?: string;
|
||||
}): {
|
||||
loading: boolean;
|
||||
detailData: Detail;
|
||||
manual?: boolean;
|
||||
cancelRequest: () => void;
|
||||
fetchData: (params: Params, extra?: any) => Promise<Detail>;
|
||||
} {
|
||||
const { key, fetchDetail, getData, errorMsg, delay } = option;
|
||||
const { key, fetchDetail, getData, errorMsg, delay, manual = true } = option;
|
||||
const axiosTokenRef = useRef<CancelTokenSource | null>(null);
|
||||
const [detailData, setDetailData] = useState<Detail>({} as Detail);
|
||||
|
||||
@@ -142,7 +146,7 @@ export function useQueryData<Detail, Params = any>(option: {
|
||||
return res;
|
||||
},
|
||||
{
|
||||
manual: true,
|
||||
manual: manual,
|
||||
onSuccess: () => {},
|
||||
onError: (error) => {
|
||||
message.error(
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { useRef, useState } from 'react';
|
||||
|
||||
/**
|
||||
* Centralizes the anti-double-submit + anti-deadlock logic shared by the
|
||||
* "add/edit" modals.
|
||||
*
|
||||
* The lock has to straddle antd's async field validation, whose result comes
|
||||
* back through two separate callbacks (`onFinish` / `onFinishFailed`). So the
|
||||
* three wiring points below are intentional and map 1:1 to those anchors:
|
||||
*
|
||||
* - `guard` wrap the submit trigger (button / ModalFooter onOk). Blocks
|
||||
* re-entry while a submit is already in flight, then fires submit.
|
||||
* - `run` wrap the `onFinish` handler. Holds the lock + loading until the
|
||||
* `onOk` request settles (success or error).
|
||||
* - `release` pass as the form's `onFinishFailed`. Releases the lock when
|
||||
* validation fails, otherwise the button would dead-lock.
|
||||
*/
|
||||
export default function useSubmitLock() {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const lockRef = useRef<boolean>(false);
|
||||
|
||||
const release = useMemoizedFn(() => {
|
||||
setLoading(false);
|
||||
lockRef.current = false;
|
||||
});
|
||||
|
||||
const guard = useMemoizedFn((submit: () => void) => {
|
||||
if (lockRef.current) {
|
||||
return;
|
||||
}
|
||||
lockRef.current = true;
|
||||
submit();
|
||||
});
|
||||
|
||||
const run = useMemoizedFn(async (task: () => void | Promise<void>) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await task();
|
||||
} finally {
|
||||
release();
|
||||
}
|
||||
});
|
||||
|
||||
return { loading, guard, run, release };
|
||||
}
|
||||
@@ -37,6 +37,7 @@ export default function useTableFetch<T>(
|
||||
key?: (typeof PaginationKey)[keyof typeof PaginationKey];
|
||||
fetchAPI: (params: any, options?: any) => Promise<Global.PageResponse<T>>;
|
||||
deleteAPI?: (id: number, params?: any) => Promise<any>;
|
||||
afterDelete?: (id?: number | number[]) => void;
|
||||
contentForDelete?: string;
|
||||
defaultData?: any[];
|
||||
events?: EventsType[];
|
||||
@@ -48,6 +49,7 @@ export default function useTableFetch<T>(
|
||||
const {
|
||||
fetchAPI,
|
||||
deleteAPI,
|
||||
afterDelete,
|
||||
contentForDelete,
|
||||
API,
|
||||
polling = false,
|
||||
@@ -363,7 +365,7 @@ export default function useTableFetch<T>(
|
||||
|
||||
// remove the deleted id from selected ids in row selection
|
||||
rowSelection.removeSelectedKeys([row.id]);
|
||||
|
||||
afterDelete?.(row.id);
|
||||
// ======== to avoid fetch data twice, because of debounceFetchData has been run =======
|
||||
if (!updateManually) {
|
||||
fetchData();
|
||||
@@ -390,6 +392,7 @@ export default function useTableFetch<T>(
|
||||
successIds.push(id);
|
||||
}
|
||||
);
|
||||
afterDelete?.(successIds);
|
||||
rowSelection.removeSelectedKeys(successIds);
|
||||
fetchData();
|
||||
return res;
|
||||
|
||||
@@ -14,7 +14,6 @@ type EventsType = 'CREATE' | 'UPDATE' | 'DELETE' | 'INSERT';
|
||||
export function useUpdateChunkedList(options: {
|
||||
events?: EventsType[];
|
||||
dataList?: any[];
|
||||
triggerAt?: React.MutableRefObject<number>;
|
||||
limit?: number;
|
||||
onCreate?: (args: any) => void;
|
||||
onUpdate?: (args: any) => void;
|
||||
@@ -24,9 +23,9 @@ export function useUpdateChunkedList(options: {
|
||||
filterFun?: (args: any) => boolean;
|
||||
mapFun?: (args: any) => any;
|
||||
computedID?: (d: object) => string;
|
||||
isNewItem?: (item: any) => boolean;
|
||||
}) {
|
||||
const { events = ['CREATE', 'DELETE', 'UPDATE', 'INSERT'], triggerAt } =
|
||||
options;
|
||||
const { events = ['CREATE', 'DELETE', 'UPDATE', 'INSERT'] } = options;
|
||||
const deletedIdsRef = useRef<Set<number | string>>(new Set());
|
||||
const cacheDataListRef = useRef<any[]>(options.dataList || []);
|
||||
const timerRef = useRef<any>(null);
|
||||
@@ -71,17 +70,14 @@ export function useUpdateChunkedList(options: {
|
||||
(sItem: any) => sItem.id === item.id
|
||||
);
|
||||
const updateItem = { ...item };
|
||||
if (updateIndex === -1 && !triggerAt?.current) {
|
||||
if (updateIndex === -1) {
|
||||
acc.push(updateItem);
|
||||
} else if (!triggerAt?.current) {
|
||||
} else {
|
||||
cacheDataListRef.current[updateIndex] = updateItem;
|
||||
}
|
||||
// only push items created after the watch started
|
||||
|
||||
// TODO: only push items created after triggerAt
|
||||
if (
|
||||
triggerAt?.current &&
|
||||
Date.parse(item.created_at) >= triggerAt.current
|
||||
) {
|
||||
if (options.isNewItem?.(item)) {
|
||||
latestCreateList.push(updateItem);
|
||||
}
|
||||
|
||||
@@ -93,6 +89,10 @@ export function useUpdateChunkedList(options: {
|
||||
...cacheDataListRef.current
|
||||
].slice(0, limit);
|
||||
|
||||
options.setDataList?.([...cacheDataListRef.current], {
|
||||
createdIds: newDataList.map((item) => item.id)
|
||||
});
|
||||
|
||||
options.onCreate?.(latestCreateList);
|
||||
}
|
||||
|
||||
@@ -103,10 +103,8 @@ export function useUpdateChunkedList(options: {
|
||||
cacheDataListRef.current = cacheDataListRef.current?.filter(
|
||||
(item: any) => {
|
||||
// collect deleted items
|
||||
if (triggerAt?.current) {
|
||||
if (ids?.includes(item.id)) {
|
||||
deletedList.push(item);
|
||||
}
|
||||
if (ids?.includes(item.id) && !options.isNewItem?.(item)) {
|
||||
deletedList.push(item);
|
||||
}
|
||||
return !ids?.includes(item.id);
|
||||
}
|
||||
@@ -133,10 +131,8 @@ export function useUpdateChunkedList(options: {
|
||||
updateItem,
|
||||
...cacheDataListRef.current.slice(0, limit - 1)
|
||||
];
|
||||
if (options.onUpdate && triggerAt?.current) {
|
||||
if (Date.parse(item.created_at) >= triggerAt.current) {
|
||||
options.onUpdate?.([updateItem]);
|
||||
}
|
||||
if (options.onUpdate && options.isNewItem?.(item)) {
|
||||
options.onUpdate?.([updateItem]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -63,8 +63,7 @@ export default function useWatchList<T = Record<string, any>>(API: string) {
|
||||
listRequestTokenRef.current?.cancel?.();
|
||||
listRequestTokenRef.current = createAxiosToken();
|
||||
const params = {
|
||||
page: 1,
|
||||
perPage: 100
|
||||
page: -1
|
||||
};
|
||||
const res: any = await queryAllDataList(params, {
|
||||
token: listRequestTokenRef.current.token
|
||||
@@ -86,6 +85,7 @@ export default function useWatchList<T = Record<string, any>>(API: string) {
|
||||
|
||||
return {
|
||||
watchDataList,
|
||||
setWatchDataList,
|
||||
deleteItemFromCache: handleDeleteItemFromCache
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { GPUStackVersionAtom, UpdateCheckAtom } from '@/atoms/user';
|
||||
import PluginExtraField from '@/components/plugin-extra-fields';
|
||||
import VersionInfo, { modalConfig } from '@/components/version-info';
|
||||
import externalLinks from '@/constants/external-links';
|
||||
import useBodyScroll from '@/hooks/use-body-scroll';
|
||||
import { logout } from '@/pages/login/apis';
|
||||
import { useModel } from '@@/plugin-model';
|
||||
import {
|
||||
@@ -13,7 +11,7 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
import { DropdownActions, IconFont } from '@gpustack/core-ui';
|
||||
import { history, useIntl, useNavigate } from '@umijs/max';
|
||||
import { Avatar, Button, Divider, Modal } from 'antd';
|
||||
import { Avatar, Divider } from 'antd';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
@@ -97,11 +95,9 @@ const CustomItem = styled.div`
|
||||
|
||||
export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
||||
const { isDarkTheme } = props;
|
||||
const { saveScrollHeight, restoreScrollHeight } = useBodyScroll();
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
const intl = useIntl();
|
||||
const [version] = useAtom(GPUStackVersionAtom);
|
||||
const [updateCheck] = useAtom(UpdateCheckAtom);
|
||||
const intl = useIntl();
|
||||
const initialInfo = useModel('@@initialState') || {
|
||||
initialState: undefined,
|
||||
loading: false,
|
||||
@@ -139,16 +135,6 @@ export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
||||
return {};
|
||||
}, [isDarkTheme]);
|
||||
|
||||
const showVersion = () => {
|
||||
saveScrollHeight();
|
||||
modal.info({
|
||||
...modalConfig,
|
||||
width: 460,
|
||||
content: <VersionInfo intl={intl} />,
|
||||
onCancel: restoreScrollHeight
|
||||
});
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logout();
|
||||
navigate(loginPath);
|
||||
@@ -158,7 +144,7 @@ export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
||||
{
|
||||
key: 'site',
|
||||
icon: <HomeOutlined />,
|
||||
label: 'GPUStack',
|
||||
label: 'MesaStack',
|
||||
url: externalLinks.site
|
||||
},
|
||||
{
|
||||
@@ -210,14 +196,14 @@ export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
||||
key: 'settings',
|
||||
label: (
|
||||
<span className="flex flex-center">
|
||||
<IconFont type="icon-settings-02" />
|
||||
<IconFont type="icon-preferences" />
|
||||
<span className="m-l-8" style={{ marginLeft: 8 }}>
|
||||
{intl?.formatMessage?.({ id: 'common.button.settings' })}
|
||||
{intl?.formatMessage?.({ id: 'common.preferences' })}
|
||||
</span>
|
||||
</span>
|
||||
),
|
||||
onClick: () => {
|
||||
history.push('/profile');
|
||||
history.push('/preferences');
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -258,7 +244,6 @@ export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
{contextHolder}
|
||||
<PluginExtraField name="OrgSwitcher" isDarkTheme={isDarkTheme} />
|
||||
<div
|
||||
style={{
|
||||
@@ -266,16 +251,13 @@ export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
onClick={showVersion}
|
||||
<span
|
||||
style={{
|
||||
color: 'var(--ant-color-text-tertiary)'
|
||||
}}
|
||||
>
|
||||
{version.version}
|
||||
</Button>
|
||||
</span>
|
||||
{showUpgrade && (
|
||||
<NewLabel>
|
||||
<span className="text">
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import { IconFont } from '@gpustack/core-ui';
|
||||
import { Link, useLocation, useNavigate } from '@umijs/max';
|
||||
import { Menu } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
interface MenuItem {
|
||||
icon?: string;
|
||||
selectedIcon?: string;
|
||||
defaultIcon?: string;
|
||||
children?: MenuItem[];
|
||||
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface HeaderMenuProps {
|
||||
menuData: MenuItem[];
|
||||
initialState?: Global.InitialStateType;
|
||||
}
|
||||
|
||||
const useStyles = createStyles(({ css }) => {
|
||||
return {
|
||||
headerMenu: css`
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
background: transparent;
|
||||
border-bottom: none;
|
||||
line-height: inherit;
|
||||
|
||||
&.ant-menu-horizontal {
|
||||
border-bottom: none;
|
||||
}
|
||||
&.ant-menu-horizontal > .ant-menu-item::after,
|
||||
&.ant-menu-horizontal > .ant-menu-submenu::after {
|
||||
display: none;
|
||||
}
|
||||
.ant-menu-title-content {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.anticon {
|
||||
font-size: 16px;
|
||||
}
|
||||
`
|
||||
};
|
||||
});
|
||||
|
||||
const isItemSelected = (item: MenuItem, pathname: string) => {
|
||||
return (
|
||||
pathname === item.path ||
|
||||
(Array.isArray(item.subMenu) && item.subMenu.includes(pathname))
|
||||
);
|
||||
};
|
||||
|
||||
const HeaderMenu: React.FC<HeaderMenuProps> = (props) => {
|
||||
const { menuData } = props;
|
||||
const { styles } = useStyles();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const buildLeaf = (item: MenuItem) => {
|
||||
const selected = isItemSelected(item, location.pathname);
|
||||
return {
|
||||
key: item.path as string,
|
||||
label: (
|
||||
<Link
|
||||
prefetch="intent"
|
||||
to={(item.path as string).replace('/*', '')}
|
||||
target={item.target}
|
||||
>
|
||||
<span className="flex-center gap-8">
|
||||
<IconFont
|
||||
type={selected ? item.selectedIcon || '' : item.defaultIcon || ''}
|
||||
/>
|
||||
<span>{item.name}</span>
|
||||
</span>
|
||||
</Link>
|
||||
)
|
||||
};
|
||||
};
|
||||
|
||||
const items = useMemo(() => {
|
||||
return menuData.map((item) => {
|
||||
if (item.children && item.children.length > 0) {
|
||||
return {
|
||||
key: item.key,
|
||||
label: item.name,
|
||||
children: item.children.map((child) => buildLeaf(child))
|
||||
};
|
||||
}
|
||||
return buildLeaf(item);
|
||||
});
|
||||
}, [menuData, location.pathname]);
|
||||
|
||||
const selectedKeys = useMemo(() => {
|
||||
const keys: string[] = [];
|
||||
for (const item of menuData) {
|
||||
const leaves =
|
||||
item.children && item.children.length > 0 ? item.children : [item];
|
||||
for (const leaf of leaves) {
|
||||
if (isItemSelected(leaf, location.pathname)) {
|
||||
keys.push(leaf.path as string);
|
||||
}
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}, [menuData, location.pathname]);
|
||||
|
||||
const handleClick = ({ key }: { key: string }) => {
|
||||
if (key.startsWith('/')) {
|
||||
navigate(key.replace('/*', ''));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Menu
|
||||
className={styles.headerMenu}
|
||||
mode="horizontal"
|
||||
selectedKeys={selectedKeys}
|
||||
items={items}
|
||||
onClick={handleClick}
|
||||
triggerSubMenuAction="hover"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderMenu;
|
||||
+25
-77
@@ -21,11 +21,7 @@ import {
|
||||
import { useAccessMarkedRoutes } from '@@/plugin-access';
|
||||
import { useModel } from '@@/plugin-model';
|
||||
import { ProLayout } from '@ant-design/pro-components';
|
||||
import {
|
||||
CoreUIProvider,
|
||||
IconFont,
|
||||
useOverlayScroller
|
||||
} from '@gpustack/core-ui';
|
||||
import { CoreUIProvider } from '@gpustack/core-ui';
|
||||
import {
|
||||
Access,
|
||||
Outlet,
|
||||
@@ -42,18 +38,18 @@ import {
|
||||
useNavigate,
|
||||
type IRoute
|
||||
} from '@umijs/max';
|
||||
import { Button, ConfigProvider, Modal, theme } from 'antd';
|
||||
import { ConfigProvider, Modal, theme } from 'antd';
|
||||
import { useAtom } from 'jotai';
|
||||
import 'overlayscrollbars/overlayscrollbars.css';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { PageContainerInner } from '../pages/_components/page-box';
|
||||
import Exception from './Exception';
|
||||
import './Layout.css';
|
||||
import { LogoIcon, SLogoIcon } from './Logo';
|
||||
import { LogoIcon } from './Logo';
|
||||
import ErrorBoundary from './error-boundary';
|
||||
import { ExtraContent } from './extraRender';
|
||||
import HeaderMenu from './header-menu';
|
||||
import { patchRoutes } from './runtime';
|
||||
import SiderMenu from './sider-menu';
|
||||
|
||||
// Pages that use the page container in the page
|
||||
const NO_CONTAINER_PAGES = [
|
||||
@@ -72,9 +68,9 @@ const NO_CONTAINER_PAGES = [
|
||||
|
||||
const CHECK_RESOURCE_PATH = [
|
||||
'/resources/workers',
|
||||
'/cluster-management/clusters/list',
|
||||
'/cluster-management/credentials',
|
||||
'/cluster-management/clusters/create'
|
||||
'/resources/clusters/list',
|
||||
'/resources/credentials',
|
||||
'/resources/clusters/create'
|
||||
];
|
||||
|
||||
type NewRoute = IRoute & {
|
||||
@@ -135,11 +131,8 @@ const mapRoutes = (routes: IRoute[], role: string) => {
|
||||
};
|
||||
|
||||
export default (props: any) => {
|
||||
const { initialize: initialize } = useOverlayScroller({
|
||||
defer: false
|
||||
});
|
||||
const [, contextHolder] = Modal.useModal();
|
||||
const { themeData, setUserSettings, userSettings } = useUserSettings();
|
||||
const { themeData, userSettings } = useUserSettings();
|
||||
const [userInfo] = useAtom(userAtom);
|
||||
const [routeCache] = useAtom(routeCacheAtom);
|
||||
const location = useLocation();
|
||||
@@ -200,13 +193,6 @@ export default (props: any) => {
|
||||
|
||||
const coreUISlots = useMemo(() => ({ ExtraContent }), []);
|
||||
|
||||
const handleToggleCollapse = (e: any) => {
|
||||
e.stopPropagation();
|
||||
setUserSettings({
|
||||
...userSettings,
|
||||
collapsed: !userSettings.collapsed
|
||||
});
|
||||
};
|
||||
const newRoutes = filterRoutes(
|
||||
// @ts-ignore
|
||||
clientRoutes.filter((route) => route.id === 'max-tabs'),
|
||||
@@ -236,39 +222,19 @@ export default (props: any) => {
|
||||
return NO_CONTAINER_PAGES.includes(matchedRoute?.name as string);
|
||||
}, [matchedRoute]);
|
||||
|
||||
const collapsed = useMemo(() => {
|
||||
return userSettings.collapsed || false;
|
||||
}, [userSettings.collapsed]);
|
||||
|
||||
const renderMenuHeader = (logo: React.ReactNode, title: React.ReactNode) => {
|
||||
return (
|
||||
<>
|
||||
{logo}
|
||||
<div className="collapse-wrap" onClick={handleToggleCollapse}>
|
||||
<Button
|
||||
style={{
|
||||
marginRight: collapsed ? 0 : -14,
|
||||
border: 'none',
|
||||
cursor: 'w-resize'
|
||||
}}
|
||||
size="small"
|
||||
type={collapsed ? 'default' : 'text'}
|
||||
>
|
||||
<IconFont
|
||||
type={collapsed ? 'icon-expand-left' : 'icon-expand-right'}
|
||||
className="font-size-18 text-secondary"
|
||||
style={{
|
||||
display: 'block'
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
return <>{logo}</>;
|
||||
};
|
||||
|
||||
const menuContentRender = (menuProps: any, defaultDom: React.ReactNode) => {
|
||||
return <SiderMenu {...menuProps}></SiderMenu>;
|
||||
const headerContentRender = (
|
||||
headerProps: any,
|
||||
defaultDom: React.ReactNode
|
||||
) => {
|
||||
return <HeaderMenu {...headerProps}></HeaderMenu>;
|
||||
};
|
||||
|
||||
const actionsRender = () => {
|
||||
return <ExtraContent isDarkTheme={userSettings.isDarkTheme} />;
|
||||
};
|
||||
|
||||
const onPageChange = async (route: any) => {
|
||||
@@ -322,17 +288,6 @@ export default (props: any) => {
|
||||
navigate(pagepath);
|
||||
};
|
||||
|
||||
const onCollapse = (value: boolean) => {
|
||||
// only trigger by window resize
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
setUserSettings({
|
||||
...userSettings,
|
||||
collapsed: value
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfigProvider
|
||||
componentSize="large"
|
||||
@@ -393,34 +348,27 @@ export default (props: any) => {
|
||||
<DarkMask></DarkMask>
|
||||
<ProLayout
|
||||
fixSiderbar
|
||||
fixedHeader={false}
|
||||
headerRender={false}
|
||||
fixedHeader
|
||||
breadcrumbRender={false}
|
||||
route={route}
|
||||
location={location}
|
||||
title={userConfig.title}
|
||||
navTheme={userSettings.theme}
|
||||
layout="side"
|
||||
layout="top"
|
||||
contentStyle={{
|
||||
paddingBlock: 0,
|
||||
paddingInline: 0
|
||||
}}
|
||||
openKeys={false}
|
||||
disableMobile={true}
|
||||
siderWidth={220}
|
||||
onCollapse={onCollapse}
|
||||
onMenuHeaderClick={onMenuHeaderClick}
|
||||
menuHeaderRender={renderMenuHeader}
|
||||
collapsed={userSettings.collapsed}
|
||||
onPageChange={onPageChange}
|
||||
formatMessage={formatMessage}
|
||||
menu={{
|
||||
locale: true,
|
||||
type: 'group'
|
||||
locale: true
|
||||
}}
|
||||
splitMenus={true}
|
||||
logo={userSettings.collapsed ? <SLogoIcon /> : <LogoIcon />}
|
||||
menuContentRender={menuContentRender}
|
||||
logo={<LogoIcon />}
|
||||
headerContentRender={headerContentRender}
|
||||
actionsRender={actionsRender}
|
||||
{...runtimeConfig}
|
||||
ErrorBoundary={ErrorBoundary}
|
||||
>
|
||||
@@ -428,7 +376,7 @@ export default (props: any) => {
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
height: '100%',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -82,7 +82,7 @@ export const getRightRenderContent = (opts: {
|
||||
{
|
||||
key: 'site',
|
||||
icon: <HomeOutlined />,
|
||||
label: 'GPUStack',
|
||||
label: 'MesaStack',
|
||||
url: externalLinks.site
|
||||
},
|
||||
{
|
||||
@@ -256,7 +256,7 @@ export const getRightRenderContent = (opts: {
|
||||
</span>
|
||||
),
|
||||
onClick: () => {
|
||||
history.push('/profile');
|
||||
history.push('/preferences');
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+75
-49
@@ -1,9 +1,11 @@
|
||||
import { collapsedMenuGroupsAtom } from '@/atoms/settings';
|
||||
import { CaretDownOutlined } from '@ant-design/icons';
|
||||
import { IconFont } from '@gpustack/core-ui';
|
||||
import { IconFont, OverlayScroller } from '@gpustack/core-ui';
|
||||
import { Link, useLocation } from '@umijs/max';
|
||||
import { Tooltip } from 'antd';
|
||||
import { createStyles, type FullToken } from 'antd-style';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useAtom } from 'jotai';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
interface MenuItem {
|
||||
icon?: string;
|
||||
@@ -29,12 +31,17 @@ const useStyles = createStyles(
|
||||
|
||||
return {
|
||||
siderMenu: css`
|
||||
width: 100%;
|
||||
&.sider-menu-collapsed {
|
||||
.menu-item {
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.os-scrollbar-vertical .os-scrollbar-handle {
|
||||
min-width: 4px;
|
||||
max-width: 4px;
|
||||
}
|
||||
`,
|
||||
groupTitle: css`
|
||||
display: flex;
|
||||
@@ -139,8 +146,19 @@ const SiderMenu: React.FC<SiderMenuProps> = (props) => {
|
||||
const { menuData, collapsed } = props;
|
||||
const { styles, cx } = useStyles();
|
||||
const location = useLocation();
|
||||
const [collapseKeys, setCollapseKeys] = useState<Set<string>>(new Set());
|
||||
console.log('SiderMenu', location.pathname);
|
||||
const [storedCollapsedGroups, setCollapsedGroups] = useAtom(
|
||||
collapsedMenuGroupsAtom
|
||||
);
|
||||
// atomWithStorage falls back to the initial value on JSON parse
|
||||
// errors, but not when the stored value is valid JSON of another
|
||||
// shape — normalize so array methods below can't throw.
|
||||
const collapsedGroups = Array.isArray(storedCollapsedGroups)
|
||||
? storedCollapsedGroups
|
||||
: [];
|
||||
const collapseKeys = useMemo(
|
||||
() => new Set(collapsedGroups),
|
||||
[collapsedGroups]
|
||||
);
|
||||
|
||||
const dividerStyles = useMemo(() => {
|
||||
if (collapsed) {
|
||||
@@ -159,14 +177,11 @@ const SiderMenu: React.FC<SiderMenuProps> = (props) => {
|
||||
const handleToggleGroup = (e: any, menuGroup: any) => {
|
||||
e.stopPropagation();
|
||||
|
||||
console.log('handleToggleGroup', menuGroup.key);
|
||||
|
||||
if (collapseKeys.has(menuGroup.key)) {
|
||||
collapseKeys.delete(menuGroup.key);
|
||||
} else {
|
||||
collapseKeys.add(menuGroup.key);
|
||||
}
|
||||
setCollapseKeys(new Set(collapseKeys));
|
||||
setCollapsedGroups(
|
||||
collapsedGroups.includes(menuGroup.key)
|
||||
? collapsedGroups.filter((key) => key !== menuGroup.key)
|
||||
: [...collapsedGroups, menuGroup.key]
|
||||
);
|
||||
};
|
||||
|
||||
const menuItemRender = (menuItem: MenuItem, key: string) => {
|
||||
@@ -222,44 +237,55 @@ const SiderMenu: React.FC<SiderMenuProps> = (props) => {
|
||||
'sider-menu-collapsed': collapsed
|
||||
})}
|
||||
>
|
||||
{menuData.map((item: MenuItem, index: number) => (
|
||||
<div key={item.key}>
|
||||
{item.children && item.children.length > 0 ? (
|
||||
<>
|
||||
<div
|
||||
className={cx(styles.groupTitle, {
|
||||
'menu-item-group-title-collapsed': collapsed
|
||||
})}
|
||||
onClick={(e) => handleToggleGroup(e, item)}
|
||||
>
|
||||
{!collapsed ? (
|
||||
<span className="group-title-text">
|
||||
<span>{item.name}</span>
|
||||
<CaretDownOutlined
|
||||
rotate={collapseKeys.has(item.key) ? -90 : 0}
|
||||
></CaretDownOutlined>
|
||||
</span>
|
||||
) : (
|
||||
<span className={styles.line}></span>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={cx(styles.menuItemGroup, {
|
||||
'menu-item-group-collapsed': collapsed,
|
||||
'menu-item-group-hidden':
|
||||
!collapsed && collapseKeys.has(item.key)
|
||||
})}
|
||||
>
|
||||
{item.children?.map((child: MenuItem) =>
|
||||
menuItemRender(child, child.key)
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
menuItemRender(item, item.key)
|
||||
)}
|
||||
<OverlayScroller
|
||||
styles={{
|
||||
wrapper: {
|
||||
paddingInline: 0,
|
||||
maxHeight: '100%'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div style={{ paddingRight: 8 }}>
|
||||
{menuData.map((item: MenuItem, index: number) => (
|
||||
<div key={item.key}>
|
||||
{item.children && item.children.length > 0 ? (
|
||||
<>
|
||||
<div
|
||||
className={cx(styles.groupTitle, {
|
||||
'menu-item-group-title-collapsed': collapsed
|
||||
})}
|
||||
onClick={(e) => handleToggleGroup(e, item)}
|
||||
>
|
||||
{!collapsed ? (
|
||||
<span className="group-title-text">
|
||||
<span>{item.name}</span>
|
||||
<CaretDownOutlined
|
||||
rotate={collapseKeys.has(item.key) ? -90 : 0}
|
||||
></CaretDownOutlined>
|
||||
</span>
|
||||
) : (
|
||||
<span className={styles.line}></span>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={cx(styles.menuItemGroup, {
|
||||
'menu-item-group-collapsed': collapsed,
|
||||
'menu-item-group-hidden':
|
||||
!collapsed && collapseKeys.has(item.key)
|
||||
})}
|
||||
>
|
||||
{item.children?.map((child: MenuItem) =>
|
||||
menuItemRender(child, child.key)
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
menuItemRender(item, item.key)
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</OverlayScroller>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export default {
|
||||
'ai.provider.ollama': 'Ollama',
|
||||
'ai.provider.openai': 'OpenAI',
|
||||
'ai.provider.openrouter': 'OpenRouter',
|
||||
'ai.provider.qwen': 'Qwen',
|
||||
'ai.provider.qwen': 'Alibaba Cloud Model Studio',
|
||||
'ai.provider.spark': 'Spark',
|
||||
'ai.provider.stepfun': 'StepFun',
|
||||
'ai.provider.together-ai': 'TogetherAI',
|
||||
|
||||
@@ -23,5 +23,7 @@ export default {
|
||||
'apikeys.accessScope.inference': 'Inference APIs',
|
||||
'apikeys.access.permissions': 'Access Permissions',
|
||||
'apikeys.type.auto': 'Auto-generated',
|
||||
'apikeys.type.custom': 'Custom'
|
||||
'apikeys.type.custom': 'Custom',
|
||||
'apikeys.button.ipConfig': 'IP Access Control',
|
||||
'quotaLimits.button.title': 'Quota Limit'
|
||||
};
|
||||
|
||||
@@ -23,11 +23,12 @@ export default {
|
||||
'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.defaultBackendParameters': 'Default Backend Parameters',
|
||||
'backend.form.parameterFormat': 'Parameter Format',
|
||||
'backend.form.parameterFormat.default': 'Backend Default',
|
||||
'backend.form.parameterFormat.space': 'Space (--key value)',
|
||||
'backend.form.parameterFormat.equal': 'Equal (--key=value)',
|
||||
'backend.form.commonParameters': 'Common Parameters',
|
||||
'backend.form.flagFormat': 'Flag Format',
|
||||
'backend.form.flagFormat.tips':
|
||||
'The format applied between an option and its value. Leave empty to keep each parameter as entered, without normalizing it.',
|
||||
'backend.form.flagFormat.space': 'Space Separated (--key value)',
|
||||
'backend.form.flagFormat.equal': 'Equal Sign (--key=value)',
|
||||
'backend.form.commonParameters': 'Common Backend Parameters',
|
||||
'backend.form.commonParameters.tips':
|
||||
'Shown as suggestions in the backend parameters input during deployment.',
|
||||
'backend.form.versionConfig': 'Versions Config',
|
||||
|
||||
@@ -37,10 +37,10 @@ export default {
|
||||
'Stress test for long-context handling. Evaluates KV cache behavior, memory usage, and backend stability.',
|
||||
'benchmark.form.profile.heavy.tips':
|
||||
'Decode-heavy generation benchmark. Measures sustained decoding speed and output token throughput.',
|
||||
'benchmark.table.filter.bygpu': 'Filter by GPU',
|
||||
'benchmark.table.filter.bymodel': 'Filter by Model',
|
||||
'benchmark.table.filter.bygpu': 'Search by GPU',
|
||||
'benchmark.table.filter.bymodel': 'Search by model',
|
||||
'benchmark.table.filter.bydataset': 'Filter by Dataset',
|
||||
'benchmark.table.filter.byProfile': 'Filter by Profile',
|
||||
'benchmark.table.filter.byProfile': 'Filter by profile',
|
||||
'benchmark.table.avg': 'Avg',
|
||||
'benchmark.table.columnSettings': 'Column Settings',
|
||||
'benchmark.detail.summary.results': 'Test Results',
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export default {
|
||||
'billing.upsell.title': 'Billing is an Enterprise feature',
|
||||
'billing.upsell.subtitle':
|
||||
'Track spend, generate invoices, and enforce budgets across teams. Upgrade to MesaStack Enterprise to manage billing.',
|
||||
'billing.upsell.featuresTitle': 'What you get in Enterprise',
|
||||
'billing.upsell.feature.usage':
|
||||
'See cost breakdowns by organization, user, and model',
|
||||
'billing.upsell.feature.invoices':
|
||||
'Generate invoices and export billing reports',
|
||||
'billing.upsell.feature.budgets':
|
||||
'Set budgets and spending limits with alerts',
|
||||
'billing.upsell.feature.chargeback':
|
||||
'Attribute and charge back usage to teams and projects',
|
||||
'billing.upsell.cta': 'Learn about Enterprise'
|
||||
};
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
'clusters.workerpool.batchSize.desc':
|
||||
'Number of workers created simultaneously in the Worker pool',
|
||||
'clusters.create.addworker.tips':
|
||||
'Please make sure the <a href={link} target="_blank">prerequisites</a> for {label} are met before executing the following command.',
|
||||
'Please make sure the <a href={link} target="_blank">prerequisites</a> are met before executing the following command.',
|
||||
'clusters.create.addCommand.tips':
|
||||
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
||||
'clusters.create.addCommand.k8s.tips':
|
||||
@@ -70,8 +70,13 @@ export default {
|
||||
'For <span class="bold-text">non-Docker</span> clusters, please register clusters or manage worker pools from the Clusters page.',
|
||||
'clusters.addworker.selectGPU': 'Select GPU Vendor',
|
||||
'clusters.addworker.selectGPU.multiTag': 'Multi-select',
|
||||
'clusters.addworker.selectGPU.subtitle':
|
||||
'You can select multiple GPU Vendors or none for CPU-only clusters',
|
||||
'clusters.addworker.checkEnv': 'Check Environment',
|
||||
'clusters.addworker.checkEnv.cpuOnlyTips':
|
||||
'Use the following command to verify that the Kubernetes cluster has at least one ready node. You are registering a CPU-only cluster.',
|
||||
'clusters.addworker.specifyArgs': 'Specify Arguments',
|
||||
'clusters.addworker.dtkVersion': 'DTK Version',
|
||||
'clusters.addworker.runCommand': 'Run Command',
|
||||
'clusters.addworker.specifyWorkerIP': 'Worker IP',
|
||||
'clusters.addworker.detectWorkerIP': 'Auto-detect Worker IP',
|
||||
@@ -80,7 +85,7 @@ export default {
|
||||
'clusters.addworker.detectWorkerAddress.tips':
|
||||
'Defaults to Worker IP if not specified.',
|
||||
'clusters.addworker.externalIP.tips':
|
||||
'If running in a VPC or private network, please specify the Worker external address reachable by the GPUStack Server.',
|
||||
'If running in a VPC or private network, please specify the Worker external address reachable by the MesaStack Server.',
|
||||
'clusters.addworker.enterWorkerIP': 'Enter worker IP',
|
||||
'clusters.addworker.enterWorkerIP.error': 'Please enter the worker IP.',
|
||||
'clusters.addworker.enterWorkerAddress': 'Enter worker external address',
|
||||
@@ -108,18 +113,20 @@ export default {
|
||||
'{count} new worker has been added to the cluster.',
|
||||
'clusters.addworker.message.success_multiple':
|
||||
'{count} new workers have been added to the cluster.',
|
||||
'clusters.create.serverUrl': 'GPUStack Server URL',
|
||||
'clusters.create.serverUrl': 'MesaStack Server URL',
|
||||
'clusters.create.workerConfig': 'Worker Configuration',
|
||||
'clusters.edit.k8sOptions.changed.tip':
|
||||
'You have changed the Kubernetes options. Re-run the registration command on the target cluster for the changes to take effect.',
|
||||
'clusters.addworker.containerName': 'Worker Container Name',
|
||||
'clusters.addworker.containerName.tips':
|
||||
'Specify a name for the worker container.',
|
||||
'clusters.addworker.dataVolume': 'GPUStack Data Volume',
|
||||
'clusters.addworker.dataVolume': 'MesaStack Data Volume',
|
||||
'clusters.addworker.dataVolume.tips':
|
||||
'Specify a data storage path for GPUStack.',
|
||||
'Specify a data storage path for MesaStack.',
|
||||
'clusters.table.ip.internal': 'Internal',
|
||||
'clusters.table.ip.external': 'External',
|
||||
'clusters.form.serverUrl.tips':
|
||||
'Specify an externally accessible GPUStack service URL if the worker cannot access GPUStack Server directly.',
|
||||
'Specify an externally accessible MesaStack service URL if the worker cannot access MesaStack Server directly.',
|
||||
'clusters.form.setDefault': 'Set as Default',
|
||||
'clusters.form.setDefault.tips': 'Default for deployment.',
|
||||
'clusters.addworker.noClusters': 'No available Docker clusters found',
|
||||
@@ -137,7 +144,7 @@ export default {
|
||||
'clusters.addworker.theadNotes-02':
|
||||
'T-Head PPU uses the Container Device Interface (CDI) for device injection and requires the <span class="bold-text">/var/run/cdi</span> directory to be available for CDI generation.',
|
||||
'clusters.addworker.nvidiaNotes':
|
||||
'The built-in inference backends in GPUStack v2.1 require <span class="bold-text">CUDA 12.6+</span>. Please ensure your NVIDIA driver version is <span class="bold-text">560</span> or newer.',
|
||||
'The built-in inference backends in MesaStack require <span class="bold-text">CUDA 12.8+</span>. Please ensure your NVIDIA driver version is <span class="bold-text">570</span> or newer.',
|
||||
'clusters.volume.title': 'Volume Mounts',
|
||||
'clusters.volume.name': 'Volume Name',
|
||||
'clusters.volume.mountPath': 'Container Path',
|
||||
@@ -164,8 +171,8 @@ export default {
|
||||
'clusters.volume.add': 'Add Volume Mount',
|
||||
'clusters.systemDefaultContainerRegistry.title': 'Default Container Registry',
|
||||
'clusters.systemDefaultContainerRegistry.tip':
|
||||
'Default registry used to resolve GPUStack images for this cluster. Falls back to the server default when unset.',
|
||||
'clusters.k8sOptions.title': 'K8s Deployment Options',
|
||||
'Default registry used to resolve MesaStack images for this cluster. Falls back to the server default when unset.',
|
||||
'clusters.k8sOptions.title': 'Kubernetes Deployment Options',
|
||||
'clusters.imageCredentials.title': 'Image Credentials',
|
||||
'clusters.imageCredentials.add': 'Add Credential',
|
||||
'clusters.imageCredentials.registry': 'Registry',
|
||||
@@ -176,13 +183,18 @@ export default {
|
||||
'Pod nodeSelector applied to every worker DaemonSet — only nodes whose labels match are eligible to run the worker.',
|
||||
'clusters.operatorImage.title': 'Operator Image',
|
||||
'clusters.operatorImage.tip':
|
||||
'Override for the GPUStack Operator container image. Leave empty to use the server default.',
|
||||
'Override for the MesaStack Operator container image. Leave empty to use the server default.',
|
||||
'clusters.namespace.title': 'Namespace',
|
||||
'clusters.namespace.tip':
|
||||
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
||||
'clusters.gpuInstances.title': 'GPU Instances',
|
||||
'clusters.gpuInstances.tip': 'Enable GPU instance support for this cluster.',
|
||||
'clusters.gpuInstances.staticAddress': 'Static Access Address',
|
||||
'clusters.clusterType.title': 'Cluster Type',
|
||||
'clusters.modelService.title': 'Model Service',
|
||||
'clusters.modelService.tip':
|
||||
'For LLM inference and API serving — e.g. exposing model APIs and token-based services.',
|
||||
'clusters.gpuInstances.title': 'GPU Service',
|
||||
'clusters.gpuInstances.tip':
|
||||
'For on-demand GPU compute — e.g. interactive development, training jobs, or custom environments.',
|
||||
'clusters.gpuInstances.staticAddress': 'GPU Service Static Access Address',
|
||||
'clusters.gpuInstances.staticAddress.tip':
|
||||
'Static address the operator uses to access GPU instances in this cluster (e.g. a LoadBalancer VIP). Optional.'
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ export default {
|
||||
'common.button.enabled': 'Enabled',
|
||||
'common.button.disabled': 'Disabled',
|
||||
'common.button.upgrade': 'Upgrade',
|
||||
'common.enterprise.feature': 'Available in MesaStack Enterprise',
|
||||
'common.input.holder': 'Please enter',
|
||||
'common.validate.value': '{name} value is required',
|
||||
'common.button.edit': 'Edit',
|
||||
@@ -55,6 +56,7 @@ export default {
|
||||
'common.button.viewevent': 'View Events',
|
||||
'common.button.recreate': 'Recreate',
|
||||
'common.table.operation': 'Operations',
|
||||
'common.table.creator': 'Creator',
|
||||
'common.table.createTime': 'Created',
|
||||
'common.table.updateTime': 'Updated',
|
||||
'common.table.description': 'Description',
|
||||
@@ -65,6 +67,7 @@ export default {
|
||||
'common.search.name.placeholder': 'filter by name',
|
||||
'common.search.id.placeholder': 'filter by ID',
|
||||
'common.filter.byId': 'filter by ID',
|
||||
'common.filter.byCreator': 'Filter by creator',
|
||||
'common.table.type': 'Type',
|
||||
'common.table.default': 'Default Value',
|
||||
'common.copy.success': 'Copied success!',
|
||||
@@ -166,6 +169,7 @@ export default {
|
||||
'common.time.hour': 'hour',
|
||||
'common.time.minute': 'minutes',
|
||||
'common.issue.report': 'Report an issue',
|
||||
'common.github.star.tooltip': 'Star us on GitHub',
|
||||
'common.social.discord': 'Join Our Discord',
|
||||
'common.table.mark': 'Comment',
|
||||
'common.table.rollback.mark': 'Rollback Comment',
|
||||
@@ -208,7 +212,7 @@ export default {
|
||||
'common.form.password': 'Password',
|
||||
'common.form.username': 'Username',
|
||||
'common.login.rember': 'Remember me',
|
||||
'settings.company': 'GPUStack.ai',
|
||||
'settings.company': 'MesaStack',
|
||||
'common.button.help': 'Help',
|
||||
'common.button.feedback': 'Feedback',
|
||||
'common.button.docs': 'Documentation',
|
||||
@@ -257,6 +261,10 @@ export default {
|
||||
'common.login.auth': 'Authenticating...',
|
||||
'common.login.auth.failed': 'Authentication failed',
|
||||
'common.login.password': 'Log in with Password',
|
||||
'common.login.username.holder': 'Please enter username',
|
||||
'common.login.password.holder': 'Please enter password',
|
||||
'common.login.newpassword.holder': 'Please enter new password',
|
||||
'common.login.confirm.holder': 'Please enter password again',
|
||||
'common.external.login': 'Log in with {type}',
|
||||
'common.sso.noConfig':
|
||||
'Single sign-on is not enabled on this system. Please contact your administrator.',
|
||||
@@ -285,5 +293,7 @@ export default {
|
||||
'common.image.limit.height': 'Image height must be {height}.',
|
||||
'common.remaining': 'Remaining {count}',
|
||||
'common.max': 'Max {count}',
|
||||
'common.validate.group': 'Please complete the {group} configuration'
|
||||
'common.max.count': '{label} Count',
|
||||
'common.validate.group': 'Please complete the {group} configuration',
|
||||
'common.preferences': 'Preferences'
|
||||
};
|
||||
|
||||
@@ -13,7 +13,11 @@ export default {
|
||||
'gpuservice.template.command.placeholder':
|
||||
'Separate arguments with spaces; wrap arguments containing spaces in quotes, e.g.: /bin/bash -c "echo hello world"',
|
||||
'gpuservice.template.mountPath': 'Mount Path',
|
||||
'gpuservice.template.mountPath.tips':
|
||||
'The default mount path for the storage volume when creating an instance from this template. Useful for persisting data that needs to be retained while the instance is running.',
|
||||
'gpuservice.template.containerDisk': 'Container Disk (GB)',
|
||||
'gpuservice.template.containerDisk.tips':
|
||||
'The size of the container system disk.',
|
||||
'gpuservice.template.memory': 'Memory (GB)',
|
||||
'gpuservice.instance.containerDisk.remaining':
|
||||
'Container Disk (Max {count} GB)',
|
||||
@@ -97,12 +101,17 @@ export default {
|
||||
'gpuservice.instance.templates': 'Instance Templates',
|
||||
'gpuservice.instance.section.storage': 'Storage',
|
||||
'gpuservice.instance.type.required': 'Please select an instance type',
|
||||
'gpuservice.instance.type.noAvailable': 'No instance type available',
|
||||
'gpuservice.instance.gpuCount': 'GPU Count',
|
||||
'gpuservice.instance.gpuCount.required': 'Please enter the GPU count',
|
||||
'gpuservice.instance.gpuCount.max':
|
||||
'Please select at most {count} GPU card(s)',
|
||||
'gpuservice.instance.gpuCount.min':
|
||||
'Please select at least {count} GPU card(s)',
|
||||
'gpuservice.instance.cpuCount.max':
|
||||
'Please select at most {count} CPU core(s)',
|
||||
'gpuservice.instance.cpuCount.min':
|
||||
'Please select at least {count} CPU core(s)',
|
||||
'gpuservice.instance.gpuCount.noAvailable':
|
||||
'No available GPU resources, please choose another instance type.',
|
||||
'gpuservice.instance.gpuCount.zero':
|
||||
@@ -111,7 +120,13 @@ export default {
|
||||
'gpuservice.instance.sliced': 'Sliced',
|
||||
'gpuservice.instance.memory': 'VRAM',
|
||||
'gpuservice.instance.ram': 'RAM',
|
||||
'gpuservice.instance.os': 'OS',
|
||||
'gpuservice.instance.arch': 'Arch',
|
||||
'gpuservice.instance.disk': 'Disk',
|
||||
'gpuservice.table.count': 'Count',
|
||||
'gpuservice.instance.disk.system': 'System Disk',
|
||||
'gpuservice.instance.disk.ephemeral': 'Ephemeral Storage',
|
||||
'gpuservice.instance.disk.persistent': 'Persistent Storage',
|
||||
'gpuservice.instance.search.type.placeholder': 'Search by name',
|
||||
'gpuservice.instance.search.template.placeholder':
|
||||
'Search by template name, image or mount path',
|
||||
@@ -147,9 +162,8 @@ export default {
|
||||
'gpuservice.storage.temporary.tips':
|
||||
'Data is cleared when the instance stops.',
|
||||
'gpuservice.storage.persistentVolume.tips':
|
||||
'Data persists across restarts and is deleted only when the instance is terminated. Cannot be shared with other instances.',
|
||||
'gpuservice.storage.persistentVolume.required':
|
||||
'Please select a persistent volume',
|
||||
'Data persists across instance restarts. Persistent volumes remain intact after instance termination and can be shared by multiple instances.',
|
||||
'gpuservice.storage.persistentVolume.required': 'Please select a storage',
|
||||
'gpuservice.storage.persistentVolume.capacity': 'Capacity (GB)',
|
||||
'gpuservice.storage.persistentVolume.capacity.required':
|
||||
'Please enter capacity',
|
||||
@@ -159,5 +173,10 @@ export default {
|
||||
'gpuservice.storage.tempCapacity.required':
|
||||
'Please enter the temporary storage capacity',
|
||||
'gpuservice.form.rule.name':
|
||||
"Lowercase letters, numbers, and '-'. Start and end with a letter or number, no consecutive '-', max 63 characters."
|
||||
"Lowercase letters, numbers, and '-'. Start and end with a letter or number, no consecutive '-', max 63 characters.",
|
||||
'gpuservice.form.storage.select': 'Select Storage',
|
||||
'gpuservice.creator': 'Creator',
|
||||
'gpuservice.owner.global': 'Global',
|
||||
'gpuservice.template.group.yours': 'Your Templates',
|
||||
'gpuservice.template.group.global': 'Global Templates'
|
||||
};
|
||||
|
||||
@@ -31,15 +31,19 @@ export default {
|
||||
'menu.accessControl': 'Access Control',
|
||||
'menu.accessControl.apikeys': 'API Keys',
|
||||
'menu.accessControl.users': 'Users',
|
||||
'menu.profile': 'Profile',
|
||||
'menu.accessControl.organizations': 'Organizations',
|
||||
'menu.profile': 'Preferences',
|
||||
'menu.login': 'Login',
|
||||
'menu.usage': 'Usage',
|
||||
'menu.usage.usage': 'Usage',
|
||||
'menu.billingAndUsage': 'Usage & Billing',
|
||||
'menu.billingAndUsage.usage': 'Usage',
|
||||
'menu.billingAndUsage.billing': 'Billing',
|
||||
'menu.404': '404',
|
||||
'menu.clusterManagement': 'Cluster Management',
|
||||
'menu.clusterManagement.clusters': 'Clusters',
|
||||
'menu.clusterManagement.credentials': 'Cloud Credentials',
|
||||
'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
||||
'menu.clusterManagement.clusterCreate': 'Create Cluster',
|
||||
'menu.resources.clusters': 'Clusters',
|
||||
'menu.resources.credentials': 'Cloud Credentials',
|
||||
'menu.resources.clusterDetail': 'Cluster Detail',
|
||||
'menu.resources.clusterCreate': 'Create Cluster',
|
||||
'menu.models.backendsList': 'Inference Backends',
|
||||
'menu.gpuService': 'GPU Service',
|
||||
'menu.gpuService.instances': 'GPU Instances',
|
||||
|
||||
@@ -14,7 +14,7 @@ export default {
|
||||
'models.form.env': 'Environment Variables',
|
||||
'models.form.configurations': 'Configurations',
|
||||
'models.form.s3address': 'S3 Address',
|
||||
'models.form.partialoffload.tips': `When CPU offloading is enabled, GPUStack will allocate CPU memory if GPU resources are insufficient. You must correctly configure the inference backend to use hybrid CPU+GPU or full CPU inference.`,
|
||||
'models.form.partialoffload.tips': `When CPU offloading is enabled, MesaStack will allocate CPU memory if GPU resources are insufficient. You must correctly configure the inference backend to use hybrid CPU+GPU or full CPU inference.`,
|
||||
'models.form.distribution.tips': `Allows for offloading part of the model's layers to single or multiple remote workers when the resources of a worker are insufficient.`,
|
||||
'models.openinplayground': 'Open in Playground',
|
||||
'models.instances': 'instances',
|
||||
@@ -24,7 +24,7 @@ export default {
|
||||
'model.deploy.sort': 'Sort',
|
||||
'model.deploy.search.placeholder': 'Type <kbd>/</kbd> to search models',
|
||||
'model.form.ollamatips':
|
||||
'Tip: The following are the preconfigured Ollama models in GPUStack. Please select the model you want, or directly enter the model you wish to deploy in the 【{name}】 input box on the right.',
|
||||
'Tip: The following are the preconfigured Ollama models in MesaStack. Please select the model you want, or directly enter the model you wish to deploy in the 【{name}】 input box on the right.',
|
||||
'models.sort.name': 'Name',
|
||||
'models.sort.size': 'Size',
|
||||
'models.sort.likes': 'Likes',
|
||||
@@ -87,7 +87,7 @@ export default {
|
||||
'models.form.filePath': 'Model Path',
|
||||
'models.form.backendVersion': 'Backend Version',
|
||||
'models.form.backendVersion.tips':
|
||||
'To use the desired version of {backend}{version}, the system will automatically create a virtual environment in the online environment to install the corresponding version. After a GPUStack upgrade, the backend version will remain fixed. {link}',
|
||||
'To use the desired version of {backend}{version}, the system will automatically create a virtual environment in the online environment to install the corresponding version. After a MesaStack upgrade, the backend version will remain fixed. {link}',
|
||||
'models.form.gpuselector': 'GPU Selector',
|
||||
'models.form.backend.llamabox':
|
||||
'For GGUF format models, supports Linux, macOS, and Windows.',
|
||||
@@ -277,7 +277,7 @@ export default {
|
||||
'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`,
|
||||
'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.modelfile.notfound': `The model file path you specified does not exist on the MesaStack server. It's recommended to place the model file at the same path on both the MesaStack server and MesaStack workers. This helps MesaStack make better decisions.`,
|
||||
'models.form.readyWorkers': 'workers ready',
|
||||
'models.form.maxContextLength': 'Maximum Context Length',
|
||||
'models.form.backend.helperText':
|
||||
@@ -291,7 +291,7 @@ export default {
|
||||
'models.instance.startHistory': 'Run History',
|
||||
'models.instance.startHistory.tips':
|
||||
'Shows logs from the run before the last error-triggered restart.',
|
||||
'models.form.lora.label': 'LoRA Adapter',
|
||||
'models.form.lora.label': 'LoRA Adapters',
|
||||
'models.form.lora.add': 'Add LoRA Adapter',
|
||||
'models.form.lora.select': 'Select LoRA',
|
||||
'models.form.lora.name': 'LoRA name',
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export default {
|
||||
'organizations.upsell.title': 'Organizations are an Enterprise feature',
|
||||
'organizations.upsell.subtitle':
|
||||
'Multi-tenancy lets you isolate users, resources, and quotas across teams. Upgrade to MesaStack Enterprise to manage organizations.',
|
||||
'organizations.upsell.featuresTitle': 'What you get in Enterprise',
|
||||
'organizations.upsell.feature.orgs':
|
||||
'Create organizations to group users and isolate workloads',
|
||||
'organizations.upsell.feature.members':
|
||||
'Manage members and roles per organization',
|
||||
'organizations.upsell.feature.quotas':
|
||||
'Set resource and token quotas per organization',
|
||||
'organizations.upsell.feature.isolation':
|
||||
'Scope API keys, model deployments, and resources per organization',
|
||||
'organizations.upsell.cta': 'Learn about Enterprise'
|
||||
};
|
||||
@@ -52,7 +52,7 @@ export default {
|
||||
'resources.worker.container.supported': 'Do not support macOS or Windows.',
|
||||
'resources.worker.current.version': 'Current version is {version}.',
|
||||
'resources.worker.driver.install':
|
||||
'Install <a href="https://docs.gpustack.ai/latest/installation/installation-requirements/" target="_blank">required drivers and libraries</a> prior to GPUStack installation.',
|
||||
'Install <a href="https://docs.gpustack.ai/latest/installation/installation-requirements/" target="_blank">required drivers and libraries</a> prior to MesaStack installation.',
|
||||
'resources.worker.select.command':
|
||||
'Select a label to generate the command and copy it using the copy button.',
|
||||
'resources.worker.script.install': 'Script Installation',
|
||||
@@ -89,7 +89,7 @@ export default {
|
||||
'Paste the <span class="bold-text">Token</span>.',
|
||||
'resources.register.worker.step7':
|
||||
'Click <span class="bold-text">Restart</span> to apply the settings.',
|
||||
'resources.register.install.title': 'Install GPUStack on {os}',
|
||||
'resources.register.install.title': 'Install MesaStack on {os}',
|
||||
'resources.register.download':
|
||||
'Download and install the <a href={url} target="_blank">installer</a>. Only supported: {versions}.',
|
||||
'resource.register.maos.support': 'Apple Silicon (M series), macOS 14+',
|
||||
@@ -111,7 +111,7 @@ export default {
|
||||
'No available clusters. Please create a cluster before adding a node.',
|
||||
'resources.metrics.details': 'Monitoring',
|
||||
'resoureces.worker.upgrade.tips':
|
||||
'Please upgrade to match the GPUStack Server version.',
|
||||
'Please upgrade to match the MesaStack Server version.',
|
||||
'resources.worker.version': 'Worker Version: {version}',
|
||||
'resources.server.version': 'Server Version: {version}',
|
||||
'resources.worker.currentVersion': 'Current Version: {version}',
|
||||
|
||||
@@ -17,9 +17,15 @@ export default {
|
||||
'usage.table.user.apiKeysUsed': 'API Keys Used',
|
||||
'usage.table.lastActive': 'Last Active',
|
||||
'usage.filter.granularity': 'Granularity',
|
||||
'usage.filter.granularity.hour': 'Hour',
|
||||
'usage.filter.granularity.day': 'Day',
|
||||
'usage.filter.granularity.week': 'Week',
|
||||
'usage.filter.granularity.month': 'Month',
|
||||
'usage.tabs.summary': 'Summary',
|
||||
'usage.tabs.tokens': 'Tokens',
|
||||
'usage.tabs.gpuInstances': 'GPU Instances',
|
||||
'usage.tabs.storage': 'Storage',
|
||||
'usage.tabs.resourceEvents': 'Resource Events',
|
||||
'usage.tabs.models': 'Models',
|
||||
'usage.tabs.apikeys': 'API Keys',
|
||||
'usage.tabs.users': 'User',
|
||||
@@ -32,5 +38,70 @@ export default {
|
||||
'usage.chart.cached': 'Cached',
|
||||
'usage.chart.uncached': 'Uncached',
|
||||
'usage.chart.inputTokensCached': 'Input Tokens (Cached/Uncached)',
|
||||
'usage.table.inputTokensCached': 'Input Tokens Cached'
|
||||
'usage.table.inputTokensCached': 'Input Tokens Cached',
|
||||
|
||||
// --- Resource usage: shared metrics & units ---
|
||||
'usage.metric.tokens': 'Tokens',
|
||||
'usage.metric.input': 'Input',
|
||||
'usage.metric.output': 'Output',
|
||||
'usage.metric.gpuHours': 'GPU Hours',
|
||||
'usage.metric.instanceHours': 'Instance Hours',
|
||||
'usage.metric.gbDays': 'GB-Days',
|
||||
'usage.metric.gbHours': 'GB-Hours',
|
||||
'usage.metric.activeUsers': 'Active Users',
|
||||
'usage.metric.activeInstances': 'Active Instances',
|
||||
'usage.metric.activeStorage': 'Active Storage',
|
||||
'usage.metric.activeVolumes': 'Active Volumes',
|
||||
'usage.metric.storageTypes': 'Storage Types',
|
||||
'usage.metric.gpuHours.tip':
|
||||
'Instance running time weighted by GPU count: an instance with N GPUs running for H hours counts as N × H GPU-hours. Equal to Instance Hours when every instance uses a single GPU.',
|
||||
'usage.metric.instanceHours.tip':
|
||||
'Total running time summed across all instances, regardless of how many GPUs each uses. One instance running for 2 hours = 2 instance-hours.',
|
||||
'usage.metric.gbDays.tip':
|
||||
'Storage capacity integrated over time, in GB × days: 10 GB kept for 5 days = 50 GB-days. (= GB-Hours ÷ 24)',
|
||||
'usage.metric.gbHours.tip':
|
||||
'Storage capacity integrated over time, in GB × hours: 10 GB kept for 5 hours = 50 GB-hours.',
|
||||
|
||||
// --- Resource usage: common table / labels ---
|
||||
'usage.common.noData': 'No data',
|
||||
'usage.common.unknown': 'unknown',
|
||||
'usage.table.date': 'Date',
|
||||
'usage.table.name': 'Name',
|
||||
'usage.table.user': 'User',
|
||||
'usage.table.users': 'Users',
|
||||
'usage.table.type': 'Type',
|
||||
'usage.table.instance': 'Instance',
|
||||
'usage.table.instanceType': 'Instance Type',
|
||||
'usage.table.instanceTypes': 'Instance Types',
|
||||
'usage.table.instances': 'Instances',
|
||||
'usage.table.capacity': 'Capacity',
|
||||
'usage.export.tableNamed': 'Export Table Data — {name}',
|
||||
|
||||
// --- Summary tab ---
|
||||
'usage.summary.compute': 'Compute',
|
||||
'usage.summary.tokensOverTime': 'Tokens over time',
|
||||
'usage.summary.gpuHoursOverTime': 'GPU Hours over time',
|
||||
'usage.summary.gbDaysOverTime': 'GB-Days over time',
|
||||
|
||||
// --- GPU Instances / Storage filters ---
|
||||
'usage.filter.instance': 'Filter by instance',
|
||||
'usage.filter.storage': 'Filter by storage',
|
||||
|
||||
// --- Resource events ---
|
||||
'usage.events.resourceType': 'Resource type',
|
||||
'usage.events.eventType': 'Event type',
|
||||
'usage.events.resourceName': 'Filter by name',
|
||||
'usage.events.col.time': 'Time',
|
||||
'usage.events.col.resource': 'Resource',
|
||||
'usage.events.col.event': 'Event',
|
||||
'usage.events.col.message': 'Message',
|
||||
'usage.events.resource.gpuInstance': 'GPU Instance',
|
||||
'usage.events.resource.cpuInstance': 'CPU Instance',
|
||||
'usage.events.type.created': 'Created',
|
||||
'usage.events.type.deleted': 'Deleted',
|
||||
'usage.events.type.started': 'Started',
|
||||
'usage.events.type.stopped': 'Stopped',
|
||||
'usage.events.type.updated': 'Updated',
|
||||
'usage.events.type.attached': 'Attached',
|
||||
'usage.events.type.detached': 'Detached'
|
||||
};
|
||||
|
||||
@@ -32,12 +32,12 @@ export default {
|
||||
'users.password.confirm.empty': 'Please confirm the new password.',
|
||||
'users.password.confirm.error': 'The two passwords entered do not match.',
|
||||
'users.login.title': 'Log in to',
|
||||
'users.version.islatest': 'GPUStack {version} is the latest version',
|
||||
'users.version.update': 'GPUStack {version} is available',
|
||||
'users.version.islatest': 'MesaStack {version} is the latest version',
|
||||
'users.version.update': 'MesaStack {version} is available',
|
||||
'users.settings.title': 'User Settings',
|
||||
'users.status.activate': 'Activate Account',
|
||||
'users.status.deactivate': 'Deactivate Account',
|
||||
'users.status.inactiveAccount': 'Inactive Account',
|
||||
'users.login.getInitialPassword':
|
||||
'Run the following command on your GPUStack Server to retrieve the initial admin password.'
|
||||
'Run the following command on your MesaStack Server to retrieve the initial admin password.'
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ export default {
|
||||
'vendor.hygon': 'Hygon',
|
||||
'vendor.moorthreads': 'Moore Threads',
|
||||
'vendor.iluvatar': 'Iluvatar',
|
||||
'vendor.metax': 'Metax',
|
||||
'vendor.metax': 'MetaX',
|
||||
'vendor.cambricon': 'Cambricon',
|
||||
'vendor.thead': 'T-Head PPU'
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export default {
|
||||
'ai.provider.ollama': 'Ollama',
|
||||
'ai.provider.openai': 'OpenAI',
|
||||
'ai.provider.openrouter': 'OpenRouter',
|
||||
'ai.provider.qwen': 'Qwen',
|
||||
'ai.provider.qwen': 'Alibaba Cloud Model Studio',
|
||||
'ai.provider.spark': 'Spark',
|
||||
'ai.provider.stepfun': 'StepFun',
|
||||
'ai.provider.together-ai': 'TogetherAI',
|
||||
|
||||
@@ -23,7 +23,9 @@ export default {
|
||||
'apikeys.accessScope.inference': 'Inference APIs',
|
||||
'apikeys.access.permissions': 'Access Permissions',
|
||||
'apikeys.type.auto': 'Auto-generated',
|
||||
'apikeys.type.custom': 'Custom'
|
||||
'apikeys.type.custom': 'Custom',
|
||||
'apikeys.button.ipConfig': 'IP Access Control',
|
||||
'quotaLimits.button.title': 'Quota Limit'
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
|
||||
@@ -23,11 +23,12 @@ export default {
|
||||
'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.defaultBackendParameters': 'Default Backend Parameters',
|
||||
'backend.form.parameterFormat': 'パラメータ形式',
|
||||
'backend.form.parameterFormat.default': 'バックエンドのデフォルト',
|
||||
'backend.form.parameterFormat.space': 'スペース区切り (--key value)',
|
||||
'backend.form.parameterFormat.equal': 'イコール連結 (--key=value)',
|
||||
'backend.form.commonParameters': 'よく使うパラメータ',
|
||||
'backend.form.flagFormat': 'フラグ形式',
|
||||
'backend.form.flagFormat.tips':
|
||||
'オプションとその値を連結する形式。空欄の場合は各パラメータを入力されたまま保持し、形式を統一しません。',
|
||||
'backend.form.flagFormat.space': 'スペース区切り (--key value)',
|
||||
'backend.form.flagFormat.equal': 'イコール記号 (--key=value)',
|
||||
'backend.form.commonParameters': 'よく使うバックエンドパラメータ',
|
||||
'backend.form.commonParameters.tips':
|
||||
'モデルのデプロイ時にバックエンドパラメータ入力欄の候補として表示されます。',
|
||||
'backend.form.versionConfig': 'Versions Config',
|
||||
|
||||
@@ -37,8 +37,8 @@ export default {
|
||||
'Stress test for long-context handling. Evaluates KV cache behavior, memory usage, and backend stability.',
|
||||
'benchmark.form.profile.heavy.tips':
|
||||
'Decode-heavy generation benchmark. Measures sustained decoding speed and output token throughput.',
|
||||
'benchmark.table.filter.bygpu': 'Filter by GPU',
|
||||
'benchmark.table.filter.bymodel': 'Filter by Model',
|
||||
'benchmark.table.filter.bygpu': 'GPU 検索',
|
||||
'benchmark.table.filter.bymodel': 'モデル検索',
|
||||
'benchmark.table.filter.bydataset': 'Filter by Dataset',
|
||||
'benchmark.table.filter.byProfile': 'Filter by Profile',
|
||||
'benchmark.table.avg': 'Avg',
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export default {
|
||||
'billing.upsell.title': 'Billing is an Enterprise feature',
|
||||
'billing.upsell.subtitle':
|
||||
'Track spend, generate invoices, and enforce budgets across teams. Upgrade to MesaStack Enterprise to manage billing.',
|
||||
'billing.upsell.featuresTitle': 'What you get in Enterprise',
|
||||
'billing.upsell.feature.usage':
|
||||
'See cost breakdowns by organization, user, and model',
|
||||
'billing.upsell.feature.invoices':
|
||||
'Generate invoices and export billing reports',
|
||||
'billing.upsell.feature.budgets':
|
||||
'Set budgets and spending limits with alerts',
|
||||
'billing.upsell.feature.chargeback':
|
||||
'Attribute and charge back usage to teams and projects',
|
||||
'billing.upsell.cta': 'Learn about Enterprise'
|
||||
};
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
'clusters.workerpool.batchSize.desc':
|
||||
'Number of workers created simultaneously in the Worker pool',
|
||||
'clusters.create.addworker.tips':
|
||||
'Please make sure the <a href={link} target="_blank">prerequisites</a> for {label} are met before executing the following command.',
|
||||
'Please make sure the <a href={link} target="_blank">prerequisites</a> are met before executing the following command.',
|
||||
'clusters.create.addCommand.tips':
|
||||
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
||||
'clusters.create.addCommand.k8s.tips':
|
||||
@@ -70,8 +70,13 @@ export default {
|
||||
'For <span class="bold-text">non-Docker</span> clusters, please register clusters or manage worker pools from the Clusters page.',
|
||||
'clusters.addworker.selectGPU': 'Select GPU Vendor',
|
||||
'clusters.addworker.selectGPU.multiTag': 'Multi-select',
|
||||
'clusters.addworker.selectGPU.subtitle':
|
||||
'複数の GPU ベンダーを選択するか、CPU クラスター専用の場合は選択不要です',
|
||||
'clusters.addworker.checkEnv': 'Check Environment',
|
||||
'clusters.addworker.checkEnv.cpuOnlyTips':
|
||||
'以下のコマンドを使用して、Kubernetes クラスターに少なくとも 1 つのレディーノードがあることを確認してください。CPU クラスターを登録しています。',
|
||||
'clusters.addworker.specifyArgs': 'Specify Arguments',
|
||||
'clusters.addworker.dtkVersion': 'DTK バージョン',
|
||||
'clusters.addworker.runCommand': 'Run Command',
|
||||
'clusters.addworker.specifyWorkerIP': 'Worker IP',
|
||||
'clusters.addworker.detectWorkerIP': 'Worker IP を自動検出',
|
||||
@@ -108,18 +113,20 @@ export default {
|
||||
'{count} new worker has been added to the cluster.',
|
||||
'clusters.addworker.message.success_multiple':
|
||||
'{count} new workers have been added to the cluster.',
|
||||
'clusters.create.serverUrl': 'GPUStack Server URL',
|
||||
'clusters.create.serverUrl': 'MesaStack Server URL',
|
||||
'clusters.create.workerConfig': 'Worker Configuration',
|
||||
'clusters.edit.k8sOptions.changed.tip':
|
||||
'Kubernetes オプションを変更しました。変更を有効にするには、対象クラスターで登録コマンドを再実行してください。',
|
||||
'clusters.addworker.containerName': 'Worker Container Name',
|
||||
'clusters.addworker.containerName.tips':
|
||||
'Specify a name for the worker container.',
|
||||
'clusters.addworker.dataVolume': 'GPUStack Data Volume',
|
||||
'clusters.addworker.dataVolume': 'MesaStack Data Volume',
|
||||
'clusters.addworker.dataVolume.tips':
|
||||
'Specify a data storage path for GPUStack.',
|
||||
'Specify a data storage path for MesaStack.',
|
||||
'clusters.table.ip.internal': 'Internal',
|
||||
'clusters.table.ip.external': 'External',
|
||||
'clusters.form.serverUrl.tips':
|
||||
'Specify an externally accessible GPUStack service URL if the worker cannot access GPUStack Server directly.',
|
||||
'Specify an externally accessible MesaStack service URL if the worker cannot access MesaStack Server directly.',
|
||||
'clusters.form.setDefault': 'Set as Default',
|
||||
'clusters.form.setDefault.tips': 'Default for deployment.',
|
||||
'clusters.addworker.noClusters': 'No available Docker clusters found',
|
||||
@@ -137,7 +144,7 @@ export default {
|
||||
'clusters.addworker.theadNotes-02':
|
||||
'T-Head PPU uses the Container Device Interface (CDI) for device injection and requires the <span class="bold-text">/var/run/cdi</span> directory to be available for CDI generation.',
|
||||
'clusters.addworker.nvidiaNotes':
|
||||
'The built-in inference backends in GPUStack v2.1 require <span class="bold-text">CUDA 12.6+</span>. Please ensure your NVIDIA driver version is <span class="bold-text">560</span> or newer.',
|
||||
'The built-in inference backends in MesaStack require <span class="bold-text">CUDA 12.8+</span>. Please ensure your NVIDIA driver version is <span class="bold-text">570</span> or newer.',
|
||||
'clusters.volume.title': 'Volume Mounts',
|
||||
'clusters.volume.name': 'Volume Name',
|
||||
'clusters.volume.mountPath': 'Container Path',
|
||||
@@ -164,8 +171,8 @@ export default {
|
||||
'clusters.volume.add': 'Add Volume Mount',
|
||||
'clusters.systemDefaultContainerRegistry.title': 'Default Container Registry',
|
||||
'clusters.systemDefaultContainerRegistry.tip':
|
||||
'Default registry used to resolve GPUStack images for this cluster. Falls back to the server default when unset.',
|
||||
'clusters.k8sOptions.title': 'K8s Deployment Options',
|
||||
'Default registry used to resolve MesaStack images for this cluster. Falls back to the server default when unset.',
|
||||
'clusters.k8sOptions.title': 'Kubernetes Deployment Options',
|
||||
'clusters.imageCredentials.title': 'Image Credentials',
|
||||
'clusters.imageCredentials.add': 'Add Credential',
|
||||
'clusters.imageCredentials.registry': 'Registry',
|
||||
@@ -176,13 +183,18 @@ export default {
|
||||
'Pod nodeSelector applied to every worker DaemonSet — only nodes whose labels match are eligible to run the worker.',
|
||||
'clusters.operatorImage.title': 'Operator Image',
|
||||
'clusters.operatorImage.tip':
|
||||
'Override for the GPUStack Operator container image. Leave empty to use the server default.',
|
||||
'Override for the MesaStack Operator container image. Leave empty to use the server default.',
|
||||
'clusters.namespace.title': 'Namespace',
|
||||
'clusters.namespace.tip':
|
||||
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
||||
'clusters.gpuInstances.title': 'GPU Instances',
|
||||
'clusters.gpuInstances.tip': 'Enable GPU instance support for this cluster.',
|
||||
'clusters.gpuInstances.staticAddress': 'Static Access Address',
|
||||
'clusters.clusterType.title': 'Cluster Type',
|
||||
'clusters.modelService.title': 'Model Service',
|
||||
'clusters.modelService.tip':
|
||||
'For LLM inference and API serving — e.g. exposing model APIs and token-based services.',
|
||||
'clusters.gpuInstances.title': 'GPU Service',
|
||||
'clusters.gpuInstances.tip':
|
||||
'For on-demand GPU compute — e.g. interactive development, training jobs, or custom environments.',
|
||||
'clusters.gpuInstances.staticAddress': 'GPU Service Static Access Address',
|
||||
'clusters.gpuInstances.staticAddress.tip':
|
||||
'Static address the operator uses to access GPU instances in this cluster (e.g. a LoadBalancer VIP). Optional.'
|
||||
};
|
||||
@@ -264,15 +276,15 @@ export default {
|
||||
// 73. 'clusters.addworker.cacheVolume.holder': 'e.g. /data/cache (path must start with /)',
|
||||
// 74. 'clusters.addworker.message.success_single': '{count} new worker has been added to the cluster.',
|
||||
// 75. 'clusters.addworker.message.success_multiple': '{count} new workers have been added to the cluster.',
|
||||
// 76. 'clusters.create.serverUrl': 'GPUStack Server URL',
|
||||
// 76. 'clusters.create.serverUrl': 'MesaStack Server URL',
|
||||
// 77. 'clusters.create.workerConfig': 'Worker Configuration'
|
||||
// 78. 'clusters.addworker.containerName': 'Worker Container Name',
|
||||
// 79. 'clusters.addworker.containerName.tips':'Specify a name for the worker container.',
|
||||
// 77. 'clusters.addworker.dataVolume': 'GPUStack Data Volume',
|
||||
// 78. 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for GPUStack.',
|
||||
// 77. 'clusters.addworker.dataVolume': 'MesaStack Data Volume',
|
||||
// 78. 'clusters.addworker.dataVolume.tips': 'Specify a data storage path for MesaStack.',
|
||||
// 79. 'clusters.table.ip.internal': 'Internal',
|
||||
// 80. 'clusters.table.ip.external': 'External',
|
||||
// 81. 'clusters.form.serverUrl.tips': 'Specify an externally accessible GPUStack service URL if the worker cannot access GPUStack Server directly.',
|
||||
// 81. 'clusters.form.serverUrl.tips': 'Specify an externally accessible MesaStack service URL if the worker cannot access MesaStack Server directly.',
|
||||
// 82. 'clusters.addworker.externalIP.tips': 'Specify an external IP if the worker is in a VPC or private network.',
|
||||
// 83. 'clusters.form.setDefault': 'Set as Default',
|
||||
// 84. 'clusters.form.setDefault.tips': 'Default for deployment',
|
||||
@@ -288,5 +300,5 @@ export default {
|
||||
// 94. 'clusters.create.steps.configure': 'Configure',
|
||||
// 99. 'clusters.addworker.theadNotes': 'If the <span class="bold-text>/usr/local/PPU_SDK</span> directory does not exist, please create a symbolic link pointing to the T-Head PPU SDK installed path: <span class="bold-text>ln -s /path/to/PPU_SDK /usr/local/PPU_SDK</span>',
|
||||
// 100. 'clusters.addworker.theadNotes-02': 'T-Head PPU uses the Container Device Interface (CDI) for device injection and requires the <span class="bold-text">/var/run/cdi</span> directory to be available for CDI generation.',
|
||||
// 101. 'clusters.addworker.nvidiaNotes': 'The built-in inference backends in GPUStack v2.1 require <span class="bold-text">CUDA 12.6+</span>. Please ensure your NVIDIA driver version is <span class="bold-text">560</span> or newer.'
|
||||
// 101. 'clusters.addworker.nvidiaNotes': 'The built-in inference backends in MesaStack v2.1 require <span class="bold-text">CUDA 12.6+</span>. Please ensure your NVIDIA driver version is <span class="bold-text">560</span> or newer.'
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -46,6 +46,7 @@ export default {
|
||||
'common.button.enabled': '有効',
|
||||
'common.button.disabled': '無効',
|
||||
'common.button.upgrade': 'アップグレード',
|
||||
'common.enterprise.feature': 'Available in MesaStack Enterprise',
|
||||
'common.input.holder': '入力してください',
|
||||
'common.validate.value': '{name} の値は必須です',
|
||||
'common.button.edit': '編集',
|
||||
@@ -55,6 +56,7 @@ export default {
|
||||
'common.button.viewevent': 'イベントを表示',
|
||||
'common.button.recreate': '再作成',
|
||||
'common.table.operation': '操作',
|
||||
'common.table.creator': '作成者',
|
||||
'common.table.createTime': '作成日時',
|
||||
'common.table.updateTime': '更新日時',
|
||||
'common.table.description': '説明',
|
||||
@@ -65,6 +67,7 @@ export default {
|
||||
'common.search.name.placeholder': '名前でフィルタ',
|
||||
'common.search.id.placeholder': 'IDでフィルタ',
|
||||
'common.filter.byId': 'IDでフィルタ',
|
||||
'common.filter.byCreator': '作成者でフィルタ',
|
||||
'common.table.type': 'タイプ',
|
||||
'common.table.default': 'デフォルト値',
|
||||
'common.copy.success': 'コピー成功!',
|
||||
@@ -167,6 +170,7 @@ export default {
|
||||
'common.time.hour': '時間',
|
||||
'common.time.minute': '分',
|
||||
'common.issue.report': '問題を報告',
|
||||
'common.github.star.tooltip': 'GitHub でスターをつける',
|
||||
'common.social.discord': 'Discordに参加',
|
||||
'common.table.mark': 'コメント',
|
||||
'common.table.rollback.mark': 'ロールバックコメント',
|
||||
@@ -208,7 +212,7 @@ export default {
|
||||
'common.form.password': 'パスワード',
|
||||
'common.form.username': 'ユーザー名',
|
||||
'common.login.rember': 'ログイン状態を保持',
|
||||
'settings.company': 'GPUStack.ai',
|
||||
'settings.company': 'MesaStack',
|
||||
'common.button.help': 'ヘルプ',
|
||||
'common.button.feedback': 'フィードバック',
|
||||
'common.button.docs': 'ドキュメント',
|
||||
@@ -257,6 +261,10 @@ export default {
|
||||
'common.login.auth': 'Authenticating...',
|
||||
'common.login.auth.failed': 'Authentication failed',
|
||||
'common.login.password': 'Log in with Password',
|
||||
'common.login.username.holder': 'Please enter username',
|
||||
'common.login.password.holder': 'Please enter password',
|
||||
'common.login.newpassword.holder': 'Please enter new password',
|
||||
'common.login.confirm.holder': 'Please enter password again',
|
||||
'common.external.login': 'Log in with {type}',
|
||||
'common.sso.noConfig':
|
||||
'Single sign-on is not enabled on this system. Please contact your administrator.',
|
||||
@@ -285,7 +293,9 @@ export default {
|
||||
'common.image.limit.height': 'Image height must be {height}.',
|
||||
'common.remaining': '残り {count}',
|
||||
'common.max': '最大 {count}',
|
||||
'common.validate.group': 'Please complete the {group} configuration'
|
||||
'common.max.count': '{label} 数',
|
||||
'common.validate.group': 'Please complete the {group} configuration',
|
||||
'common.preferences': 'Preferences'
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
|
||||
@@ -13,7 +13,11 @@ export default {
|
||||
'gpuservice.template.command.placeholder':
|
||||
'引数はスペースで区切り、スペースを含む引数は引用符で囲んでください。例:/bin/bash -c "echo hello world"',
|
||||
'gpuservice.template.mountPath': 'マウントパス',
|
||||
'gpuservice.template.mountPath.tips':
|
||||
'このテンプレートからインスタンスを作成する際に、ストレージボリュームがデフォルトでマウントされるパスです。インスタンスの実行中に保持する必要があるデータの永続化に使用できます。',
|
||||
'gpuservice.template.containerDisk': 'コンテナディスク (GB)',
|
||||
'gpuservice.template.containerDisk.tips':
|
||||
'コンテナシステムディスクのサイズです。',
|
||||
'gpuservice.template.memory': 'メモリ (GB)',
|
||||
'gpuservice.instance.containerDisk.remaining':
|
||||
'コンテナディスク (最大 {count} GB)',
|
||||
@@ -96,12 +100,18 @@ export default {
|
||||
'gpuservice.instance.templates': 'インスタンステンプレート',
|
||||
'gpuservice.instance.section.storage': 'ストレージボリューム',
|
||||
'gpuservice.instance.type.required': 'インスタンスタイプを選択してください',
|
||||
'gpuservice.instance.type.noAvailable':
|
||||
'利用可能なインスタンスタイプがありません',
|
||||
'gpuservice.instance.gpuCount': 'GPU 数',
|
||||
'gpuservice.instance.gpuCount.required': 'GPU 数を入力してください',
|
||||
'gpuservice.instance.gpuCount.max':
|
||||
'最大 {count} 枚の GPU カードを選択してください',
|
||||
'gpuservice.instance.gpuCount.min':
|
||||
'少なくとも {count} 枚の GPU カードを選択してください',
|
||||
'gpuservice.instance.cpuCount.max':
|
||||
'最大 {count} 個の CPU コアを選択してください',
|
||||
'gpuservice.instance.cpuCount.min':
|
||||
'少なくとも {count} 個の CPU コアを選択してください',
|
||||
'gpuservice.instance.gpuCount.noAvailable':
|
||||
'利用可能な GPU リソースがありません。別のインスタンスタイプを選択してください。',
|
||||
'gpuservice.instance.gpuCount.zero': 'CPU のみを使用し、環境準備用です。',
|
||||
@@ -109,7 +119,13 @@ export default {
|
||||
'gpuservice.instance.sliced': '分割',
|
||||
'gpuservice.instance.memory': 'VRAM',
|
||||
'gpuservice.instance.ram': 'RAM',
|
||||
'gpuservice.instance.os': 'OS',
|
||||
'gpuservice.instance.arch': 'アーキテクチャ',
|
||||
'gpuservice.instance.disk': 'ディスク',
|
||||
'gpuservice.table.count': '数量',
|
||||
'gpuservice.instance.disk.system': 'システムディスク',
|
||||
'gpuservice.instance.disk.ephemeral': '一時ストレージ',
|
||||
'gpuservice.instance.disk.persistent': '永続ストレージ',
|
||||
'gpuservice.instance.search.type.placeholder': '名前で検索',
|
||||
'gpuservice.instance.search.template.placeholder':
|
||||
'テンプレート名、イメージまたはマウントパスで検索',
|
||||
@@ -143,7 +159,7 @@ export default {
|
||||
'gpuservice.storage.temporary': '一時',
|
||||
'gpuservice.storage.persistentVolume': '永続',
|
||||
'gpuservice.storage.persistentVolume.required':
|
||||
'永続ボリュームを選択してください',
|
||||
'ストレージを選択してください',
|
||||
'gpuservice.storage.persistentVolume.capacity': '容量 (GB)',
|
||||
'gpuservice.storage.persistentVolume.capacity.required':
|
||||
'容量を入力してください',
|
||||
@@ -157,5 +173,10 @@ export default {
|
||||
'gpuservice.storage.temporary.tips':
|
||||
'Data is cleared when the instance stops.',
|
||||
'gpuservice.storage.persistentVolume.tips':
|
||||
'Data persists across restarts and is deleted only when the instance is terminated. Cannot be shared with other instances.'
|
||||
'Data persists across instance restarts. Persistent volumes remain intact after instance termination and can be shared by multiple instances.',
|
||||
'gpuservice.form.storage.select': 'ストレージを選択',
|
||||
'gpuservice.creator': '作成者',
|
||||
'gpuservice.owner.global': 'グローバル',
|
||||
'gpuservice.template.group.yours': '自分のテンプレート',
|
||||
'gpuservice.template.group.global': 'グローバルテンプレート'
|
||||
};
|
||||
|
||||
@@ -23,9 +23,13 @@ export default {
|
||||
'menu.resources': 'リソース',
|
||||
'menu.apikeys': 'APIキー',
|
||||
'menu.users': 'ユーザー',
|
||||
'menu.profile': 'プロフィール',
|
||||
'menu.profile': 'Preferences',
|
||||
'menu.login': 'ログイン',
|
||||
'menu.usage': '使用状況',
|
||||
'menu.usage.usage': '使用状況',
|
||||
'menu.billingAndUsage': '使用状況と請求',
|
||||
'menu.billingAndUsage.usage': '使用状況',
|
||||
'menu.billingAndUsage.billing': '請求',
|
||||
'menu.404': '404',
|
||||
'menu.settings': 'Settings',
|
||||
'menu.resources.workers': 'Workers',
|
||||
@@ -34,12 +38,12 @@ export default {
|
||||
'menu.accessControl': 'Access Control',
|
||||
'menu.accessControl.apikeys': 'API Keys',
|
||||
'menu.accessControl.users': 'Users',
|
||||
'menu.clusterManagement': 'Cluster Management',
|
||||
'menu.clusterManagement.clusters': 'Clusters',
|
||||
'menu.clusterManagement.credentials': 'Cloud Credentials',
|
||||
'menu.accessControl.organizations': 'Organizations',
|
||||
'menu.resources.clusters': 'Clusters',
|
||||
'menu.resources.credentials': 'Cloud Credentials',
|
||||
'menu.models.userModels': 'My Models',
|
||||
'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
||||
'menu.clusterManagement.clusterCreate': 'Create Cluster',
|
||||
'menu.resources.clusterDetail': 'Cluster Detail',
|
||||
'menu.resources.clusterCreate': 'Create Cluster',
|
||||
'menu.models.backendsList': 'Inference Backends',
|
||||
'menu.gpuService': 'GPU Service',
|
||||
'menu.gpuService.instances': 'GPU Instances',
|
||||
|
||||
@@ -14,7 +14,7 @@ export default {
|
||||
'models.form.env': '環境変数',
|
||||
'models.form.configurations': '設定',
|
||||
'models.form.s3address': 'S3アドレス',
|
||||
'models.form.partialoffload.tips': `When CPU offloading is enabled, GPUStack will allocate CPU memory if GPU resources are insufficient. You must correctly configure the inference backend to use hybrid CPU+GPU or full CPU inference.`,
|
||||
'models.form.partialoffload.tips': `When CPU offloading is enabled, MesaStack will allocate CPU memory if GPU resources are insufficient. You must correctly configure the inference backend to use hybrid CPU+GPU or full CPU inference.`,
|
||||
'models.form.distribution.tips':
|
||||
'ワーカーのリソースが不足している場合、モデルの一部のレイヤーを単一または複数のリモートワーカーにオフロードすることができます。',
|
||||
'models.openinplayground': 'プレイグラウンドで開く',
|
||||
@@ -25,7 +25,7 @@ export default {
|
||||
'model.deploy.sort': '並び替え',
|
||||
'model.deploy.search.placeholder': '<kbd>/</kbd>を入力してモデルを検索',
|
||||
'model.form.ollamatips':
|
||||
'ヒント: 以下はGPUStackで事前設定されたOllamaモデルです。希望するモデルを選択するか、右側の【{name}】入力ボックスにデプロイしたいモデルを直接入力してください。',
|
||||
'ヒント: 以下はMesaStackで事前設定されたOllamaモデルです。希望するモデルを選択するか、右側の【{name}】入力ボックスにデプロイしたいモデルを直接入力してください。',
|
||||
'models.sort.name': '名前',
|
||||
'models.sort.size': 'サイズ',
|
||||
'models.sort.likes': 'いいね',
|
||||
@@ -88,7 +88,7 @@ export default {
|
||||
'models.form.filePath': 'モデルパス',
|
||||
'models.form.backendVersion': 'バックエンドバージョン',
|
||||
'models.form.backendVersion.tips':
|
||||
'希望する{backend}{version}バージョンを使用するには、システムがオンライン環境で対応するバージョンをインストールする仮想環境を自動的に作成します。GPUStackのアップグレード後もバックエンドバージョンは固定されます。{link}',
|
||||
'希望する{backend}{version}バージョンを使用するには、システムがオンライン環境で対応するバージョンをインストールする仮想環境を自動的に作成します。MesaStackのアップグレード後もバックエンドバージョンは固定されます。{link}',
|
||||
'models.form.gpuselector': 'GPUセレクター',
|
||||
'models.form.backend.llamabox':
|
||||
'GGUF形式のモデル用(Linux、macOS、Windowsをサポート)。',
|
||||
@@ -277,7 +277,7 @@ export default {
|
||||
'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`,
|
||||
'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.modelfile.notfound': `The model file path you specified does not exist on the MesaStack server. It's recommended to place the model file at the same path on both the MesaStack server and MesaStack workers. This helps MesaStack make better decisions.`,
|
||||
'models.form.readyWorkers': 'workers ready',
|
||||
'models.form.maxContextLength': 'Maximum Context Length',
|
||||
'models.form.backend.helperText':
|
||||
@@ -291,7 +291,7 @@ export default {
|
||||
'models.instance.startHistory': 'Run History',
|
||||
'models.instance.startHistory.tips':
|
||||
'Shows logs from the run before the last error-triggered restart.',
|
||||
'models.form.lora.label': 'LoRA Adapter',
|
||||
'models.form.lora.label': 'LoRA Adapters',
|
||||
'models.form.lora.add': 'Add LoRA Adapter',
|
||||
'models.form.lora.select': 'Select LoRA',
|
||||
'models.form.lora.name': 'LoRA name',
|
||||
@@ -381,7 +381,7 @@ export default {
|
||||
// 62. 'models.form.backend_parameters.vllm.tips': 'For more details about {backend} parameters, see <a href={link} target="_blank">here</a>.',
|
||||
// 63. 'models.button.accessSettings.tips': 'Changes to access settings take effect after one minute.',
|
||||
// 64. 'models.table.userSelection.tips': 'Admin users can access all models by default.',
|
||||
// 65. 'models.form.partialoffload.tips': `When CPU offloading is enabled, GPUStack will allocate CPU memory if GPU resources are insufficient. You must correctly configure the inference backend to use hybrid CPU+GPU or full CPU inference.`,
|
||||
// 65. 'models.form.partialoffload.tips': `When CPU offloading is enabled, MesaStack will allocate CPU memory if GPU resources are insufficient. You must correctly configure the inference backend to use hybrid CPU+GPU or full CPU inference.`,
|
||||
// 66. 'models.form.backend.warning': 'The selected backend does not support GGUF models. Please add a backend with GGUF support in the Inference Backend.',
|
||||
// 67. 'models.form.backend.warning.gguf': 'Please ensure that the selected custom backend supports GGUF models.',,
|
||||
// 68. 'models.form.backendVersion.deprecated': 'Deprecated',
|
||||
@@ -390,7 +390,7 @@ export default {
|
||||
// 71.'models.accessSettings.allowedUsers.tips': 'Only designated users can access the model.',
|
||||
// 72. 'models.form.backendVersions.tips': `To use more versions, go to the {link} page and edit the backend to add versions.`,
|
||||
// 73. 'models.catalog.nogpus.tips': 'No compatible GPUs are available in the selected cluster for this model.',
|
||||
// 74. '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.`,
|
||||
// 74. 'models.form.modelfile.notfound': `The model file path you specified does not exist on the MesaStack server. It's recommended to place the model file at the same path on both the MesaStack server and MesaStack workers. This helps MesaStack make better decisions.`,
|
||||
// 75. 'models.form.readyWorkers': 'workers ready',
|
||||
// 76. 'models.form.maxContextLength': 'Maximum Context Length',
|
||||
// 77. 'models.form.backend.helperText': 'Not enabled yet. Will be enabled after deployment. ',
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export default {
|
||||
'organizations.upsell.title': 'Organizations are an Enterprise feature',
|
||||
'organizations.upsell.subtitle':
|
||||
'Multi-tenancy lets you isolate users, resources, and quotas across teams. Upgrade to MesaStack Enterprise to manage organizations.',
|
||||
'organizations.upsell.featuresTitle': 'What you get in Enterprise',
|
||||
'organizations.upsell.feature.orgs':
|
||||
'Create organizations to group users and isolate workloads',
|
||||
'organizations.upsell.feature.members':
|
||||
'Manage members and roles per organization',
|
||||
'organizations.upsell.feature.quotas':
|
||||
'Set resource and token quotas per organization',
|
||||
'organizations.upsell.feature.isolation':
|
||||
'Scope API keys, model deployments, and resources per organization',
|
||||
'organizations.upsell.cta': 'Learn about Enterprise'
|
||||
};
|
||||
@@ -52,7 +52,7 @@ export default {
|
||||
'MacOSまたはWindowsはサポートされていません。',
|
||||
'resources.worker.current.version': '現在のバージョンは {version} です。',
|
||||
'resources.worker.driver.install':
|
||||
'<a href="https://docs.gpustack.ai/latest/installation/installation-requirements/" target="_blank">必要なドライバとライブラリ</a> をGPUStackのインストール前にインストールしてください。',
|
||||
'<a href="https://docs.gpustack.ai/latest/installation/installation-requirements/" target="_blank">必要なドライバとライブラリ</a> をMesaStackのインストール前にインストールしてください。',
|
||||
'resources.worker.select.command':
|
||||
'ラベルを選択してコマンドを生成し、コピーを使用してコマンドをコピーします。',
|
||||
'resources.worker.script.install': 'スクリプトインストール',
|
||||
@@ -89,7 +89,7 @@ export default {
|
||||
'Paste the <span class="bold-text">Token</span>.',
|
||||
'resources.register.worker.step7':
|
||||
'Click <span class="bold-text">Restart</span> to apply the settings.',
|
||||
'resources.register.install.title': 'Install GPUStack on {os}',
|
||||
'resources.register.install.title': 'Install MesaStack on {os}',
|
||||
'resources.register.download':
|
||||
'Download and install the <a href={url} target="_blank">installer</a>. Only supported: {versions}.',
|
||||
'resource.register.maos.support': 'Apple Silicon (M series), macOS 14+',
|
||||
@@ -112,7 +112,7 @@ export default {
|
||||
'No available clusters. Please create a cluster before adding a node.',
|
||||
'resources.metrics.details': 'Monitoring',
|
||||
'resoureces.worker.upgrade.tips':
|
||||
'Please upgrade to match the GPUStack Server version.',
|
||||
'Please upgrade to match the MesaStack Server version.',
|
||||
'resources.worker.version': 'Worker Version: {version}',
|
||||
'resources.server.version': 'Server Version: {version}',
|
||||
'resources.worker.currentVersion': 'Current Version: {version}',
|
||||
@@ -128,7 +128,7 @@ export default {
|
||||
// 5. 'resources.register.worker.step5': 'Enter the <span class="bold-text">Server URL</span>: {url}.',
|
||||
// 6. 'resources.register.worker.step6': 'Paste the <span class="bold-text">Token</span>.',
|
||||
// 7. 'resources.register.worker.step7': 'Click <span class="bold-text">Restart</span> to apply the settings.',
|
||||
// 8. 'resources.register.install.title': 'Install GPUStack on {os}',
|
||||
// 8. 'resources.register.install.title': 'Install MesaStack on {os}',
|
||||
// 9. 'resources.register.download':'Download and install the <a>installer</a>. Only supported: {versions}.',
|
||||
// 10. 'resource.register.maos.support': 'Apple Silicon (M series), macOS 14+',
|
||||
// 11. 'resource.register.windows.support': 'win 10, win 11',
|
||||
@@ -145,5 +145,5 @@ export default {
|
||||
// 22. 'resources.worker.maintenance.remark.rules': 'Please enter maintenance remarks',
|
||||
// 23. 'resources.worker.maintenance.tips': 'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.',
|
||||
// 24. 'resources.worker.noCluster.tips': 'No available clusters. Please create a cluster before adding a node.'
|
||||
// 25. 'resoureces.worker.upgrade.tips': 'Please upgrade to match the GPUStack Server version.'
|
||||
// 25. 'resoureces.worker.upgrade.tips': 'Please upgrade to match the MesaStack Server version.'
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -17,9 +17,15 @@ export default {
|
||||
'usage.table.user.apiKeysUsed': 'API Keys Used',
|
||||
'usage.table.lastActive': 'Last Active',
|
||||
'usage.filter.granularity': 'Granularity',
|
||||
'usage.filter.granularity.hour': '時間',
|
||||
'usage.filter.granularity.day': 'Day',
|
||||
'usage.filter.granularity.week': 'Week',
|
||||
'usage.filter.granularity.month': 'Month',
|
||||
'usage.tabs.summary': '概要',
|
||||
'usage.tabs.tokens': 'トークン',
|
||||
'usage.tabs.gpuInstances': 'GPU インスタンス',
|
||||
'usage.tabs.storage': 'ストレージ',
|
||||
'usage.tabs.resourceEvents': 'リソースイベント',
|
||||
'usage.tabs.models': 'Models',
|
||||
'usage.tabs.apikeys': 'API Keys',
|
||||
'usage.tabs.users': 'User',
|
||||
@@ -32,5 +38,70 @@ export default {
|
||||
'usage.chart.cached': 'Cached',
|
||||
'usage.chart.uncached': 'Uncached',
|
||||
'usage.chart.inputTokensCached': 'Input Tokens (Cached/Uncached)',
|
||||
'usage.table.inputTokensCached': 'Input Tokens Cached'
|
||||
'usage.table.inputTokensCached': 'Input Tokens Cached',
|
||||
|
||||
// --- Resource usage: shared metrics & units ---
|
||||
'usage.metric.tokens': 'トークン数',
|
||||
'usage.metric.input': '入力',
|
||||
'usage.metric.output': '出力',
|
||||
'usage.metric.gpuHours': 'GPU 時間',
|
||||
'usage.metric.instanceHours': 'インスタンス時間',
|
||||
'usage.metric.gbDays': 'GB·日',
|
||||
'usage.metric.gbHours': 'GB·時間',
|
||||
'usage.metric.activeUsers': 'アクティブユーザー',
|
||||
'usage.metric.activeInstances': 'アクティブインスタンス',
|
||||
'usage.metric.activeStorage': 'アクティブストレージ',
|
||||
'usage.metric.activeVolumes': 'アクティブボリューム',
|
||||
'usage.metric.storageTypes': 'ストレージタイプ',
|
||||
'usage.metric.gpuHours.tip':
|
||||
'インスタンスの稼働時間を GPU 数で重み付けした値:N 個の GPU を使用するインスタンスが H 時間稼働すると N × H GPU 時間としてカウントされます。すべてのインスタンスが単一の GPU を使用する場合はインスタンス時間と等しくなります。',
|
||||
'usage.metric.instanceHours.tip':
|
||||
'GPU の数に関係なく、すべてのインスタンスの稼働時間を合計した値。1 つのインスタンスが 2 時間稼働 = 2 インスタンス時間。',
|
||||
'usage.metric.gbDays.tip':
|
||||
'ストレージ容量を時間で積分した値(GB × 日):10 GB を 5 日間保持 = 50 GB·日。(= GB·時間 ÷ 24)',
|
||||
'usage.metric.gbHours.tip':
|
||||
'ストレージ容量を時間で積分した値(GB × 時間):10 GB を 5 時間保持 = 50 GB·時間。',
|
||||
|
||||
// --- Resource usage: common table / labels ---
|
||||
'usage.common.noData': 'データがありません',
|
||||
'usage.common.unknown': '不明',
|
||||
'usage.table.date': '日付',
|
||||
'usage.table.name': '名前',
|
||||
'usage.table.user': 'ユーザー',
|
||||
'usage.table.users': 'ユーザー',
|
||||
'usage.table.type': 'タイプ',
|
||||
'usage.table.instance': 'インスタンス',
|
||||
'usage.table.instanceType': 'インスタンスタイプ',
|
||||
'usage.table.instanceTypes': 'インスタンスタイプ',
|
||||
'usage.table.instances': 'インスタンス',
|
||||
'usage.table.capacity': '容量',
|
||||
'usage.export.tableNamed': 'テーブルデータをエクスポート — {name}',
|
||||
|
||||
// --- Summary tab ---
|
||||
'usage.summary.compute': 'コンピュート',
|
||||
'usage.summary.tokensOverTime': 'トークン数の推移',
|
||||
'usage.summary.gpuHoursOverTime': 'GPU 時間の推移',
|
||||
'usage.summary.gbDaysOverTime': 'GB·日の推移',
|
||||
|
||||
// --- GPU Instances / Storage filters ---
|
||||
'usage.filter.instance': 'インスタンスで絞り込み',
|
||||
'usage.filter.storage': 'ストレージで絞り込み',
|
||||
|
||||
// --- Resource events ---
|
||||
'usage.events.resourceType': 'リソースタイプ',
|
||||
'usage.events.eventType': 'イベントタイプ',
|
||||
'usage.events.resourceName': '名前で絞り込み',
|
||||
'usage.events.col.time': '時刻',
|
||||
'usage.events.col.resource': 'リソース',
|
||||
'usage.events.col.event': 'イベント',
|
||||
'usage.events.col.message': 'メッセージ',
|
||||
'usage.events.resource.gpuInstance': 'GPU インスタンス',
|
||||
'usage.events.resource.cpuInstance': 'CPU インスタンス',
|
||||
'usage.events.type.created': '作成済み',
|
||||
'usage.events.type.deleted': '削除済み',
|
||||
'usage.events.type.started': '開始',
|
||||
'usage.events.type.stopped': '停止',
|
||||
'usage.events.type.updated': '更新済み',
|
||||
'usage.events.type.attached': 'アタッチ済み',
|
||||
'usage.events.type.detached': 'デタッチ済み'
|
||||
};
|
||||
|
||||
@@ -33,14 +33,14 @@ export default {
|
||||
'users.password.confirm.empty': '新しいパスワードを確認してください。',
|
||||
'users.password.confirm.error': '入力された2つのパスワードが一致しません。',
|
||||
'users.login.title': 'ログイン',
|
||||
'users.version.islatest': 'GPUStack {version} は最新バージョンです',
|
||||
'users.version.update': 'GPUStack {version} が利用可能です',
|
||||
'users.version.islatest': 'MesaStack {version} は最新バージョンです',
|
||||
'users.version.update': 'MesaStack {version} が利用可能です',
|
||||
'users.settings.title': 'User Settings',
|
||||
'users.status.activate': 'Activate Account',
|
||||
'users.status.deactivate': 'Deactivate Account',
|
||||
'users.status.inactiveAccount': 'Inactive Account',
|
||||
'users.login.getInitialPassword':
|
||||
'Run the following command on your GPUStack Server to retrieve the initial admin password.'
|
||||
'Run the following command on your MesaStack Server to retrieve the initial admin password.'
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
@@ -48,5 +48,5 @@ export default {
|
||||
// 2. 'users.status.activate': 'Activate Account',
|
||||
// 3. 'users.status.deactivate': 'Deactivate Account',
|
||||
// 4. 'users.status.inactiveAccount': 'Inactive Account',
|
||||
// 5. 'users.login.getInitialPassword': 'Run the following command on your GPUStack Server to retrieve the initial admin password.'
|
||||
// 5. 'users.login.getInitialPassword': 'Run the following command on your MesaStack Server to retrieve the initial admin password.'
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -4,7 +4,7 @@ export default {
|
||||
'vendor.hygon': 'Hygon',
|
||||
'vendor.moorthreads': 'Moore Threads',
|
||||
'vendor.iluvatar': 'Iluvatar',
|
||||
'vendor.metax': 'Metax',
|
||||
'vendor.metax': 'MetaX',
|
||||
'vendor.cambricon': 'Cambricon',
|
||||
'vendor.thead': 'T-Head PPU'
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export default {
|
||||
'ai.provider.ollama': 'Ollama',
|
||||
'ai.provider.openai': 'OpenAI',
|
||||
'ai.provider.openrouter': 'OpenRouter',
|
||||
'ai.provider.qwen': 'Qwen',
|
||||
'ai.provider.qwen': 'Alibaba Cloud Model Studio',
|
||||
'ai.provider.spark': 'Spark',
|
||||
'ai.provider.stepfun': 'StepFun',
|
||||
'ai.provider.together-ai': 'TogetherAI',
|
||||
|
||||
@@ -23,7 +23,9 @@ export default {
|
||||
'apikeys.accessScope.inference': 'Inference APIs',
|
||||
'apikeys.access.permissions': 'Access Permissions',
|
||||
'apikeys.type.auto': 'Auto-generated',
|
||||
'apikeys.type.custom': 'Custom'
|
||||
'apikeys.type.custom': 'Custom',
|
||||
'apikeys.button.ipConfig': 'IP Access Control',
|
||||
'quotaLimits.button.title': 'Quota Limit'
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
|
||||
@@ -23,11 +23,12 @@ export default {
|
||||
'backend.form.defaultExecuteCommand': 'Команда выполнения по умолчанию',
|
||||
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}', '{{'port'}}', '{{'worker_ip'}}' и '{{'model_name'}}' заполняются реальными значениями во время запуска`,
|
||||
'backend.form.defaultBackendParameters': 'Параметры бэкенда по умолчанию',
|
||||
'backend.form.parameterFormat': 'Формат параметров',
|
||||
'backend.form.parameterFormat.default': 'По умолчанию бэкенда',
|
||||
'backend.form.parameterFormat.space': 'Пробел (--key value)',
|
||||
'backend.form.parameterFormat.equal': 'Знак равенства (--key=value)',
|
||||
'backend.form.commonParameters': 'Часто используемые параметры',
|
||||
'backend.form.flagFormat': 'Формат флага',
|
||||
'backend.form.flagFormat.tips':
|
||||
'Формат соединения опции и её значения. Оставьте пустым, чтобы сохранить каждый параметр в исходном виде, без приведения к единому формату.',
|
||||
'backend.form.flagFormat.space': 'Разделение пробелом (--key value)',
|
||||
'backend.form.flagFormat.equal': 'Знак равенства (--key=value)',
|
||||
'backend.form.commonParameters': 'Общие параметры бэкенда',
|
||||
'backend.form.commonParameters.tips':
|
||||
'Отображаются как подсказки в поле параметров бэкенда при развёртывании.',
|
||||
'backend.form.versionConfig': 'Конфигурация версий',
|
||||
|
||||
@@ -37,8 +37,8 @@ export default {
|
||||
'Stress test for long-context handling. Evaluates KV cache behavior, memory usage, and backend stability.',
|
||||
'benchmark.form.profile.heavy.tips':
|
||||
'Decode-heavy generation benchmark. Measures sustained decoding speed and output token throughput.',
|
||||
'benchmark.table.filter.bygpu': 'Filter by GPU',
|
||||
'benchmark.table.filter.bymodel': 'Filter by Model',
|
||||
'benchmark.table.filter.bygpu': 'Поиск GPU',
|
||||
'benchmark.table.filter.bymodel': 'Поиск модели',
|
||||
'benchmark.table.filter.bydataset': 'Filter by Dataset',
|
||||
'benchmark.table.filter.byProfile': 'Filter by Profile',
|
||||
'benchmark.table.avg': 'Avg',
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export default {
|
||||
'billing.upsell.title': 'Billing is an Enterprise feature',
|
||||
'billing.upsell.subtitle':
|
||||
'Track spend, generate invoices, and enforce budgets across teams. Upgrade to MesaStack Enterprise to manage billing.',
|
||||
'billing.upsell.featuresTitle': 'What you get in Enterprise',
|
||||
'billing.upsell.feature.usage':
|
||||
'See cost breakdowns by organization, user, and model',
|
||||
'billing.upsell.feature.invoices':
|
||||
'Generate invoices and export billing reports',
|
||||
'billing.upsell.feature.budgets':
|
||||
'Set budgets and spending limits with alerts',
|
||||
'billing.upsell.feature.chargeback':
|
||||
'Attribute and charge back usage to teams and projects',
|
||||
'billing.upsell.cta': 'Learn about Enterprise'
|
||||
};
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
'clusters.workerpool.batchSize.desc':
|
||||
'Количество воркеров, создаваемых одновременно в пуле воркеров',
|
||||
'clusters.create.addworker.tips':
|
||||
'Пожалуйста, убедитесь, что выполнены <a href={link} target="_blank">предварительные условия</a> для {label} перед выполнением следующей команды.',
|
||||
'Пожалуйста, убедитесь, что выполнены <a href={link} target="_blank">предварительные условия</a> перед выполнением следующей команды.',
|
||||
'clusters.create.addCommand.tips':
|
||||
'На воркере, который необходимо добавить, выполните следующую команду, чтобы присоединить его к кластеру.',
|
||||
'clusters.create.addCommand.k8s.tips':
|
||||
@@ -70,8 +70,13 @@ export default {
|
||||
'Для <span class="bold-text">не-Docker</span> кластеров, пожалуйста, регистрируйте кластеры или управляйте пулами воркеров на странице Кластеры.',
|
||||
'clusters.addworker.selectGPU': 'Выбрать производителя GPU',
|
||||
'clusters.addworker.selectGPU.multiTag': 'Multi-select',
|
||||
'clusters.addworker.selectGPU.subtitle':
|
||||
'Вы можете выбрать несколько производителей GPU или не выбирать для кластера только с CPU',
|
||||
'clusters.addworker.checkEnv': 'Проверить окружение',
|
||||
'clusters.addworker.checkEnv.cpuOnlyTips':
|
||||
'Используйте следующую команду, чтобы убедиться, что в кластере Kubernetes есть хотя бы один готовый узел. Вы регистрируете кластер только с CPU.',
|
||||
'clusters.addworker.specifyArgs': 'Указать аргументы',
|
||||
'clusters.addworker.dtkVersion': 'Версия DTK',
|
||||
'clusters.addworker.runCommand': 'Выполнить команду',
|
||||
'clusters.addworker.specifyWorkerIP': 'Указать IP воркера',
|
||||
'clusters.addworker.detectWorkerIP': 'Автоматически определить IP воркера',
|
||||
@@ -108,18 +113,20 @@ export default {
|
||||
'{count} новый воркер был добавлен в кластер.',
|
||||
'clusters.addworker.message.success_multiple':
|
||||
'{count} новых воркеров были добавлены в кластер.',
|
||||
'clusters.create.serverUrl': 'URL сервера GPUStack',
|
||||
'clusters.create.serverUrl': 'URL сервера MesaStack',
|
||||
'clusters.create.workerConfig': 'Конфигурация воркера',
|
||||
'clusters.edit.k8sOptions.changed.tip':
|
||||
'Вы изменили параметры Kubernetes. Чтобы изменения вступили в силу, повторно выполните команду регистрации в целевом кластере.',
|
||||
'clusters.addworker.containerName': 'Имя контейнера воркера',
|
||||
'clusters.addworker.containerName.tips':
|
||||
'Укажите имя для контейнера воркера.',
|
||||
'clusters.addworker.dataVolume': 'Том данных GPUStack',
|
||||
'clusters.addworker.dataVolume': 'Том данных MesaStack',
|
||||
'clusters.addworker.dataVolume.tips':
|
||||
'Укажите путь для хранения данных GPUStack.',
|
||||
'Укажите путь для хранения данных MesaStack.',
|
||||
'clusters.table.ip.internal': 'Внутренний',
|
||||
'clusters.table.ip.external': 'Внешний',
|
||||
'clusters.form.serverUrl.tips':
|
||||
'Если рабочий узел не может напрямую получить доступ к GPUStack Server, укажите внешний URL службы GPUStack Server.',
|
||||
'Если рабочий узел не может напрямую получить доступ к MesaStack Server, укажите внешний URL службы MesaStack Server.',
|
||||
'clusters.form.setDefault': 'Установить по умолчанию',
|
||||
'clusters.form.setDefault.tips':
|
||||
'Использовать по умолчанию для развертывания.',
|
||||
@@ -138,7 +145,7 @@ export default {
|
||||
'clusters.addworker.theadNotes-02':
|
||||
'T-Head PPU uses the Container Device Interface (CDI) for device injection and requires the <span class="bold-text">/var/run/cdi</span> directory to be available for CDI generation.',
|
||||
'clusters.addworker.nvidiaNotes':
|
||||
'The built-in inference backends in GPUStack v2.1 require <span class="bold-text">CUDA 12.6+</span>. Please ensure your NVIDIA driver version is <span class="bold-text">560</span> or newer.',
|
||||
'The built-in inference backends in MesaStack require <span class="bold-text">CUDA 12.8+</span>. Please ensure your NVIDIA driver version is <span class="bold-text">570</span> or newer.',
|
||||
'clusters.volume.title': 'Volume Mounts',
|
||||
'clusters.volume.name': 'Volume Name',
|
||||
'clusters.volume.mountPath': 'Container Path',
|
||||
@@ -165,8 +172,8 @@ export default {
|
||||
'clusters.volume.add': 'Add Volume Mount',
|
||||
'clusters.systemDefaultContainerRegistry.title': 'Default Container Registry',
|
||||
'clusters.systemDefaultContainerRegistry.tip':
|
||||
'Default registry used to resolve GPUStack images for this cluster. Falls back to the server default when unset.',
|
||||
'clusters.k8sOptions.title': 'K8s Deployment Options',
|
||||
'Default registry used to resolve MesaStack images for this cluster. Falls back to the server default when unset.',
|
||||
'clusters.k8sOptions.title': 'Kubernetes Deployment Options',
|
||||
'clusters.imageCredentials.title': 'Image Credentials',
|
||||
'clusters.imageCredentials.add': 'Add Credential',
|
||||
'clusters.imageCredentials.registry': 'Registry',
|
||||
@@ -177,13 +184,18 @@ export default {
|
||||
'Pod nodeSelector applied to every worker DaemonSet — only nodes whose labels match are eligible to run the worker.',
|
||||
'clusters.operatorImage.title': 'Operator Image',
|
||||
'clusters.operatorImage.tip':
|
||||
'Override for the GPUStack Operator container image. Leave empty to use the server default.',
|
||||
'Override for the MesaStack Operator container image. Leave empty to use the server default.',
|
||||
'clusters.namespace.title': 'Namespace',
|
||||
'clusters.namespace.tip':
|
||||
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
||||
'clusters.gpuInstances.title': 'GPU Instances',
|
||||
'clusters.gpuInstances.tip': 'Enable GPU instance support for this cluster.',
|
||||
'clusters.gpuInstances.staticAddress': 'Static Access Address',
|
||||
'clusters.clusterType.title': 'Cluster Type',
|
||||
'clusters.modelService.title': 'Model Service',
|
||||
'clusters.modelService.tip':
|
||||
'For LLM inference and API serving — e.g. exposing model APIs and token-based services.',
|
||||
'clusters.gpuInstances.title': 'GPU Service',
|
||||
'clusters.gpuInstances.tip':
|
||||
'For on-demand GPU compute — e.g. interactive development, training jobs, or custom environments.',
|
||||
'clusters.gpuInstances.staticAddress': 'GPU Service Static Access Address',
|
||||
'clusters.gpuInstances.staticAddress.tip':
|
||||
'Static address the operator uses to access GPU instances in this cluster (e.g. a LoadBalancer VIP). Optional.'
|
||||
};
|
||||
@@ -201,5 +213,5 @@ export default {
|
||||
// 10. 'clusters.addworker.theadNotes': 'If the <span class="bold-text>/usr/local/PPU_SDK</span> directory does not exist, please create a symbolic link pointing to the T-Head PPU SDK installed path: <span class="bold-text>ln -s /path/to/PPU_SDK /usr/local/PPU_SDK</span>',
|
||||
// 11. 'clusters.addworker.theadNotes-02': 'T-Head PPU uses the Container Device Interface (CDI) for device injection and requires the <span class="bold-text">/var/run/cdi</span> directory to be available for CDI generation.'
|
||||
// 12. 'clusters.addworker.metaxNotes': `If the <span class="bold-text">/opt/mxdriver</span> or <span class="bold-text">/opt/maca</span> directory does not exist, create a symbolic link to the MetaX driver and SDK installation path: <span class="desc-fill">ln -s /path/to/mxdriver /opt/mxdriver</span><span class="desc-fill">ln -s /path/to/maca /opt/maca</span>.`,
|
||||
// 13. 'clusters.addworker.nvidiaNotes': 'The built-in inference backends in GPUStack v2.1 require <span class="bold-text">CUDA 12.6+</span>. Please ensure your NVIDIA driver version is <span class="bold-text">560</span> or newer.'
|
||||
// 13. 'clusters.addworker.nvidiaNotes': 'The built-in inference backends in MesaStack v2.1 require <span class="bold-text">CUDA 12.6+</span>. Please ensure your NVIDIA driver version is <span class="bold-text">560</span> or newer.'
|
||||
// ================================================================
|
||||
|
||||
@@ -46,6 +46,7 @@ export default {
|
||||
'common.button.enabled': 'Активно',
|
||||
'common.button.disabled': 'Отключено',
|
||||
'common.button.upgrade': 'Обновить',
|
||||
'common.enterprise.feature': 'Available in MesaStack Enterprise',
|
||||
'common.input.holder': 'Введите значение',
|
||||
'common.validate.value': 'Поле {name} обязательно',
|
||||
'common.button.edit': 'Редактировать',
|
||||
@@ -55,6 +56,7 @@ export default {
|
||||
'common.button.viewevent': 'Просмотр событий',
|
||||
'common.button.recreate': 'Пересоздать',
|
||||
'common.table.operation': 'Действия',
|
||||
'common.table.creator': 'Создатель',
|
||||
'common.table.createTime': 'Создано',
|
||||
'common.table.updateTime': 'Обновлено',
|
||||
'common.table.description': 'Описание',
|
||||
@@ -65,6 +67,7 @@ export default {
|
||||
'common.search.name.placeholder': 'Фильтр по названию',
|
||||
'common.search.id.placeholder': 'Фильтр по ID',
|
||||
'common.filter.byId': 'Фильтр по ID',
|
||||
'common.filter.byCreator': 'Фильтр по создателю',
|
||||
'common.table.type': 'Тип',
|
||||
'common.table.default': 'Значение по умолчанию',
|
||||
'common.copy.success': 'Скопировано!',
|
||||
@@ -164,6 +167,7 @@ export default {
|
||||
'common.time.hour': 'Час',
|
||||
'common.time.minute': 'Минута',
|
||||
'common.issue.report': 'Сообщить о проблеме',
|
||||
'common.github.star.tooltip': 'Поставьте нам звезду на GitHub',
|
||||
'common.social.discord': 'Присоединиться к Discord',
|
||||
'common.table.mark': 'Комментарий',
|
||||
'common.table.rollback.mark': 'Комментарий к откату',
|
||||
@@ -206,7 +210,7 @@ export default {
|
||||
'common.form.password': 'Пароль',
|
||||
'common.form.username': 'Имя пользователя',
|
||||
'common.login.rember': 'Запомнить меня',
|
||||
'settings.company': 'GPUStack.ai',
|
||||
'settings.company': 'MesaStack',
|
||||
'common.button.help': 'Помощь',
|
||||
'common.button.feedback': 'Обратная связь',
|
||||
'common.button.docs': 'Документация',
|
||||
@@ -256,11 +260,15 @@ export default {
|
||||
'common.login.auth': 'Аутентификация...',
|
||||
'common.login.auth.failed': 'Ошибка аутентификации',
|
||||
'common.login.password': 'Войти с паролем',
|
||||
'common.login.username.holder': 'Введите имя пользователя',
|
||||
'common.login.password.holder': 'Введите пароль',
|
||||
'common.login.newpassword.holder': 'Введите новый пароль',
|
||||
'common.login.confirm.holder': 'Введите пароль ещё раз',
|
||||
'common.external.login': 'Войти через {type}',
|
||||
'common.sso.noConfig':
|
||||
'Единый вход не настроен в этой системе. Пожалуйста, обратитесь к администратору.',
|
||||
'common.button.edit.item': 'Редактировать {name}',
|
||||
'common.button.copy.item': 'Duplicate {name}',
|
||||
'common.button.copy.item': 'Дублировать {name}',
|
||||
'common.button.terminal': 'Терминал',
|
||||
'common.button.addItem': 'Добавить элемент',
|
||||
'common.help.default': 'По умолчанию: {content}',
|
||||
@@ -284,7 +292,9 @@ export default {
|
||||
'common.image.limit.height': 'Image height must be {height}.',
|
||||
'common.remaining': 'Остаток {count}',
|
||||
'common.max': 'Макс. {count}',
|
||||
'common.validate.group': 'Please complete the {group} configuration'
|
||||
'common.max.count': 'Количество {label}',
|
||||
'common.validate.group': 'Please complete the {group} configuration',
|
||||
'common.preferences': 'Preferences'
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
|
||||
@@ -14,7 +14,11 @@ export default {
|
||||
'gpuservice.template.command.placeholder':
|
||||
'Разделяйте аргументы пробелами; аргументы с пробелами заключайте в кавычки, например: /bin/bash -c "echo hello world"',
|
||||
'gpuservice.template.mountPath': 'Путь монтирования',
|
||||
'gpuservice.template.mountPath.tips':
|
||||
'Путь, по которому том хранилища монтируется по умолчанию при создании экземпляра из этого шаблона. Может использоваться для сохранения данных, которые нужно сохранить во время работы экземпляра.',
|
||||
'gpuservice.template.containerDisk': 'Диск контейнера (GB)',
|
||||
'gpuservice.template.containerDisk.tips':
|
||||
'Размер системного диска контейнера.',
|
||||
'gpuservice.template.memory': 'Память (GB)',
|
||||
'gpuservice.instance.containerDisk.remaining':
|
||||
'Диск контейнера (Макс. {count} GB)',
|
||||
@@ -100,10 +104,13 @@ export default {
|
||||
'gpuservice.instance.templates': 'Шаблоны экземпляров',
|
||||
'gpuservice.instance.section.storage': 'Том хранилища',
|
||||
'gpuservice.instance.type.required': 'Выберите тип экземпляра',
|
||||
'gpuservice.instance.type.noAvailable': 'Нет доступных типов экземпляров',
|
||||
'gpuservice.instance.gpuCount': 'Количество GPU',
|
||||
'gpuservice.instance.gpuCount.required': 'Введите количество GPU',
|
||||
'gpuservice.instance.gpuCount.max': 'Выберите максимум {count} GPU-карт',
|
||||
'gpuservice.instance.gpuCount.min': 'Выберите минимум {count} GPU-карт',
|
||||
'gpuservice.instance.cpuCount.max': 'Выберите максимум {count} ядер CPU',
|
||||
'gpuservice.instance.cpuCount.min': 'Выберите минимум {count} ядер CPU',
|
||||
'gpuservice.instance.gpuCount.noAvailable':
|
||||
'Нет доступных ресурсов GPU, выберите другой тип экземпляра.',
|
||||
'gpuservice.instance.gpuCount.zero': 'Только CPU, для подготовки окружения.',
|
||||
@@ -111,7 +118,13 @@ export default {
|
||||
'gpuservice.instance.sliced': 'Разделено',
|
||||
'gpuservice.instance.memory': 'VRAM',
|
||||
'gpuservice.instance.ram': 'RAM',
|
||||
'gpuservice.instance.os': 'ОС',
|
||||
'gpuservice.instance.arch': 'Архитектура',
|
||||
'gpuservice.instance.disk': 'Диск',
|
||||
'gpuservice.table.count': 'Количество',
|
||||
'gpuservice.instance.disk.system': 'Системный диск',
|
||||
'gpuservice.instance.disk.ephemeral': 'Временное хранилище',
|
||||
'gpuservice.instance.disk.persistent': 'Постоянное хранилище',
|
||||
'gpuservice.instance.search.type.placeholder': 'Поиск по имени',
|
||||
'gpuservice.instance.search.template.placeholder':
|
||||
'Поиск по имени шаблона, образу или пути монтирования',
|
||||
@@ -144,7 +157,7 @@ export default {
|
||||
'gpuservice.storage.persistent': 'Постоянное',
|
||||
'gpuservice.storage.temporary': 'Временное',
|
||||
'gpuservice.storage.persistentVolume': 'Постоянное',
|
||||
'gpuservice.storage.persistentVolume.required': 'Выберите постоянный том',
|
||||
'gpuservice.storage.persistentVolume.required': 'Выберите хранилище',
|
||||
'gpuservice.storage.persistentVolume.capacity': 'Ёмкость (ГБ)',
|
||||
'gpuservice.storage.persistentVolume.capacity.required': 'Введите ёмкость',
|
||||
'gpuservice.storage.persistentVolume.releaseWithInstance':
|
||||
@@ -157,5 +170,10 @@ export default {
|
||||
'gpuservice.storage.temporary.tips':
|
||||
'Data is cleared when the instance stops.',
|
||||
'gpuservice.storage.persistentVolume.tips':
|
||||
'Data persists across restarts and is deleted only when the instance is terminated. Cannot be shared with other instances.'
|
||||
'Data persists across instance restarts. Persistent volumes remain intact after instance termination and can be shared by multiple instances.',
|
||||
'gpuservice.form.storage.select': 'Выберите хранилище',
|
||||
'gpuservice.creator': 'Создатель',
|
||||
'gpuservice.owner.global': 'Глобальный',
|
||||
'gpuservice.template.group.yours': 'Ваши шаблоны',
|
||||
'gpuservice.template.group.global': 'Глобальные шаблоны'
|
||||
};
|
||||
|
||||
@@ -23,9 +23,13 @@ export default {
|
||||
'menu.resources': 'Ресурсы',
|
||||
'menu.apikeys': 'API-ключи',
|
||||
'menu.users': 'Пользователи',
|
||||
'menu.profile': 'Профиль',
|
||||
'menu.profile': 'Preferences',
|
||||
'menu.login': 'Авторизация',
|
||||
'menu.usage': 'Использование',
|
||||
'menu.usage.usage': 'Использование',
|
||||
'menu.billingAndUsage': 'Использование и биллинг',
|
||||
'menu.billingAndUsage.usage': 'Использование',
|
||||
'menu.billingAndUsage.billing': 'Биллинг',
|
||||
'menu.404': 'Ошибка 404',
|
||||
'menu.resources.workers': 'Воркеры',
|
||||
'menu.resources.gpus': 'GPUs',
|
||||
@@ -33,12 +37,12 @@ export default {
|
||||
'menu.accessControl': 'Управление доступом',
|
||||
'menu.accessControl.apikeys': 'API Ключи',
|
||||
'menu.accessControl.users': 'Пользователи',
|
||||
'menu.clusterManagement': 'Управление кластерами',
|
||||
'menu.clusterManagement.clusters': 'Кластеры',
|
||||
'menu.clusterManagement.credentials': 'Облачные аккаунты',
|
||||
'menu.accessControl.organizations': 'Организации',
|
||||
'menu.resources.clusters': 'Кластеры',
|
||||
'menu.resources.credentials': 'Облачные аккаунты',
|
||||
'menu.models.userModels': 'Мои модели',
|
||||
'menu.clusterManagement.clusterDetail': 'Детали кластера',
|
||||
'menu.clusterManagement.clusterCreate': 'Создать кластер',
|
||||
'menu.resources.clusterDetail': 'Детали кластера',
|
||||
'menu.resources.clusterCreate': 'Создать кластер',
|
||||
'menu.models.backendsList': 'Бэкенды запуска',
|
||||
'menu.settings': 'Settings',
|
||||
'menu.gpuService': 'GPU Service',
|
||||
|
||||
@@ -15,7 +15,7 @@ export default {
|
||||
'models.form.configurations': 'Конфигурации',
|
||||
'models.form.s3address': 'S3-адрес',
|
||||
'models.form.partialoffload.tips':
|
||||
'При включении CPU оффлоудинга GPUStack будет выделять оперативную память, если ресурсов GPU недостаточно. Вы должны правильно настроить бэкенд вывода для использования гибридного CPU+GPU или полного CPU вывода.',
|
||||
'При включении CPU оффлоудинга MesaStack будет выделять оперативную память, если ресурсов GPU недостаточно. Вы должны правильно настроить бэкенд вывода для использования гибридного CPU+GPU или полного CPU вывода.',
|
||||
'models.form.distribution.tips':
|
||||
'Позволяет переносить часть слоёв модели на один или несколько удалённых воркеров, когда ресурсов текущего воркера недостаточно.',
|
||||
'models.openinplayground': 'Открыть в Песочнице',
|
||||
@@ -26,7 +26,7 @@ export default {
|
||||
'model.deploy.sort': 'Сортировка',
|
||||
'model.deploy.search.placeholder': 'Введите <kbd>/</kbd> для поиска моделей',
|
||||
'model.form.ollamatips':
|
||||
'Подсказка: ниже представлены предустановленные модели Ollama в GPUStack. Выберите нужную или введите модель для развертывания в поле 【{name}】 справа.',
|
||||
'Подсказка: ниже представлены предустановленные модели Ollama в MesaStack. Выберите нужную или введите модель для развертывания в поле 【{name}】 справа.',
|
||||
'models.sort.name': 'По имени',
|
||||
'models.sort.size': 'По размеру',
|
||||
'models.sort.likes': 'По лайкам',
|
||||
@@ -89,7 +89,7 @@ export default {
|
||||
'models.form.filePath': 'Путь к модели',
|
||||
'models.form.backendVersion': 'Версия бэкенда',
|
||||
'models.form.backendVersion.tips':
|
||||
'Чтобы использовать желаемую версию {backend} {version}, система автоматически создаст виртуальную среду в онлайн-окружении для установки соответствующей версии. После обновления GPUStack версия бэкенда останется зафиксированной. {link}',
|
||||
'Чтобы использовать желаемую версию {backend} {version}, система автоматически создаст виртуальную среду в онлайн-окружении для установки соответствующей версии. После обновления MesaStack версия бэкенда останется зафиксированной. {link}',
|
||||
'models.form.gpuselector': 'Селектор GPU',
|
||||
'models.form.backend.llamabox':
|
||||
'Для моделей формата GGUF. Поддержка Linux, macOS и Windows.',
|
||||
@@ -281,7 +281,7 @@ export default {
|
||||
'models.form.backendVersions.tips': `Чтобы использовать больше версий, перейдите на страницу {link} и отредактируйте бэкенд для добавления версий.`,
|
||||
'models.catalog.nogpus.tips':
|
||||
'В выбранном кластере нет доступных GPU, совместимых с этой моделью.',
|
||||
'models.form.modelfile.notfound': `Указанный путь к файлу модели не существует на сервере GPUStack. Рекомендуется размещать файл модели по одному и тому же пути как на сервере GPUStack, так и на воркерах GPUStack. Это поможет системе принимать лучшие решения по распределению ресурсов.`,
|
||||
'models.form.modelfile.notfound': `Указанный путь к файлу модели не существует на сервере MesaStack. Рекомендуется размещать файл модели по одному и тому же пути как на сервере MesaStack, так и на воркерах MesaStack. Это поможет системе принимать лучшие решения по распределению ресурсов.`,
|
||||
'models.form.readyWorkers': 'воркеров готово',
|
||||
'models.form.maxContextLength': 'Maximum Context Length',
|
||||
'models.form.backend.helperText':
|
||||
@@ -295,7 +295,7 @@ export default {
|
||||
'models.instance.startHistory': 'Run History',
|
||||
'models.instance.startHistory.tips':
|
||||
'Shows logs from the run before the last error-triggered restart.',
|
||||
'models.form.lora.label': 'LoRA Adapter',
|
||||
'models.form.lora.label': 'LoRA Adapters',
|
||||
'models.form.lora.add': 'Add LoRA Adapter',
|
||||
'models.form.lora.select': 'Select LoRA',
|
||||
'models.form.lora.name': 'LoRA name',
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export default {
|
||||
'organizations.upsell.title': 'Organizations are an Enterprise feature',
|
||||
'organizations.upsell.subtitle':
|
||||
'Multi-tenancy lets you isolate users, resources, and quotas across teams. Upgrade to MesaStack Enterprise to manage organizations.',
|
||||
'organizations.upsell.featuresTitle': 'What you get in Enterprise',
|
||||
'organizations.upsell.feature.orgs':
|
||||
'Create organizations to group users and isolate workloads',
|
||||
'organizations.upsell.feature.members':
|
||||
'Manage members and roles per organization',
|
||||
'organizations.upsell.feature.quotas':
|
||||
'Set resource and token quotas per organization',
|
||||
'organizations.upsell.feature.isolation':
|
||||
'Scope API keys, model deployments, and resources per organization',
|
||||
'organizations.upsell.cta': 'Learn about Enterprise'
|
||||
};
|
||||
@@ -51,7 +51,7 @@ export default {
|
||||
'resources.worker.container.supported': 'Только для Linux.',
|
||||
'resources.worker.current.version': 'Текущая версия: {version}',
|
||||
'resources.worker.driver.install':
|
||||
'Установите <a href="https://docs.gpustack.ai/latest/installation/installation-requirements/" target="_blank">необходимые драйверы и библиотеки</a> перед установкой GPUStack.', // Translated
|
||||
'Установите <a href="https://docs.gpustack.ai/latest/installation/installation-requirements/" target="_blank">необходимые драйверы и библиотеки</a> перед установкой MesaStack.', // Translated
|
||||
'resources.worker.select.command':
|
||||
'Выберите метку для генерации команды и скопируйте её.',
|
||||
'resources.worker.script.install': 'Установка скриптом',
|
||||
@@ -87,7 +87,7 @@ export default {
|
||||
'Вставьте <span class="bold-text">Токен</span>.',
|
||||
'resources.register.worker.step7':
|
||||
'Нажмите <span class="bold-text">Перезапуск</span> для применения настроек.',
|
||||
'resources.register.install.title': 'Установка GPUStack на {os}',
|
||||
'resources.register.install.title': 'Установка MesaStack на {os}',
|
||||
'resources.register.download':
|
||||
'Скачайте и установите <a href={url} target="_blank">инсталлятор</a>. Поддерживаемые версии: {versions}.',
|
||||
'resource.register.maos.support': 'Apple Silicon (серия M), macOS 14+',
|
||||
@@ -110,7 +110,7 @@ export default {
|
||||
'No available clusters. Please create a cluster before adding a node.',
|
||||
'resources.metrics.details': 'Monitoring',
|
||||
'resoureces.worker.upgrade.tips':
|
||||
'Please upgrade to match the GPUStack Server version.',
|
||||
'Please upgrade to match the MesaStack Server version.',
|
||||
'resources.worker.version': 'Worker Version: {version}',
|
||||
'resources.server.version': 'Server Version: {version}',
|
||||
'resources.worker.currentVersion': 'Current Version: {version}',
|
||||
@@ -127,5 +127,5 @@ export default {
|
||||
// 7. 'resources.worker.maintenance.tips': 'When maintenance mode is enabled, the node will stop scheduling new model deployment tasks. Running instances will not be affected.',
|
||||
// 8. 'resources.worker.noCluster.tips': 'No available clusters. Please create a cluster before adding a node.',
|
||||
// 9. 'resources.metrics.details': 'Monitoring',
|
||||
// 10. 'resoureces.worker.upgrade.tips': 'Please upgrade to match the GPUStack Server version.'
|
||||
// 10. 'resoureces.worker.upgrade.tips': 'Please upgrade to match the MesaStack Server version.'
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -17,9 +17,15 @@ export default {
|
||||
'usage.table.user.apiKeysUsed': 'API Keys Used',
|
||||
'usage.table.lastActive': 'Last Active',
|
||||
'usage.filter.granularity': 'Granularity',
|
||||
'usage.filter.granularity.hour': 'Час',
|
||||
'usage.filter.granularity.day': 'Day',
|
||||
'usage.filter.granularity.week': 'Week',
|
||||
'usage.filter.granularity.month': 'Month',
|
||||
'usage.tabs.summary': 'Обзор',
|
||||
'usage.tabs.tokens': 'Токены',
|
||||
'usage.tabs.gpuInstances': 'GPU-инстансы',
|
||||
'usage.tabs.storage': 'Хранилище',
|
||||
'usage.tabs.resourceEvents': 'События ресурсов',
|
||||
'usage.tabs.models': 'Models',
|
||||
'usage.tabs.apikeys': 'API Keys',
|
||||
'usage.tabs.users': 'User',
|
||||
@@ -32,5 +38,70 @@ export default {
|
||||
'usage.chart.cached': 'Cached',
|
||||
'usage.chart.uncached': 'Uncached',
|
||||
'usage.chart.inputTokensCached': 'Input Tokens (Cached/Uncached)',
|
||||
'usage.table.inputTokensCached': 'Input Tokens Cached'
|
||||
'usage.table.inputTokensCached': 'Input Tokens Cached',
|
||||
|
||||
// --- Resource usage: shared metrics & units ---
|
||||
'usage.metric.tokens': 'Токены',
|
||||
'usage.metric.input': 'Вход',
|
||||
'usage.metric.output': 'Выход',
|
||||
'usage.metric.gpuHours': 'GPU-часы',
|
||||
'usage.metric.instanceHours': 'Часы инстансов',
|
||||
'usage.metric.gbDays': 'ГБ-дни',
|
||||
'usage.metric.gbHours': 'ГБ-часы',
|
||||
'usage.metric.activeUsers': 'Активные пользователи',
|
||||
'usage.metric.activeInstances': 'Активные инстансы',
|
||||
'usage.metric.activeStorage': 'Активное хранилище',
|
||||
'usage.metric.activeVolumes': 'Активные тома',
|
||||
'usage.metric.storageTypes': 'Типы хранилищ',
|
||||
'usage.metric.gpuHours.tip':
|
||||
'Время работы инстанса, взвешенное по количеству GPU: инстанс с N GPU, работающий H часов, считается как N × H GPU-часов. Равно часам инстансов, когда каждый инстанс использует один GPU.',
|
||||
'usage.metric.instanceHours.tip':
|
||||
'Суммарное время работы всех инстансов независимо от количества используемых GPU. Один инстанс, работающий 2 часа = 2 часа инстанса.',
|
||||
'usage.metric.gbDays.tip':
|
||||
'Ёмкость хранилища, проинтегрированная по времени, в ГБ × дни: 10 ГБ в течение 5 дней = 50 ГБ-дней. (= ГБ-часы ÷ 24)',
|
||||
'usage.metric.gbHours.tip':
|
||||
'Ёмкость хранилища, проинтегрированная по времени, в ГБ × часы: 10 ГБ в течение 5 часов = 50 ГБ-часов.',
|
||||
|
||||
// --- Resource usage: common table / labels ---
|
||||
'usage.common.noData': 'Нет данных',
|
||||
'usage.common.unknown': 'неизвестно',
|
||||
'usage.table.date': 'Дата',
|
||||
'usage.table.name': 'Имя',
|
||||
'usage.table.user': 'Пользователь',
|
||||
'usage.table.users': 'Пользователи',
|
||||
'usage.table.type': 'Тип',
|
||||
'usage.table.instance': 'Инстанс',
|
||||
'usage.table.instanceType': 'Тип инстанса',
|
||||
'usage.table.instanceTypes': 'Типы инстансов',
|
||||
'usage.table.instances': 'Инстансы',
|
||||
'usage.table.capacity': 'Ёмкость',
|
||||
'usage.export.tableNamed': 'Экспорт данных таблицы — {name}',
|
||||
|
||||
// --- Summary tab ---
|
||||
'usage.summary.compute': 'Вычисления',
|
||||
'usage.summary.tokensOverTime': 'Токены по времени',
|
||||
'usage.summary.gpuHoursOverTime': 'GPU-часы по времени',
|
||||
'usage.summary.gbDaysOverTime': 'ГБ-дни по времени',
|
||||
|
||||
// --- GPU Instances / Storage filters ---
|
||||
'usage.filter.instance': 'Фильтр по инстансу',
|
||||
'usage.filter.storage': 'Фильтр по хранилищу',
|
||||
|
||||
// --- Resource events ---
|
||||
'usage.events.resourceType': 'Тип ресурса',
|
||||
'usage.events.eventType': 'Тип события',
|
||||
'usage.events.resourceName': 'Фильтр по имени',
|
||||
'usage.events.col.time': 'Время',
|
||||
'usage.events.col.resource': 'Ресурс',
|
||||
'usage.events.col.event': 'Событие',
|
||||
'usage.events.col.message': 'Сообщение',
|
||||
'usage.events.resource.gpuInstance': 'GPU-инстанс',
|
||||
'usage.events.resource.cpuInstance': 'CPU-инстанс',
|
||||
'usage.events.type.created': 'Создан',
|
||||
'usage.events.type.deleted': 'Удалён',
|
||||
'usage.events.type.started': 'Запущен',
|
||||
'usage.events.type.stopped': 'Остановлен',
|
||||
'usage.events.type.updated': 'Обновлён',
|
||||
'usage.events.type.attached': 'Подключён',
|
||||
'usage.events.type.detached': 'Отключён'
|
||||
};
|
||||
|
||||
@@ -33,8 +33,8 @@ export default {
|
||||
'users.password.confirm.empty': 'Подтвердите новый пароль',
|
||||
'users.password.confirm.error': 'Пароли не совпадают',
|
||||
'users.login.title': 'Вход в',
|
||||
'users.version.islatest': 'GPUStack {version} — последняя версия',
|
||||
'users.version.update': 'Доступно обновление GPUStack {version}',
|
||||
'users.version.islatest': 'MesaStack {version} — последняя версия',
|
||||
'users.version.update': 'Доступно обновление MesaStack {version}',
|
||||
'users.settings.title': 'Настройки пользователя',
|
||||
'users.status.activate': 'Активировать аккаунт',
|
||||
'users.status.deactivate': 'Деактивировать аккаунт',
|
||||
|
||||
@@ -4,7 +4,7 @@ export default {
|
||||
'vendor.hygon': 'Hygon',
|
||||
'vendor.moorthreads': 'Moore Threads',
|
||||
'vendor.iluvatar': 'Iluvatar',
|
||||
'vendor.metax': 'Metax',
|
||||
'vendor.metax': 'MetaX',
|
||||
'vendor.cambricon': 'Cambricon',
|
||||
'vendor.thead': 'T-Head PPU'
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export default {
|
||||
'ai.provider.ollama': 'Ollama',
|
||||
'ai.provider.openai': 'OpenAI',
|
||||
'ai.provider.openrouter': 'OpenRouter',
|
||||
'ai.provider.qwen': 'Qwen',
|
||||
'ai.provider.qwen': 'Alibaba Cloud Model Studio',
|
||||
'ai.provider.spark': 'Spark',
|
||||
'ai.provider.stepfun': 'StepFun',
|
||||
'ai.provider.together-ai': 'TogetherAI',
|
||||
|
||||
@@ -23,5 +23,7 @@ export default {
|
||||
'apikeys.accessScope.inference': 'Inference APIs',
|
||||
'apikeys.access.permissions': 'Access Permissions',
|
||||
'apikeys.type.auto': 'Auto-generated',
|
||||
'apikeys.type.custom': 'Custom'
|
||||
'apikeys.type.custom': 'Custom',
|
||||
'apikeys.button.ipConfig': 'IP Access Control',
|
||||
'quotaLimits.button.title': 'Quota Limit'
|
||||
};
|
||||
|
||||
@@ -23,11 +23,12 @@ export default {
|
||||
'backend.form.defaultExecuteCommand': 'Varsayılan Çalıştırma Komutu',
|
||||
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}', '{{'port'}}', '{{'worker_ip'}}' ve '{{'model_name'}}' dağıtım sırasında gerçek değerlerle değiştirilecek yer tutuculardır.`,
|
||||
'backend.form.defaultBackendParameters': 'Varsayılan Altyapı Parametreleri',
|
||||
'backend.form.parameterFormat': 'Parametre Biçimi',
|
||||
'backend.form.parameterFormat.default': 'Varsayılan',
|
||||
'backend.form.parameterFormat.space': 'Boşluk (--key value)',
|
||||
'backend.form.parameterFormat.equal': 'Eşittir (--key=value)',
|
||||
'backend.form.commonParameters': 'Yaygın Parametreler',
|
||||
'backend.form.flagFormat': 'Bayrak Biçimi',
|
||||
'backend.form.flagFormat.tips':
|
||||
'Bir seçenek ile değerinin birleştirilme biçimi. Boş bırakılırsa her parametre girildiği biçimde korunur, biçim birleştirilmez.',
|
||||
'backend.form.flagFormat.space': 'Boşlukla Ayır (--key value)',
|
||||
'backend.form.flagFormat.equal': 'Eşittir İşareti (--key=value)',
|
||||
'backend.form.commonParameters': 'Ortak Altyapı Parametreleri',
|
||||
'backend.form.commonParameters.tips':
|
||||
'Dağıtım sırasında altyapı parametreleri girişinde öneri olarak gösterilir.',
|
||||
'backend.form.versionConfig': 'Sürüm Yapılandırması',
|
||||
|
||||
@@ -37,8 +37,8 @@ export default {
|
||||
'Uzun bağlam işleme için stres testi. KV önbellek davranışını, bellek kullanımını ve altyapı kararlılığını değerlendirir.',
|
||||
'benchmark.form.profile.heavy.tips':
|
||||
'Çözümleme ağırlıklı üretim kıyaslaması. Sürekli çözümleme hızını ve çıkış token verimini ölçer.',
|
||||
'benchmark.table.filter.bygpu': "GPU'ya göre filtrele",
|
||||
'benchmark.table.filter.bymodel': 'Modele göre filtrele',
|
||||
'benchmark.table.filter.bygpu': 'GPU ara',
|
||||
'benchmark.table.filter.bymodel': 'Model ara',
|
||||
'benchmark.table.filter.bydataset': 'Veri Kümesine göre filtrele',
|
||||
'benchmark.table.filter.byProfile': 'Profile göre filtrele',
|
||||
'benchmark.table.avg': 'Ort.',
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export default {
|
||||
'billing.upsell.title': 'Billing is an Enterprise feature',
|
||||
'billing.upsell.subtitle':
|
||||
'Track spend, generate invoices, and enforce budgets across teams. Upgrade to MesaStack Enterprise to manage billing.',
|
||||
'billing.upsell.featuresTitle': 'What you get in Enterprise',
|
||||
'billing.upsell.feature.usage':
|
||||
'See cost breakdowns by organization, user, and model',
|
||||
'billing.upsell.feature.invoices':
|
||||
'Generate invoices and export billing reports',
|
||||
'billing.upsell.feature.budgets':
|
||||
'Set budgets and spending limits with alerts',
|
||||
'billing.upsell.feature.chargeback':
|
||||
'Attribute and charge back usage to teams and projects',
|
||||
'billing.upsell.cta': 'Learn about Enterprise'
|
||||
};
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
'clusters.workerpool.batchSize.desc':
|
||||
'İşçi havuzunda eşzamanlı olarak oluşturulan işçi düğüm sayısı',
|
||||
'clusters.create.addworker.tips':
|
||||
'Aşağıdaki komutu çalıştırmadan önce lütfen {label} için <a href={link} target="_blank">ön koşulların</a> karşılandığından emin olun.',
|
||||
'Aşağıdaki komutu çalıştırmadan önce lütfen <a href={link} target="_blank">ön koşulların</a> karşılandığından emin olun.',
|
||||
'clusters.create.addCommand.tips':
|
||||
'Eklenmesi gereken İşçi Düğümde, kümeye katılması için aşağıdaki komutu çalıştırın.',
|
||||
'clusters.create.addCommand.k8s.tips':
|
||||
@@ -70,8 +70,13 @@ export default {
|
||||
'<span class="bold-text">Docker dışı</span> kümeler için lütfen Kümeler sayfasından küme kaydı oluşturun veya işçi havuzlarını yönetin.',
|
||||
'clusters.addworker.selectGPU': 'GPU Üreticisi Seç',
|
||||
'clusters.addworker.selectGPU.multiTag': 'Multi-select',
|
||||
'clusters.addworker.selectGPU.subtitle':
|
||||
'Birden fazla GPU Üreticisi seçebilir veya yalnızca CPU kümeleri için hiçbirini seçmeyebilirsiniz',
|
||||
'clusters.addworker.checkEnv': 'Ortamı Kontrol Et',
|
||||
'clusters.addworker.checkEnv.cpuOnlyTips':
|
||||
'Kubernetes kümesinde en az bir hazır düğüm olduğunu doğrulamak için aşağıdaki komutu kullanın. Yalnızca CPU kümelerini kaydediyorsunuz.',
|
||||
'clusters.addworker.specifyArgs': 'Argümanları Belirle',
|
||||
'clusters.addworker.dtkVersion': 'DTK Sürümü',
|
||||
'clusters.addworker.runCommand': 'Komutu Çalıştır',
|
||||
'clusters.addworker.specifyWorkerIP': "İşçi Düğüm IP'si",
|
||||
'clusters.addworker.detectWorkerIP': "İşçi Düğüm IP'sini Otomatik Algıla",
|
||||
@@ -80,7 +85,7 @@ export default {
|
||||
'clusters.addworker.detectWorkerAddress.tips':
|
||||
"Belirtilmezse İşçi Düğüm IP'si varsayılır.",
|
||||
'clusters.addworker.externalIP.tips':
|
||||
'VPC veya özel ağda çalıştırılıyorsa, lütfen GPUStack Sunucusuna erişilebilir İşçi Düğüm harici adresini belirtin.',
|
||||
'VPC veya özel ağda çalıştırılıyorsa, lütfen MesaStack Sunucusuna erişilebilir İşçi Düğüm harici adresini belirtin.',
|
||||
'clusters.addworker.enterWorkerIP': "İşçi düğüm IP'sini girin",
|
||||
'clusters.addworker.enterWorkerIP.error': "Lütfen işçi düğüm IP'sini girin.",
|
||||
'clusters.addworker.enterWorkerAddress': 'İşçi düğüm harici adresini girin',
|
||||
@@ -108,18 +113,20 @@ export default {
|
||||
'{count} yeni işçi düğüm kümeye eklendi.',
|
||||
'clusters.addworker.message.success_multiple':
|
||||
'{count} yeni işçi düğüm kümeye eklendi.',
|
||||
'clusters.create.serverUrl': "GPUStack Sunucu URL'si",
|
||||
'clusters.create.serverUrl': "MesaStack Sunucu URL'si",
|
||||
'clusters.create.workerConfig': 'İşçi Düğüm Yapılandırması',
|
||||
'clusters.edit.k8sOptions.changed.tip':
|
||||
'Kubernetes seçeneklerini değiştirdiniz. Değişikliklerin etkili olması için kayıt komutunu hedef kümede yeniden çalıştırın.',
|
||||
'clusters.addworker.containerName': 'İşçi Düğüm Konteyner Adı',
|
||||
'clusters.addworker.containerName.tips':
|
||||
'İşçi düğüm konteyneri için bir ad belirtin.',
|
||||
'clusters.addworker.dataVolume': 'GPUStack Veri Birimi',
|
||||
'clusters.addworker.dataVolume': 'MesaStack Veri Birimi',
|
||||
'clusters.addworker.dataVolume.tips':
|
||||
'GPUStack için veri depolama yolu belirtin.',
|
||||
'MesaStack için veri depolama yolu belirtin.',
|
||||
'clusters.table.ip.internal': 'Dahili',
|
||||
'clusters.table.ip.external': 'Harici',
|
||||
'clusters.form.serverUrl.tips':
|
||||
"İşçi düğüm GPUStack Sunucusuna doğrudan erişemiyorsa, harici olarak erişilebilir bir GPUStack hizmet URL'si belirtin.",
|
||||
"İşçi düğüm MesaStack Sunucusuna doğrudan erişemiyorsa, harici olarak erişilebilir bir MesaStack hizmet URL'si belirtin.",
|
||||
'clusters.form.setDefault': 'Varsayılan Olarak Ayarla',
|
||||
'clusters.form.setDefault.tips': 'Dağıtım için varsayılan.',
|
||||
'clusters.addworker.noClusters': 'Kullanılabilir Docker kümesi bulunamadı',
|
||||
@@ -138,7 +145,7 @@ export default {
|
||||
'clusters.addworker.theadNotes-02':
|
||||
'T-Head PPU, cihaz enjeksiyonu için Container Device Interface (CDI) kullanır ve CDI oluşturma için <span class="bold-text">/var/run/cdi</span> dizininin kullanılabilir olmasını gerektirir.',
|
||||
'clusters.addworker.nvidiaNotes':
|
||||
'GPUStack v2.1\'deki yerleşik çıkarım altyapıları <span class="bold-text">CUDA 12.6+</span> gerektirir. Lütfen NVIDIA sürücü sürümünüzün <span class="bold-text">560</span> veya daha yeni olduğundan emin olun.',
|
||||
'MesaStack\'teki yerleşik çıkarım altyapıları <span class="bold-text">CUDA 12.8+</span> gerektirir. Lütfen NVIDIA sürücü sürümünüzün <span class="bold-text">570</span> veya daha yeni olduğundan emin olun.',
|
||||
'clusters.volume.title': 'Volume Mounts',
|
||||
'clusters.volume.name': 'Volume Name',
|
||||
'clusters.volume.mountPath': 'Container Path',
|
||||
@@ -165,8 +172,8 @@ export default {
|
||||
'clusters.volume.add': 'Add Volume Mount',
|
||||
'clusters.systemDefaultContainerRegistry.title': 'Default Container Registry',
|
||||
'clusters.systemDefaultContainerRegistry.tip':
|
||||
'Default registry used to resolve GPUStack images for this cluster. Falls back to the server default when unset.',
|
||||
'clusters.k8sOptions.title': 'K8s Deployment Options',
|
||||
'Default registry used to resolve MesaStack images for this cluster. Falls back to the server default when unset.',
|
||||
'clusters.k8sOptions.title': 'Kubernetes Deployment Options',
|
||||
'clusters.imageCredentials.title': 'Image Credentials',
|
||||
'clusters.imageCredentials.add': 'Add Credential',
|
||||
'clusters.imageCredentials.registry': 'Registry',
|
||||
@@ -177,13 +184,18 @@ export default {
|
||||
'Pod nodeSelector applied to every worker DaemonSet — only nodes whose labels match are eligible to run the worker.',
|
||||
'clusters.operatorImage.title': 'Operator Image',
|
||||
'clusters.operatorImage.tip':
|
||||
'Override for the GPUStack Operator container image. Leave empty to use the server default.',
|
||||
'Override for the MesaStack Operator container image. Leave empty to use the server default.',
|
||||
'clusters.namespace.title': 'Namespace',
|
||||
'clusters.namespace.tip':
|
||||
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
||||
'clusters.gpuInstances.title': 'GPU Instances',
|
||||
'clusters.gpuInstances.tip': 'Enable GPU instance support for this cluster.',
|
||||
'clusters.gpuInstances.staticAddress': 'Static Access Address',
|
||||
'clusters.clusterType.title': 'Cluster Type',
|
||||
'clusters.modelService.title': 'Model Service',
|
||||
'clusters.modelService.tip':
|
||||
'For LLM inference and API serving — e.g. exposing model APIs and token-based services.',
|
||||
'clusters.gpuInstances.title': 'GPU Service',
|
||||
'clusters.gpuInstances.tip':
|
||||
'For on-demand GPU compute — e.g. interactive development, training jobs, or custom environments.',
|
||||
'clusters.gpuInstances.staticAddress': 'GPU Service Static Access Address',
|
||||
'clusters.gpuInstances.staticAddress.tip':
|
||||
'Static address the operator uses to access GPU instances in this cluster (e.g. a LoadBalancer VIP). Optional.'
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ export default {
|
||||
'common.button.enabled': 'Etkin',
|
||||
'common.button.disabled': 'Devre dışı',
|
||||
'common.button.upgrade': 'Yükselt',
|
||||
'common.enterprise.feature': 'Available in MesaStack Enterprise',
|
||||
'common.input.holder': 'Lütfen girin',
|
||||
'common.validate.value': '{name} değeri gereklidir',
|
||||
'common.button.edit': 'Düzenle',
|
||||
@@ -55,6 +56,7 @@ export default {
|
||||
'common.button.viewevent': 'Olayları Görüntüle',
|
||||
'common.button.recreate': 'Yeniden Oluştur',
|
||||
'common.table.operation': 'İşlemler',
|
||||
'common.table.creator': 'Oluşturan',
|
||||
'common.table.createTime': 'Oluşturulma',
|
||||
'common.table.updateTime': 'Güncellenme',
|
||||
'common.table.description': 'Açıklama',
|
||||
@@ -65,6 +67,7 @@ export default {
|
||||
'common.search.name.placeholder': 'ada göre filtrele',
|
||||
'common.search.id.placeholder': 'kimliğe göre filtrele',
|
||||
'common.filter.byId': 'kimliğe göre filtrele',
|
||||
'common.filter.byCreator': 'Oluşturana göre filtrele',
|
||||
'common.table.type': 'Tür',
|
||||
'common.table.default': 'Varsayılan Değer',
|
||||
'common.copy.success': 'Kopyalama başarılı!',
|
||||
@@ -164,6 +167,7 @@ export default {
|
||||
'common.time.hour': 'saat',
|
||||
'common.time.minute': 'dakika',
|
||||
'common.issue.report': 'Sorun bildir',
|
||||
'common.github.star.tooltip': "GitHub'da bize yıldız verin",
|
||||
'common.social.discord': "Discord'umuza Katılın",
|
||||
'common.table.mark': 'Yorum',
|
||||
'common.table.rollback.mark': 'Geri Alma Yorumu',
|
||||
@@ -211,7 +215,7 @@ export default {
|
||||
'common.form.password': 'Şifre',
|
||||
'common.form.username': 'Kullanıcı adı',
|
||||
'common.login.rember': 'Beni hatırla',
|
||||
'settings.company': 'GPUStack.ai',
|
||||
'settings.company': 'MesaStack',
|
||||
'common.button.help': 'Yardım',
|
||||
'common.button.feedback': 'Geri Bildirim',
|
||||
'common.button.docs': 'Dokümantasyon',
|
||||
@@ -260,6 +264,10 @@ export default {
|
||||
'common.login.auth': 'Kimlik doğrulanıyor...',
|
||||
'common.login.auth.failed': 'Kimlik doğrulama başarısız',
|
||||
'common.login.password': 'Şifre ile giriş yap',
|
||||
'common.login.username.holder': 'Lütfen kullanıcı adını girin',
|
||||
'common.login.password.holder': 'Lütfen şifreyi girin',
|
||||
'common.login.newpassword.holder': 'Lütfen yeni şifreyi girin',
|
||||
'common.login.confirm.holder': 'Lütfen şifreyi tekrar girin',
|
||||
'common.external.login': '{type} ile giriş yap',
|
||||
'common.sso.noConfig':
|
||||
'Bu sistemde çoklu oturum açma etkinleştirilmemiş. Lütfen yöneticinize başvurun.',
|
||||
@@ -288,5 +296,7 @@ export default {
|
||||
'common.image.limit.height': 'Image height must be {height}.',
|
||||
'common.remaining': 'Kalan {count}',
|
||||
'common.max': 'Maks. {count}',
|
||||
'common.validate.group': 'Please complete the {group} configuration'
|
||||
'common.max.count': '{label} Sayısı',
|
||||
'common.validate.group': 'Please complete the {group} configuration',
|
||||
'common.preferences': 'Tercihler'
|
||||
};
|
||||
|
||||
@@ -13,7 +13,10 @@ export default {
|
||||
'gpuservice.template.command.placeholder':
|
||||
'Argümanları boşlukla ayırın; boşluk içeren argümanları tırnak içine alın, örn.: /bin/bash -c "echo hello world"',
|
||||
'gpuservice.template.mountPath': 'Bağlama Yolu',
|
||||
'gpuservice.template.mountPath.tips':
|
||||
'Bu şablondan bir örnek oluşturulurken depolama biriminin varsayılan olarak bağlanacağı yol. Örnek çalışırken saklanması gereken verileri kalıcı hale getirmek için kullanılabilir.',
|
||||
'gpuservice.template.containerDisk': 'Konteyner Diski (GB)',
|
||||
'gpuservice.template.containerDisk.tips': 'Konteyner sistem diskinin boyutu.',
|
||||
'gpuservice.template.memory': 'Bellek (GB)',
|
||||
'gpuservice.instance.containerDisk.remaining':
|
||||
'Konteyner Diski (Maks. {count} GB)',
|
||||
@@ -97,10 +100,13 @@ export default {
|
||||
'gpuservice.instance.templates': 'Örnek Şablonları',
|
||||
'gpuservice.instance.section.storage': 'Depolama Hacmi',
|
||||
'gpuservice.instance.type.required': 'Lütfen bir örnek türü seçin',
|
||||
'gpuservice.instance.type.noAvailable': 'Kullanılabilir örnek türü yok',
|
||||
'gpuservice.instance.gpuCount': 'GPU Sayısı',
|
||||
'gpuservice.instance.gpuCount.required': 'Lütfen GPU sayısını girin',
|
||||
'gpuservice.instance.gpuCount.max': 'En fazla {count} GPU kartı seçin',
|
||||
'gpuservice.instance.gpuCount.min': 'En az {count} GPU kartı seçin',
|
||||
'gpuservice.instance.cpuCount.max': 'En fazla {count} CPU çekirdeği seçin',
|
||||
'gpuservice.instance.cpuCount.min': 'En az {count} CPU çekirdeği seçin',
|
||||
'gpuservice.instance.gpuCount.noAvailable':
|
||||
'Kullanılabilir GPU kaynağı yok, lütfen başka bir örnek türü seçin.',
|
||||
'gpuservice.instance.gpuCount.zero': 'Yalnızca CPU, ortam hazırlığı için.',
|
||||
@@ -108,7 +114,13 @@ export default {
|
||||
'gpuservice.instance.sliced': 'Bölünmüş',
|
||||
'gpuservice.instance.memory': 'VRAM',
|
||||
'gpuservice.instance.ram': 'RAM',
|
||||
'gpuservice.instance.os': 'OS',
|
||||
'gpuservice.instance.arch': 'Mimari',
|
||||
'gpuservice.instance.disk': 'Disk',
|
||||
'gpuservice.table.count': 'Sayı',
|
||||
'gpuservice.instance.disk.system': 'Sistem Diski',
|
||||
'gpuservice.instance.disk.ephemeral': 'Geçici Depolama',
|
||||
'gpuservice.instance.disk.persistent': 'Kalıcı Depolama',
|
||||
'gpuservice.instance.search.type.placeholder': 'Ada göre ara',
|
||||
'gpuservice.instance.search.template.placeholder':
|
||||
'Şablon adına, imaja veya bağlama yoluna göre ara',
|
||||
@@ -142,8 +154,7 @@ export default {
|
||||
'gpuservice.storage.persistent': 'Kalıcı',
|
||||
'gpuservice.storage.temporary': 'Geçici',
|
||||
'gpuservice.storage.persistentVolume': 'Kalıcı',
|
||||
'gpuservice.storage.persistentVolume.required':
|
||||
'Lütfen bir kalıcı hacim seçin',
|
||||
'gpuservice.storage.persistentVolume.required': 'Lütfen bir depolama seçin',
|
||||
'gpuservice.storage.persistentVolume.capacity': 'Kapasite (GB)',
|
||||
'gpuservice.storage.persistentVolume.capacity.required':
|
||||
'Lütfen kapasiteyi girin',
|
||||
@@ -157,5 +168,10 @@ export default {
|
||||
'gpuservice.storage.temporary.tips':
|
||||
'Data is cleared when the instance stops.',
|
||||
'gpuservice.storage.persistentVolume.tips':
|
||||
'Data persists across restarts and is deleted only when the instance is terminated. Cannot be shared with other instances.'
|
||||
'Data persists across instance restarts. Persistent volumes remain intact after instance termination and can be shared by multiple instances.',
|
||||
'gpuservice.form.storage.select': 'Depolamayı Seç',
|
||||
'gpuservice.creator': 'Oluşturan',
|
||||
'gpuservice.owner.global': 'Genel',
|
||||
'gpuservice.template.group.yours': 'Şablonlarınız',
|
||||
'gpuservice.template.group.global': 'Genel Şablonlar'
|
||||
};
|
||||
|
||||
@@ -29,15 +29,19 @@ export default {
|
||||
'menu.accessControl': 'Erişim Kontrolü',
|
||||
'menu.accessControl.apikeys': 'API Anahtarları',
|
||||
'menu.accessControl.users': 'Kullanıcılar',
|
||||
'menu.profile': 'Profil',
|
||||
'menu.accessControl.organizations': 'Organizasyonlar',
|
||||
'menu.profile': 'Preferences',
|
||||
'menu.login': 'Giriş',
|
||||
'menu.usage': 'Kullanım',
|
||||
'menu.usage.usage': 'Kullanım',
|
||||
'menu.billingAndUsage': 'Kullanım ve Faturalandırma',
|
||||
'menu.billingAndUsage.usage': 'Kullanım',
|
||||
'menu.billingAndUsage.billing': 'Faturalandırma',
|
||||
'menu.404': '404',
|
||||
'menu.clusterManagement': 'Küme Yönetimi',
|
||||
'menu.clusterManagement.clusters': 'Kümeler',
|
||||
'menu.clusterManagement.credentials': 'Bulut Kimlik Bilgileri',
|
||||
'menu.clusterManagement.clusterDetail': 'Küme Detayı',
|
||||
'menu.clusterManagement.clusterCreate': 'Küme Oluştur',
|
||||
'menu.resources.clusters': 'Kümeler',
|
||||
'menu.resources.credentials': 'Bulut Kimlik Bilgileri',
|
||||
'menu.resources.clusterDetail': 'Küme Detayı',
|
||||
'menu.resources.clusterCreate': 'Küme Oluştur',
|
||||
'menu.models.backendsList': 'Çıkarım Altyapıları',
|
||||
'menu.models.instances': 'Instances',
|
||||
'menu.settings': 'Settings',
|
||||
|
||||
@@ -14,7 +14,7 @@ export default {
|
||||
'models.form.env': 'Ortam Değişkenleri',
|
||||
'models.form.configurations': 'Yapılandırmalar',
|
||||
'models.form.s3address': 'S3 Adresi',
|
||||
'models.form.partialoffload.tips': `CPU aktarımı etkinleştirildiğinde, GPU kaynakları yetersiz olduğunda GPUStack CPU belleği ayırır. Hibrit CPU+GPU veya tam CPU çıkarımı kullanmak için çıkarım altyapısını doğru şekilde yapılandırmanız gerekir.`,
|
||||
'models.form.partialoffload.tips': `CPU aktarımı etkinleştirildiğinde, GPU kaynakları yetersiz olduğunda MesaStack CPU belleği ayırır. Hibrit CPU+GPU veya tam CPU çıkarımı kullanmak için çıkarım altyapısını doğru şekilde yapılandırmanız gerekir.`,
|
||||
'models.form.distribution.tips': `Bir işçi düğümün kaynakları yetersiz olduğunda, modelin katmanlarının bir kısmının tekli veya çoklu uzak işçi düğümlere aktarılmasına olanak tanır.`,
|
||||
'models.openinplayground': 'Deneme Alanında Aç',
|
||||
'models.instances': 'örnekler',
|
||||
@@ -24,7 +24,7 @@ export default {
|
||||
'model.deploy.sort': 'Sırala',
|
||||
'model.deploy.search.placeholder': 'Modelleri aramak için <kbd>/</kbd> yazın',
|
||||
'model.form.ollamatips':
|
||||
"İpucu: Aşağıdakiler GPUStack'te önceden yapılandırılmış Ollama modelleridir. İstediğiniz modeli seçin veya dağıtmak istediğiniz modeli doğrudan sağdaki 【{name}】 giriş kutusuna yazın.",
|
||||
"İpucu: Aşağıdakiler MesaStack'te önceden yapılandırılmış Ollama modelleridir. İstediğiniz modeli seçin veya dağıtmak istediğiniz modeli doğrudan sağdaki 【{name}】 giriş kutusuna yazın.",
|
||||
'models.sort.name': 'Ad',
|
||||
'models.sort.size': 'Boyut',
|
||||
'models.sort.likes': 'Beğeniler',
|
||||
@@ -87,7 +87,7 @@ export default {
|
||||
'models.form.filePath': 'Model Yolu',
|
||||
'models.form.backendVersion': 'Altyapı Sürümü',
|
||||
'models.form.backendVersion.tips':
|
||||
'{backend}{version} sürümünü kullanmak için sistem, ilgili sürümü yüklemek üzere çevrimiçi ortamda otomatik olarak sanal ortam oluşturur. GPUStack yükseltmesinden sonra altyapı sürümü sabit kalır. {link}',
|
||||
'{backend}{version} sürümünü kullanmak için sistem, ilgili sürümü yüklemek üzere çevrimiçi ortamda otomatik olarak sanal ortam oluşturur. MesaStack yükseltmesinden sonra altyapı sürümü sabit kalır. {link}',
|
||||
'models.form.gpuselector': 'GPU Seçici',
|
||||
'models.form.backend.llamabox':
|
||||
'GGUF format modeller için, Linux, macOS ve Windows destekler.',
|
||||
@@ -277,7 +277,7 @@ export default {
|
||||
'models.form.backendVersions.tips': `Daha fazla sürüm kullanmak için {link} sayfasına gidin ve sürüm eklemek üzere altyapıyı düzenleyin.`,
|
||||
'models.catalog.nogpus.tips':
|
||||
'Seçili kümede bu model için uyumlu GPU bulunmuyor.',
|
||||
'models.form.modelfile.notfound': `Belirttiğiniz model dosyası yolu GPUStack sunucusunda mevcut değil. Model dosyasını hem GPUStack sunucusunda hem de GPUStack işçi düğümlerinde aynı yola yerleştirmeniz önerilir. Bu, GPUStack'in daha iyi kararlar almasına yardımcı olur.`,
|
||||
'models.form.modelfile.notfound': `Belirttiğiniz model dosyası yolu MesaStack sunucusunda mevcut değil. Model dosyasını hem MesaStack sunucusunda hem de MesaStack işçi düğümlerinde aynı yola yerleştirmeniz önerilir. Bu, MesaStack'in daha iyi kararlar almasına yardımcı olur.`,
|
||||
'models.form.readyWorkers': 'hazır işçi düğüm',
|
||||
'models.form.maxContextLength': 'Maksimum Bağlam Uzunluğu',
|
||||
'models.form.backend.helperText':
|
||||
@@ -291,7 +291,7 @@ export default {
|
||||
'models.instance.startHistory': 'Run History',
|
||||
'models.instance.startHistory.tips':
|
||||
'Shows logs from the run before the last error-triggered restart.',
|
||||
'models.form.lora.label': 'LoRA Adapter',
|
||||
'models.form.lora.label': 'LoRA Adapters',
|
||||
'models.form.lora.add': 'Add LoRA Adapter',
|
||||
'models.form.lora.select': 'Select LoRA',
|
||||
'models.form.lora.name': 'LoRA name',
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export default {
|
||||
'organizations.upsell.title': 'Organizations are an Enterprise feature',
|
||||
'organizations.upsell.subtitle':
|
||||
'Multi-tenancy lets you isolate users, resources, and quotas across teams. Upgrade to MesaStack Enterprise to manage organizations.',
|
||||
'organizations.upsell.featuresTitle': 'What you get in Enterprise',
|
||||
'organizations.upsell.feature.orgs':
|
||||
'Create organizations to group users and isolate workloads',
|
||||
'organizations.upsell.feature.members':
|
||||
'Manage members and roles per organization',
|
||||
'organizations.upsell.feature.quotas':
|
||||
'Set resource and token quotas per organization',
|
||||
'organizations.upsell.feature.isolation':
|
||||
'Scope API keys, model deployments, and resources per organization',
|
||||
'organizations.upsell.cta': 'Learn about Enterprise'
|
||||
};
|
||||
@@ -52,7 +52,7 @@ export default {
|
||||
'resources.worker.container.supported': 'macOS veya Windows desteklenmez.',
|
||||
'resources.worker.current.version': 'Mevcut sürüm: {version}.',
|
||||
'resources.worker.driver.install':
|
||||
'GPUStack kurulumundan önce <a href="https://docs.gpustack.ai/latest/installation/installation-requirements/" target="_blank">gerekli sürücüleri ve kütüphaneleri</a> yükleyin.',
|
||||
'MesaStack kurulumundan önce <a href="https://docs.gpustack.ai/latest/installation/installation-requirements/" target="_blank">gerekli sürücüleri ve kütüphaneleri</a> yükleyin.',
|
||||
'resources.worker.select.command':
|
||||
'Komutu oluşturmak için bir etiket seçin ve kopyala düğmesiyle kopyalayın.',
|
||||
'resources.worker.script.install': 'Betik Kurulumu',
|
||||
@@ -89,7 +89,7 @@ export default {
|
||||
'<span class="bold-text">Token</span>\'ı yapıştırın.',
|
||||
'resources.register.worker.step7':
|
||||
'Ayarları uygulamak için <span class="bold-text">Yeniden Başlat</span>\'a tıklayın.',
|
||||
'resources.register.install.title': '{os} üzerine GPUStack kur',
|
||||
'resources.register.install.title': '{os} üzerine MesaStack kur',
|
||||
'resources.register.download':
|
||||
'<a href={url} target="_blank">Yükleyiciyi</a> indirip kurun. Yalnızca desteklenen: {versions}.',
|
||||
'resource.register.maos.support': 'Apple Silicon (M serisi), macOS 14+',
|
||||
@@ -110,7 +110,7 @@ export default {
|
||||
'Kullanılabilir küme yok. Lütfen düğüm eklemeden önce bir küme oluşturun.',
|
||||
'resources.metrics.details': 'İzleme',
|
||||
'resoureces.worker.upgrade.tips':
|
||||
'Please upgrade to match the GPUStack Server version.',
|
||||
'Please upgrade to match the MesaStack Server version.',
|
||||
'resources.worker.version': 'Worker Version: {version}',
|
||||
'resources.server.version': 'Server Version: {version}',
|
||||
'resources.worker.currentVersion': 'Current Version: {version}',
|
||||
@@ -119,5 +119,5 @@ export default {
|
||||
};
|
||||
|
||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||
// 1. 'resoureces.worker.upgrade.tips': 'Please upgrade to match the GPUStack Server version.'
|
||||
// 1. 'resoureces.worker.upgrade.tips': 'Please upgrade to match the MesaStack Server version.'
|
||||
// ========== End of To-Do List ==========
|
||||
|
||||
@@ -17,9 +17,15 @@ export default {
|
||||
'usage.table.user.apiKeysUsed': 'API Keys Used',
|
||||
'usage.table.lastActive': 'Last Active',
|
||||
'usage.filter.granularity': 'Granularity',
|
||||
'usage.filter.granularity.hour': 'Saat',
|
||||
'usage.filter.granularity.day': 'Day',
|
||||
'usage.filter.granularity.week': 'Week',
|
||||
'usage.filter.granularity.month': 'Month',
|
||||
'usage.tabs.summary': 'Özet',
|
||||
'usage.tabs.tokens': 'Token',
|
||||
'usage.tabs.gpuInstances': 'GPU Örnekleri',
|
||||
'usage.tabs.storage': 'Depolama',
|
||||
'usage.tabs.resourceEvents': 'Kaynak Olayları',
|
||||
'usage.tabs.models': 'Models',
|
||||
'usage.tabs.apikeys': 'API Keys',
|
||||
'usage.tabs.users': 'User',
|
||||
@@ -32,5 +38,70 @@ export default {
|
||||
'usage.chart.cached': 'Cached',
|
||||
'usage.chart.uncached': 'Uncached',
|
||||
'usage.chart.inputTokensCached': 'Input Tokens (Cached/Uncached)',
|
||||
'usage.table.inputTokensCached': 'Input Tokens Cached'
|
||||
'usage.table.inputTokensCached': 'Input Tokens Cached',
|
||||
|
||||
// --- Resource usage: shared metrics & units ---
|
||||
'usage.metric.tokens': 'Token',
|
||||
'usage.metric.input': 'Girdi',
|
||||
'usage.metric.output': 'Çıktı',
|
||||
'usage.metric.gpuHours': 'GPU Saati',
|
||||
'usage.metric.instanceHours': 'Örnek Saati',
|
||||
'usage.metric.gbDays': 'GB-Gün',
|
||||
'usage.metric.gbHours': 'GB-Saat',
|
||||
'usage.metric.activeUsers': 'Aktif Kullanıcılar',
|
||||
'usage.metric.activeInstances': 'Aktif Örnekler',
|
||||
'usage.metric.activeStorage': 'Aktif Depolama',
|
||||
'usage.metric.activeVolumes': 'Aktif Birimler',
|
||||
'usage.metric.storageTypes': 'Depolama Türleri',
|
||||
'usage.metric.gpuHours.tip':
|
||||
'Örnek çalışma süresinin GPU sayısına göre ağırlıklandırılmış hali: N GPU kullanan bir örnek H saat çalıştığında N × H GPU-saat olarak sayılır. Her örnek tek bir GPU kullandığında Örnek Saati ile eşittir.',
|
||||
'usage.metric.instanceHours.tip':
|
||||
'Her örneğin kullandığı GPU sayısından bağımsız olarak tüm örneklerin toplam çalışma süresi. 2 saat çalışan bir örnek = 2 örnek-saat.',
|
||||
'usage.metric.gbDays.tip':
|
||||
'Depolama kapasitesinin zamana göre integrali, GB × gün cinsinden: 5 gün boyunca tutulan 10 GB = 50 GB-gün. (= GB-Saat ÷ 24)',
|
||||
'usage.metric.gbHours.tip':
|
||||
'Depolama kapasitesinin zamana göre integrali, GB × saat cinsinden: 5 saat boyunca tutulan 10 GB = 50 GB-saat.',
|
||||
|
||||
// --- Resource usage: common table / labels ---
|
||||
'usage.common.noData': 'Veri yok',
|
||||
'usage.common.unknown': 'bilinmiyor',
|
||||
'usage.table.date': 'Tarih',
|
||||
'usage.table.name': 'Ad',
|
||||
'usage.table.user': 'Kullanıcı',
|
||||
'usage.table.users': 'Kullanıcılar',
|
||||
'usage.table.type': 'Tür',
|
||||
'usage.table.instance': 'Örnek',
|
||||
'usage.table.instanceType': 'Örnek Türü',
|
||||
'usage.table.instanceTypes': 'Örnek Türleri',
|
||||
'usage.table.instances': 'Örnekler',
|
||||
'usage.table.capacity': 'Kapasite',
|
||||
'usage.export.tableNamed': 'Tablo Verilerini Dışa Aktar — {name}',
|
||||
|
||||
// --- Summary tab ---
|
||||
'usage.summary.compute': 'Hesaplama',
|
||||
'usage.summary.tokensOverTime': 'Zaman içinde Token',
|
||||
'usage.summary.gpuHoursOverTime': 'Zaman içinde GPU Saati',
|
||||
'usage.summary.gbDaysOverTime': 'Zaman içinde GB-Gün',
|
||||
|
||||
// --- GPU Instances / Storage filters ---
|
||||
'usage.filter.instance': 'Örneğe göre filtrele',
|
||||
'usage.filter.storage': 'Depolamaya göre filtrele',
|
||||
|
||||
// --- Resource events ---
|
||||
'usage.events.resourceType': 'Kaynak türü',
|
||||
'usage.events.eventType': 'Olay türü',
|
||||
'usage.events.resourceName': 'Ada göre filtrele',
|
||||
'usage.events.col.time': 'Zaman',
|
||||
'usage.events.col.resource': 'Kaynak',
|
||||
'usage.events.col.event': 'Olay',
|
||||
'usage.events.col.message': 'Mesaj',
|
||||
'usage.events.resource.gpuInstance': 'GPU Örneği',
|
||||
'usage.events.resource.cpuInstance': 'CPU Örneği',
|
||||
'usage.events.type.created': 'Oluşturuldu',
|
||||
'usage.events.type.deleted': 'Silindi',
|
||||
'usage.events.type.started': 'Başlatıldı',
|
||||
'usage.events.type.stopped': 'Durduruldu',
|
||||
'usage.events.type.updated': 'Güncellendi',
|
||||
'usage.events.type.attached': 'Eklendi',
|
||||
'usage.events.type.detached': 'Ayrıldı'
|
||||
};
|
||||
|
||||
@@ -33,12 +33,12 @@ export default {
|
||||
'users.password.confirm.empty': 'Lütfen yeni şifreyi tekrar girin.',
|
||||
'users.password.confirm.error': 'Girilen iki şifre eşleşmiyor.',
|
||||
'users.login.title': 'Giriş yap:',
|
||||
'users.version.islatest': 'GPUStack {version} en güncel sürümdür',
|
||||
'users.version.update': 'GPUStack {version} kullanılabilir',
|
||||
'users.version.islatest': 'MesaStack {version} en güncel sürümdür',
|
||||
'users.version.update': 'MesaStack {version} kullanılabilir',
|
||||
'users.settings.title': 'Kullanıcı Ayarları',
|
||||
'users.status.activate': 'Hesabı Etkinleştir',
|
||||
'users.status.deactivate': 'Hesabı Devre Dışı Bırak',
|
||||
'users.status.inactiveAccount': 'Pasif Hesap',
|
||||
'users.login.getInitialPassword':
|
||||
'Başlangıç yönetici şifresini almak için GPUStack Sunucunuzda aşağıdaki komutu çalıştırın.'
|
||||
'Başlangıç yönetici şifresini almak için MesaStack Sunucunuzda aşağıdaki komutu çalıştırın.'
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ export default {
|
||||
'vendor.hygon': 'Hygon',
|
||||
'vendor.moorthreads': 'Moore Threads',
|
||||
'vendor.iluvatar': 'Iluvatar',
|
||||
'vendor.metax': 'Metax',
|
||||
'vendor.metax': 'MetaX',
|
||||
'vendor.cambricon': 'Cambricon',
|
||||
'vendor.thead': 'T-Head PPU'
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export default {
|
||||
'ai.provider.ollama': 'Ollama',
|
||||
'ai.provider.openai': 'OpenAI',
|
||||
'ai.provider.openrouter': 'OpenRouter',
|
||||
'ai.provider.qwen': '通义千问',
|
||||
'ai.provider.qwen': '阿里云百炼',
|
||||
'ai.provider.spark': '讯飞星火',
|
||||
'ai.provider.stepfun': '阶跃星辰',
|
||||
'ai.provider.together-ai': 'Together AI',
|
||||
|
||||
@@ -22,5 +22,7 @@ export default {
|
||||
'apikeys.accessScope.inference': '推理接口',
|
||||
'apikeys.access.permissions': '访问权限',
|
||||
'apikeys.type.auto': '自动生成',
|
||||
'apikeys.type.custom': '自定义'
|
||||
'apikeys.type.custom': '自定义',
|
||||
'apikeys.button.ipConfig': 'IP 访问控制',
|
||||
'quotaLimits.button.title': '配额限制'
|
||||
};
|
||||
|
||||
@@ -23,11 +23,12 @@ export default {
|
||||
'backend.form.defaultExecuteCommand': '默认执行命令',
|
||||
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}'、'{{'port'}}'、'{{'worker_ip'}}' 和 '{{'model_name'}}' 都是占位符,在部署过程中会被替换为实际的值。`,
|
||||
'backend.form.defaultBackendParameters': '默认后端参数',
|
||||
'backend.form.parameterFormat': '参数输出格式',
|
||||
'backend.form.parameterFormat.default': '后端默认',
|
||||
'backend.form.parameterFormat.space': '空格分隔 (--key value)',
|
||||
'backend.form.parameterFormat.equal': '等号连接 (--key=value)',
|
||||
'backend.form.commonParameters': '常用参数',
|
||||
'backend.form.flagFormat': '参数格式',
|
||||
'backend.form.flagFormat.tips':
|
||||
'选项名与取值之间使用的连接格式。留空则保留每个参数原本的写法,不做统一转换。',
|
||||
'backend.form.flagFormat.space': '空格分隔 (--key value)',
|
||||
'backend.form.flagFormat.equal': '等号连接 (--key=value)',
|
||||
'backend.form.commonParameters': '常用后端参数',
|
||||
'backend.form.commonParameters.tips': '部署模型时作为后端参数候选项展示。',
|
||||
'backend.form.versionConfig': '版本配置',
|
||||
'backend.form.addParameter': '添加参数',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user