Compare commits
215
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
16a97772d3 | ||
|
|
d33167d070 | ||
|
|
f62e0ba372 | ||
|
|
1b6466ae78 | ||
|
|
29c5987bf1 | ||
|
|
fcd1d01e83 | ||
|
|
a80b889760 | ||
|
|
f71bf1b074 | ||
|
|
7e048ee537 | ||
|
|
34098e8950 | ||
|
|
faf0d4d605 | ||
|
|
85ade88d55 | ||
|
|
7d0b94330d | ||
|
|
5cff1298fc | ||
|
|
d3a3d4e96e | ||
|
|
f99621ec92 | ||
|
|
e47a9304f4 | ||
|
|
9488fc1251 | ||
|
|
b590aa746b | ||
|
|
ef61134b8b | ||
|
|
a9daa0b586 | ||
|
|
ae0ad25ed6 | ||
|
|
a4c48c1981 | ||
|
|
3497590d2e | ||
|
|
11cf1329c8 | ||
|
|
ba5b41cf3a | ||
|
|
1d70446cc4 | ||
|
|
f15d0ab243 | ||
|
|
34be4800d7 | ||
|
|
e40b0c2e07 | ||
|
|
45e0ad9e93 | ||
|
|
96e6ddfe8a | ||
|
|
6b47bacfac | ||
|
|
099b419e34 | ||
|
|
e343ec0a1e | ||
|
|
e3d6d08916 | ||
|
|
d0bc206c83 | ||
|
|
7287f8c81b | ||
|
|
1878753cde |
+1
-2
@@ -3,7 +3,6 @@ node_modules
|
|||||||
.umi-production
|
.umi-production
|
||||||
public/static/*.js
|
public/static/*.js
|
||||||
public/static/*.css
|
public/static/*.css
|
||||||
src/components/icon-font/iconfont/iconfont.js
|
src/components/iconfont/
|
||||||
src/components/icon-font/iconfont/*.css
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -3,5 +3,5 @@ module.exports = {
|
|||||||
rules: {
|
rules: {
|
||||||
'selector-class-pattern': null
|
'selector-class-pattern': null
|
||||||
},
|
},
|
||||||
ignoreFiles: ['public/static/*.css']
|
ignoreFiles: ['public/static/*.css', 'src/components/iconfont/iconfont.css']
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,5 +31,6 @@ export default function createProxyTable(target?: string) {
|
|||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
return proxyTable;
|
return proxyTable;
|
||||||
}
|
}
|
||||||
|
|||||||
+99
-62
@@ -140,15 +140,6 @@ const baseRoutes = [
|
|||||||
access: 'canSeeOrgAdmin',
|
access: 'canSeeOrgAdmin',
|
||||||
component: './model-routes/index'
|
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',
|
name: 'providers',
|
||||||
path: '/models/providers',
|
path: '/models/providers',
|
||||||
@@ -179,6 +170,26 @@ const baseRoutes = [
|
|||||||
access: 'canSeeOrgAdmin',
|
access: 'canSeeOrgAdmin',
|
||||||
hideInMenu: true,
|
hideInMenu: true,
|
||||||
component: './benchmark/details'
|
component: './benchmark/details'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'backendsList',
|
||||||
|
path: '/models/backends',
|
||||||
|
key: 'backendsList',
|
||||||
|
icon: 'icon-backend',
|
||||||
|
selectedIcon: 'icon-backend-filled',
|
||||||
|
defaultIcon: 'icon-backend',
|
||||||
|
access: 'canSeeOrgAdmin',
|
||||||
|
component: './backends/index'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'modelfiles',
|
||||||
|
path: '/models/modelfiles',
|
||||||
|
key: 'modelfiles',
|
||||||
|
icon: 'icon-files',
|
||||||
|
selectedIcon: 'icon-files-filled',
|
||||||
|
defaultIcon: 'icon-files',
|
||||||
|
access: 'canSeeOrgAdmin',
|
||||||
|
component: './resources/components/model-files'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -186,6 +197,7 @@ const baseRoutes = [
|
|||||||
name: 'gpuService',
|
name: 'gpuService',
|
||||||
path: '/gpu-service',
|
path: '/gpu-service',
|
||||||
key: 'gpuService',
|
key: 'gpuService',
|
||||||
|
access: 'canSeeGpuService',
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/gpu-service',
|
path: '/gpu-service',
|
||||||
@@ -223,7 +235,12 @@ const baseRoutes = [
|
|||||||
path: '/gpu-service/storage-types',
|
path: '/gpu-service/storage-types',
|
||||||
key: 'gpuServiceStorageTypes',
|
key: 'gpuServiceStorageTypes',
|
||||||
icon: 'icon-storage-outlined',
|
icon: 'icon-storage-outlined',
|
||||||
access: 'canSeeAdmin',
|
// Storage types are tenant-scoped on the backend (Org owners
|
||||||
|
// can create/list their own), so the menu shouldn't be
|
||||||
|
// platform-admin-only. ``canSeeOrgAdmin`` keeps the gate at
|
||||||
|
// "admin or current-org owner" — Org members still don't see
|
||||||
|
// it, which matches the read/write model in the route.
|
||||||
|
access: 'canSeeOrgAdmin',
|
||||||
selectedIcon: 'icon-storage-filled',
|
selectedIcon: 'icon-storage-filled',
|
||||||
defaultIcon: 'icon-storage-outlined',
|
defaultIcon: 'icon-storage-outlined',
|
||||||
component: './gpu-service/storage-types'
|
component: './gpu-service/storage-types'
|
||||||
@@ -249,6 +266,16 @@ const baseRoutes = [
|
|||||||
path: '/resources',
|
path: '/resources',
|
||||||
redirect: '/resources/workers'
|
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',
|
name: 'workers',
|
||||||
path: '/resources/workers',
|
path: '/resources/workers',
|
||||||
@@ -268,67 +295,63 @@ const baseRoutes = [
|
|||||||
component: './resources/components/gpus'
|
component: './resources/components/gpus'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'backendsList',
|
name: 'credentials',
|
||||||
path: '/resources/backends',
|
path: '/resources/credentials',
|
||||||
key: 'backendsList',
|
key: 'credentials',
|
||||||
icon: 'icon-backend',
|
icon: 'icon-credential-outline',
|
||||||
selectedIcon: 'icon-backend-filled',
|
selectedIcon: 'icon-credential-filled',
|
||||||
defaultIcon: 'icon-backend',
|
defaultIcon: 'icon-credential-outline',
|
||||||
access: 'canSeeOrgAdmin',
|
component: './cluster-management/credentials'
|
||||||
component: './backends/index'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'modelfiles',
|
|
||||||
path: '/resources/modelfiles',
|
|
||||||
key: 'modelfiles',
|
|
||||||
icon: 'icon-files',
|
|
||||||
selectedIcon: 'icon-files-filled',
|
|
||||||
defaultIcon: 'icon-files',
|
|
||||||
component: './resources/components/model-files'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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: 'clusterDetail',
|
name: 'clusterDetail',
|
||||||
path: '/cluster-management/clusters/detail',
|
path: '/resources/clusters/detail',
|
||||||
key: 'clusterDetail',
|
key: 'clusterDetail',
|
||||||
icon: 'icon-cluster2-outline',
|
icon: 'icon-cluster2-outline',
|
||||||
selectedIcon: 'icon-cluster2-filled',
|
selectedIcon: 'icon-cluster2-filled',
|
||||||
defaultIcon: 'icon-cluster2-outline',
|
defaultIcon: 'icon-cluster2-outline',
|
||||||
hideInMenu: true,
|
hideInMenu: true,
|
||||||
component: './cluster-management/cluster-detail'
|
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',
|
name: 'usage',
|
||||||
path: '/cluster-management/credentials',
|
path: '/usage/overview',
|
||||||
key: 'credentials',
|
key: 'usage',
|
||||||
icon: 'icon-credential-outline',
|
icon: 'icon-usage-outlined',
|
||||||
selectedIcon: 'icon-credential-filled',
|
selectedIcon: 'icon-usage-filled',
|
||||||
defaultIcon: 'icon-credential-outline',
|
defaultIcon: 'icon-usage-outlined',
|
||||||
component: './cluster-management/credentials'
|
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'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -341,6 +364,20 @@ const baseRoutes = [
|
|||||||
path: '/access-control',
|
path: '/access-control',
|
||||||
redirect: '/access-control/users'
|
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',
|
name: 'users',
|
||||||
path: '/access-control/users',
|
path: '/access-control/users',
|
||||||
@@ -374,8 +411,8 @@ const baseRoutes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'profile',
|
name: 'profile',
|
||||||
path: '/profile',
|
path: '/preferences',
|
||||||
key: 'profile',
|
key: 'preferences',
|
||||||
hideInMenu: true,
|
hideInMenu: true,
|
||||||
component: './profile',
|
component: './profile',
|
||||||
icon: 'User'
|
icon: 'User'
|
||||||
|
|||||||
+24
-10
@@ -1,13 +1,27 @@
|
|||||||
const child_process = require('child_process');
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
export const getBranchInfo = () => {
|
export const getBranchInfo = () => {
|
||||||
const latestCommit = child_process
|
// git may be absent (source archive, bare container) or this tree may
|
||||||
.execSync('git rev-parse HEAD')
|
// not be a git checkout. Swallow the failure and fall back to the env
|
||||||
.toString()
|
// overrides below — losing build info shouldn't fail the build.
|
||||||
.trim();
|
let latestCommit = '';
|
||||||
const versionTag = child_process
|
let versionTag = '';
|
||||||
.execSync(`git tag --contains ${latestCommit}`)
|
try {
|
||||||
.toString()
|
latestCommit = execSync('git rev-parse HEAD').toString().trim();
|
||||||
.trim();
|
versionTag = execSync(`git tag --contains ${latestCommit}`)
|
||||||
return { version: versionTag || '', commitId: latestCommit.slice(0, 7) };
|
.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)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-1
@@ -14,7 +14,8 @@ export default defineConfig([
|
|||||||
'dist',
|
'dist',
|
||||||
'src/.umi/',
|
'src/.umi/',
|
||||||
'src/.umi-production/',
|
'src/.umi-production/',
|
||||||
'src/.umi-test/'
|
'src/.umi-test/',
|
||||||
|
'src/components/iconfont/'
|
||||||
]),
|
]),
|
||||||
{
|
{
|
||||||
files: ['**/*.{ts,tsx,js,jsx}'],
|
files: ['**/*.{ts,tsx,js,jsx}'],
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
"@ant-design/pro-components": "3.1.0-0",
|
"@ant-design/pro-components": "3.1.0-0",
|
||||||
"@antv/g6": "^5.0.51",
|
"@antv/g6": "^5.0.51",
|
||||||
"@braintree/sanitize-url": "^7.1.1",
|
"@braintree/sanitize-url": "^7.1.1",
|
||||||
"@gpustack/core-ui": "^1.0.16",
|
"@gpustack/core-ui": "^1.0.27",
|
||||||
"@huggingface/gguf": "^0.1.7",
|
"@huggingface/gguf": "^0.1.7",
|
||||||
"@huggingface/hub": "^0.15.1",
|
"@huggingface/hub": "^0.15.1",
|
||||||
"@huggingface/tasks": "^0.11.6",
|
"@huggingface/tasks": "^0.11.6",
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ export default (api: IApi) => {
|
|||||||
const info = JSON.parse(process.env.VERSION || '{}');
|
const info = JSON.parse(process.env.VERSION || '{}');
|
||||||
const env = process.env.NODE_ENV;
|
const env = process.env.NODE_ENV;
|
||||||
|
|
||||||
|
$('html').attr('lang', 'en');
|
||||||
|
|
||||||
$('html').attr('data-env', env);
|
$('html').attr('data-env', env);
|
||||||
|
|
||||||
$('html').attr(
|
$('html').attr(
|
||||||
|
|||||||
Generated
+7
-7
@@ -24,8 +24,8 @@ importers:
|
|||||||
specifier: ^7.1.1
|
specifier: ^7.1.1
|
||||||
version: 7.1.2
|
version: 7.1.2
|
||||||
'@gpustack/core-ui':
|
'@gpustack/core-ui':
|
||||||
specifier: ^1.0.16
|
specifier: ^1.0.27
|
||||||
version: 1.0.16(czdvzceysqw7iv6pct2ucnb23e)
|
version: 1.0.27(czdvzceysqw7iv6pct2ucnb23e)
|
||||||
'@huggingface/gguf':
|
'@huggingface/gguf':
|
||||||
specifier: ^0.1.7
|
specifier: ^0.1.7
|
||||||
version: 0.1.18
|
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}
|
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
|
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.16':
|
'@gpustack/core-ui@1.0.27':
|
||||||
resolution: {integrity: sha512-wFKDv7X0FXRmAmZPt4WKYV0+aGHcx82/3EZSJkrut/49XQd5XdrqKaimGtlFWFCNEWqsouwzs4uqaC7JFqfOkQ==, tarball: https://registry.npmjs.org/@gpustack/core-ui/-/core-ui-1.0.16.tgz}
|
resolution: {integrity: sha512-m3ue0EHFKULla0mpnpxZwn9LVaGKS+HnuzQYSBECQa4vaP8MEEQsR7oFBtG8bhWUWYA9VzP6k7fjozDkP6TymA==, tarball: https://registry.npmjs.org/@gpustack/core-ui/-/core-ui-1.0.27.tgz}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@ant-design/icons': '>=6.0.0'
|
'@ant-design/icons': '>=6.0.0'
|
||||||
'@ant-design/pro-components': 3.1.0-0
|
'@ant-design/pro-components': 3.1.0-0
|
||||||
@@ -10808,7 +10808,7 @@ snapshots:
|
|||||||
|
|
||||||
'@formatjs/intl-utils@2.3.0': {}
|
'@formatjs/intl-utils@2.3.0': {}
|
||||||
|
|
||||||
'@gpustack/core-ui@1.0.16(czdvzceysqw7iv6pct2ucnb23e)':
|
'@gpustack/core-ui@1.0.27(czdvzceysqw7iv6pct2ucnb23e)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ant-design/icons': 6.2.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
'@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)
|
'@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)
|
||||||
@@ -11829,7 +11829,7 @@ snapshots:
|
|||||||
|
|
||||||
'@types/history@5.0.0':
|
'@types/history@5.0.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
history: 4.10.1
|
history: 5.3.0
|
||||||
|
|
||||||
'@types/hoist-non-react-statics@3.3.7(@types/react@18.3.28)':
|
'@types/hoist-non-react-statics@3.3.7(@types/react@18.3.28)':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -11915,7 +11915,7 @@ snapshots:
|
|||||||
'@types/history': 4.7.11
|
'@types/history': 4.7.11
|
||||||
'@types/react': 18.3.29
|
'@types/react': 18.3.29
|
||||||
'@types/react-router': 5.1.20
|
'@types/react-router': 5.1.20
|
||||||
redux: 3.7.2
|
redux: 4.2.1
|
||||||
|
|
||||||
'@types/react-router@5.1.20':
|
'@types/react-router@5.1.20':
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
+22
-1
@@ -1,6 +1,10 @@
|
|||||||
import { applyAccessExtensions } from './access.extensions';
|
import { applyAccessExtensions } from './access.extensions';
|
||||||
|
|
||||||
export default (initialState: { currentUser?: Global.UserInfo }) => {
|
export default (initialState: {
|
||||||
|
currentUser?: Global.UserInfo;
|
||||||
|
hasKubernetesCluster?: boolean;
|
||||||
|
hasResourceEvents?: boolean;
|
||||||
|
}) => {
|
||||||
const isPlatformAdmin = !!(
|
const isPlatformAdmin = !!(
|
||||||
initialState &&
|
initialState &&
|
||||||
initialState.currentUser &&
|
initialState.currentUser &&
|
||||||
@@ -11,6 +15,16 @@ export default (initialState: { currentUser?: Global.UserInfo }) => {
|
|||||||
initialState.currentUser &&
|
initialState.currentUser &&
|
||||||
!initialState.currentUser.is_admin
|
!initialState.currentUser.is_admin
|
||||||
);
|
);
|
||||||
|
// GPU Service is Kubernetes-only. We only gate visibility down when
|
||||||
|
// the probe in `getInitialState` came back with a definitive answer;
|
||||||
|
// `undefined` (probe failed / not yet ready) collapses to the
|
||||||
|
// 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:
|
// Predicate roles, top-down by strictness:
|
||||||
// * `canSeeAdmin` — strictly platform admin (`users.is_admin`).
|
// * `canSeeAdmin` — strictly platform admin (`users.is_admin`).
|
||||||
@@ -18,6 +32,11 @@ export default (initialState: { currentUser?: Global.UserInfo }) => {
|
|||||||
// * `canSeeOrgAdmin` — admin-style menus that work cross-org
|
// * `canSeeOrgAdmin` — admin-style menus that work cross-org
|
||||||
// (Dashboard, Resources, Models, Cluster Management). Defaults
|
// (Dashboard, Resources, Models, Cluster Management). Defaults
|
||||||
// to platform admin; extensions widen to include org admins.
|
// to platform admin; extensions widen to include org admins.
|
||||||
|
// * `canSeeGpuService` — GPU Service menu. Anyone allowed to
|
||||||
|
// manage clusters (admins, Org owners) sees it; non-admins fall
|
||||||
|
// through to "show only if a Kubernetes cluster is actually
|
||||||
|
// reachable" so Org members without scheduling access don't see
|
||||||
|
// a dead-end menu item.
|
||||||
// * `canManageCurrentOrg` — pages that only make sense inside a
|
// * `canManageCurrentOrg` — pages that only make sense inside a
|
||||||
// specific org context (member / group management). Defaults to
|
// specific org context (member / group management). Defaults to
|
||||||
// `false`; extensions widen when both an org is selected AND
|
// `false`; extensions widen when both an org is selected AND
|
||||||
@@ -27,6 +46,8 @@ export default (initialState: { currentUser?: Global.UserInfo }) => {
|
|||||||
return applyAccessExtensions({
|
return applyAccessExtensions({
|
||||||
canSeeAdmin: isPlatformAdmin,
|
canSeeAdmin: isPlatformAdmin,
|
||||||
canSeeOrgAdmin: isPlatformAdmin,
|
canSeeOrgAdmin: isPlatformAdmin,
|
||||||
|
canSeeGpuService:
|
||||||
|
isPlatformAdmin || hasKubernetesCluster !== false || hasResourceEvents,
|
||||||
canManageCurrentOrg: false,
|
canManageCurrentOrg: false,
|
||||||
canSeeUser,
|
canSeeUser,
|
||||||
canDelete: true,
|
canDelete: true,
|
||||||
|
|||||||
+123
-3
@@ -1,8 +1,12 @@
|
|||||||
import { userSettingsHelperAtom } from '@/atoms/settings';
|
import { userSettingsHelperAtom } from '@/atoms/settings';
|
||||||
import { GPUStackVersionAtom, UpdateCheckAtom } from '@/atoms/user';
|
import { GPUStackVersionAtom, UpdateCheckAtom, userAtom } from '@/atoms/user';
|
||||||
import { setAtomStorage } from '@/atoms/utils';
|
import { setAtomStorage } from '@/atoms/utils';
|
||||||
import { DEFAULT_ENTER_PAGE, GPUSTACK_API_BASE_URL } from '@/config/settings';
|
import { DEFAULT_ENTER_PAGE, GPUSTACK_API_BASE_URL } from '@/config/settings';
|
||||||
import { COLOR_PRIMARY } from '@/config/theme/constants';
|
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 { enterprisePluginReady } from '@/plugins/enterprise-ready';
|
||||||
import { GPUStackPluginManager } from '@/plugins/manager';
|
import { GPUStackPluginManager } from '@/plugins/manager';
|
||||||
import { requestConfig } from '@/request-config';
|
import { requestConfig } from '@/request-config';
|
||||||
@@ -13,6 +17,7 @@ import {
|
|||||||
} from '@/services/profile/apis';
|
} from '@/services/profile/apis';
|
||||||
import { fetchSystemConfig } from '@/services/system/query-system-config';
|
import { fetchSystemConfig } from '@/services/system/query-system-config';
|
||||||
import { isOnline } from '@/utils';
|
import { isOnline } from '@/utils';
|
||||||
|
import { installTenantFetch } from '@/utils/install-fetch';
|
||||||
import {
|
import {
|
||||||
IS_FIRST_LOGIN,
|
IS_FIRST_LOGIN,
|
||||||
readState,
|
readState,
|
||||||
@@ -22,6 +27,8 @@ import '@gpustack/core-ui/style.css';
|
|||||||
import { RequestConfig, history, request as umiRequest } from '@umijs/max';
|
import { RequestConfig, history, request as umiRequest } from '@umijs/max';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
|
|
||||||
|
installTenantFetch();
|
||||||
|
|
||||||
// only for the first login and access from http://localhost
|
// only for the first login and access from http://localhost
|
||||||
|
|
||||||
const checkDefaultPage = async (userInfo: any) => {
|
const checkDefaultPage = async (userInfo: any) => {
|
||||||
@@ -34,11 +41,87 @@ const checkDefaultPage = async (userInfo: any) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Probes the caller's cluster list once so access predicates can gate
|
||||||
|
// GPU Service (Kubernetes-only). Cheap (one list request) and never
|
||||||
|
// blocks login — any failure just falls back to `undefined`, which
|
||||||
|
// the predicate treats as "unknown / don't restrict beyond role".
|
||||||
|
// The result is also mirrored into sessionStorage so access extensions
|
||||||
|
// that run without the initialState argument can read it (e.g. to
|
||||||
|
// override the admin shortcut in scopes where the menu shouldn't
|
||||||
|
// show even for admins).
|
||||||
|
const HAS_K8S_CLUSTER_KEY = 'hasKubernetesCluster';
|
||||||
|
const probeHasKubernetesCluster = async (): Promise<boolean | undefined> => {
|
||||||
|
try {
|
||||||
|
const res = await queryClusterList(
|
||||||
|
{ page: -1 },
|
||||||
|
{
|
||||||
|
skipErrorHandler: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const value = (res?.items ?? []).some(
|
||||||
|
(c) => c?.provider === ProviderValueMap.Kubernetes
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
window.sessionStorage.setItem(HAS_K8S_CLUSTER_KEY, JSON.stringify(value));
|
||||||
|
} catch {
|
||||||
|
// sessionStorage may be unavailable (Safari private mode); the
|
||||||
|
// access predicate already handles a missing value as "unknown".
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('probeHasKubernetesCluster error', error);
|
||||||
|
try {
|
||||||
|
window.sessionStorage.removeItem(HAS_K8S_CLUSTER_KEY);
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return 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
|
// runtime configuration
|
||||||
export async function getInitialState(): Promise<{
|
export async function getInitialState(): Promise<{
|
||||||
fetchUserInfo: () => Promise<Global.UserInfo>;
|
fetchUserInfo: () => Promise<Global.UserInfo>;
|
||||||
currentUser?: Global.UserInfo;
|
currentUser?: Global.UserInfo;
|
||||||
pluginData?: Record<string, any>;
|
pluginData?: Record<string, any>;
|
||||||
|
hasKubernetesCluster?: boolean;
|
||||||
|
hasResourceEvents?: boolean;
|
||||||
}> {
|
}> {
|
||||||
const { location } = history;
|
const { location } = history;
|
||||||
|
|
||||||
@@ -83,6 +166,36 @@ export async function getInitialState(): Promise<{
|
|||||||
getUpdateCheck();
|
getUpdateCheck();
|
||||||
fetchSystemConfig();
|
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;
|
return data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const data = error?.response?.data;
|
const data = error?.response?.data;
|
||||||
@@ -122,12 +235,19 @@ export async function getInitialState(): Promise<{
|
|||||||
getAppVersionInfo();
|
getAppVersionInfo();
|
||||||
|
|
||||||
if (![DEFAULT_ENTER_PAGE.login].includes(location.pathname)) {
|
if (![DEFAULT_ENTER_PAGE.login].includes(location.pathname)) {
|
||||||
const userInfo = await fetchUserInfo();
|
const [userInfo, hasKubernetesCluster, hasResourceEvents] =
|
||||||
|
await Promise.all([
|
||||||
|
fetchUserInfo(),
|
||||||
|
probeHasKubernetesCluster(),
|
||||||
|
probeHasResourceEvents()
|
||||||
|
]);
|
||||||
checkDefaultPage(userInfo);
|
checkDefaultPage(userInfo);
|
||||||
return {
|
return {
|
||||||
fetchUserInfo,
|
fetchUserInfo,
|
||||||
currentUser: userInfo,
|
currentUser: userInfo,
|
||||||
pluginData
|
pluginData,
|
||||||
|
hasKubernetesCluster,
|
||||||
|
hasResourceEvents
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
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 {
|
.ant-layout-sider-children {
|
||||||
border-inline: none;
|
border-inline: none;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
padding-inline-end: 0;
|
||||||
padding-block-end: 8px;
|
padding-block-end: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,13 @@ export const fromClusterCreationAtom = atom(false);
|
|||||||
export const clusterSessionAtom = atom<{
|
export const clusterSessionAtom = atom<{
|
||||||
firstAddWorker: boolean;
|
firstAddWorker: boolean;
|
||||||
firstAddCluster: 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
|
||||||
|
// "Add Cluster" button skips provider catalog and lands on the
|
||||||
|
// K8s configure step). Consumed once by ClusterCreate on mount.
|
||||||
|
providerHint?: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
export const clusterDetailAtom = atom<ClusterListItem | null>(null);
|
export const clusterDetailAtom = atom<ClusterListItem | null>(null);
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import { ClusterListItem } from '@/pages/cluster-management/config/types';
|
|
||||||
import { atom } from 'jotai';
|
|
||||||
|
|
||||||
export const currentClusterAtom = atom<
|
|
||||||
(Partial<ClusterListItem> & { label?: string; value?: number }) | null
|
|
||||||
>(null);
|
|
||||||
|
|
||||||
export const addSSHKeyPageAtom = atom<{ create: boolean }>({
|
|
||||||
create: false
|
|
||||||
});
|
|
||||||
@@ -55,3 +55,10 @@ export const userSettingsHelperAtom = atom(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
export const hideModalTemporarilyAtom = atom<boolean>(false);
|
export const hideModalTemporarilyAtom = atom<boolean>(false);
|
||||||
|
|
||||||
|
export const collapsedMenuGroupsAtom = atomWithStorage<string[]>(
|
||||||
|
'collapsedMenuGroups',
|
||||||
|
[],
|
||||||
|
undefined,
|
||||||
|
{ getOnInit: true }
|
||||||
|
);
|
||||||
|
|||||||
+50
-10
@@ -3,6 +3,15 @@ import { atomWithStorage } from 'jotai/utils';
|
|||||||
|
|
||||||
export const userAtom = atomWithStorage<any>('userInfo', null);
|
export const userAtom = atomWithStorage<any>('userInfo', null);
|
||||||
|
|
||||||
|
// Backs the `currentOrganizationId` localStorage key. Stays null in
|
||||||
|
// builds with no Org context (single-tenant), and is shared with any
|
||||||
|
// extension that persists the same key so both sides stay in sync
|
||||||
|
// without one side having to import from the other.
|
||||||
|
export const currentOrganizationIdAtom = atomWithStorage<number | null>(
|
||||||
|
'currentOrganizationId',
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
export const GPUStackVersionAtom = atom<{
|
export const GPUStackVersionAtom = atom<{
|
||||||
version: string;
|
version: string;
|
||||||
git_commit: string;
|
git_commit: string;
|
||||||
@@ -23,10 +32,7 @@ export const UpdateCheckAtom = atom<{
|
|||||||
latest_version: ''
|
latest_version: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
export const initialPasswordAtom = atomWithStorage<string>(
|
export const initialPasswordAtom = atom<string>('');
|
||||||
'initialPassword',
|
|
||||||
''
|
|
||||||
);
|
|
||||||
|
|
||||||
// Namespace the server creates for an Org's resources on each Kubernetes
|
// Namespace the server creates for an Org's resources on each Kubernetes
|
||||||
// cluster. The format must match the backend's ``get_namespace_name``
|
// cluster. The format must match the backend's ``get_namespace_name``
|
||||||
@@ -81,23 +87,57 @@ const getStoredCurrentOrgId = (): number | null => {
|
|||||||
// cluster-owner fallback.
|
// cluster-owner fallback.
|
||||||
const ORG_CACHE_KEYS = ['organizationList', 'allOrganizations'] as const;
|
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;
|
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);
|
const target = String(id);
|
||||||
for (const key of ORG_CACHE_KEYS) {
|
for (const key of ORG_CACHE_KEYS) {
|
||||||
try {
|
try {
|
||||||
const raw = localStorage.getItem(key);
|
const raw = localStorage.getItem(key);
|
||||||
if (!raw) continue;
|
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;
|
if (!Array.isArray(list)) continue;
|
||||||
const match = list.find((item) => String(item?.id) === target);
|
const match = list.find((item) => String(item?.id) === target);
|
||||||
if (match?.name) return `gpustack-${match.name}`;
|
if (match) return match;
|
||||||
} catch {
|
} catch {
|
||||||
// ignore malformed cache; continue checking other keys
|
// ignore malformed cache; continue checking other keys
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Vendored
+6
@@ -92,6 +92,12 @@ declare namespace Global {
|
|||||||
interface InitialStateType {
|
interface InitialStateType {
|
||||||
fetchUserInfo: () => Promise<UserInfo>;
|
fetchUserInfo: () => Promise<UserInfo>;
|
||||||
currentUser?: UserInfo;
|
currentUser?: UserInfo;
|
||||||
|
// Captured at app boot so access predicates can gate GPU Service —
|
||||||
|
// the feature is Kubernetes-only, and Org members without a K8s
|
||||||
|
// cluster they can schedule on shouldn't see the menu. Refreshed
|
||||||
|
// by full page reload (e.g. OrgSwitcher) which re-runs
|
||||||
|
// getInitialState.
|
||||||
|
hasKubernetesCluster?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SearchParams = Pagination & { search?: string; [key: string]: any };
|
type SearchParams = Pagination & { search?: string; [key: string]: any };
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ export default {
|
|||||||
itemHoverColor: 'rgba(0,0,0,1)',
|
itemHoverColor: 'rgba(0,0,0,1)',
|
||||||
itemColor: 'rgba(0,0,0,1)',
|
itemColor: 'rgba(0,0,0,1)',
|
||||||
itemHoverBg: 'rgb(24 25 27)',
|
itemHoverBg: 'rgb(24 25 27)',
|
||||||
itemActiveBg: 'rgb(24 25 27)'
|
itemActiveBg: 'rgb(24 25 27)',
|
||||||
|
menuItemSelectedBg: '#292929'
|
||||||
},
|
},
|
||||||
Progress: {
|
Progress: {
|
||||||
lineBorderRadius: 4
|
lineBorderRadius: 4
|
||||||
@@ -108,6 +109,7 @@ export default {
|
|||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
motion: true,
|
motion: true,
|
||||||
colorFill: '#0A0A0A',
|
colorFill: '#0A0A0A',
|
||||||
colorBgBase: '#0A0A0A'
|
colorBgBase: '#0A0A0A',
|
||||||
|
menuItemSelectedBg: '#292929'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ export default {
|
|||||||
itemHoverColor: 'rgba(0,0,0,1)',
|
itemHoverColor: 'rgba(0,0,0,1)',
|
||||||
itemColor: 'rgba(0,0,0,1)',
|
itemColor: 'rgba(0,0,0,1)',
|
||||||
itemHoverBg: 'rgba(0,0,0,0.04)',
|
itemHoverBg: 'rgba(0,0,0,0.04)',
|
||||||
itemActiveBg: 'rgba(0,0,0,0.04)'
|
itemActiveBg: 'rgba(0,0,0,0.04)',
|
||||||
|
menuItemSelectedBg: '#e8eaed'
|
||||||
},
|
},
|
||||||
Progress: {
|
Progress: {
|
||||||
lineBorderRadius: 3
|
lineBorderRadius: 3
|
||||||
|
|||||||
+16
-37
@@ -171,8 +171,6 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ant-table .ant-table-container table {
|
.ant-table .ant-table-container table {
|
||||||
// border-spacing: 0 20px;
|
|
||||||
|
|
||||||
.ant-table-thead th.ant-table-column-sort {
|
.ant-table-thead th.ant-table-column-sort {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
@@ -241,8 +239,8 @@ body {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.ant-pro-sider-footer {
|
.ant-pro-sider-footer {
|
||||||
padding-block: 2px 0;
|
padding-block: 4px 0;
|
||||||
padding-left: 6px;
|
padding-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-pro-sider-actions-list-item {
|
.ant-pro-sider-actions-list-item {
|
||||||
@@ -259,46 +257,26 @@ body {
|
|||||||
border-block-end: none;
|
border-block-end: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-pro-sider-logo-collapsed {
|
.ant-pro-sider-logo {
|
||||||
padding-left: 12px;
|
.collapse-btn {
|
||||||
cursor: e-resize;
|
color: var(--ant-color-text-tertiary);
|
||||||
|
:hover {
|
||||||
.collapse-wrap {
|
color: var(--ant-color-text);
|
||||||
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 .ant-layout-sider-children {
|
.ant-pro-sider-logo-collapsed {
|
||||||
// border-right: 1px solid var(--ant-color-split);
|
padding-left: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-table-content table {
|
.ant-table .ant-table-tbody {
|
||||||
.ant-table-tbody {
|
.ant-table-row {
|
||||||
.ant-table-row {
|
border-radius: var(--table-td-radius);
|
||||||
border-radius: var(--table-td-radius);
|
|
||||||
|
|
||||||
> td {
|
> td {
|
||||||
background-color: unset;
|
border-bottom: 1px solid var(--ant-color-split);
|
||||||
border-bottom: 1px solid var(--ant-color-split);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,7 +325,6 @@ body {
|
|||||||
.ant-pro-layout-container {
|
.ant-pro-layout-container {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
// background-color: var(--ant-color-bg-container);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-pro-sider {
|
.ant-pro-sider {
|
||||||
@@ -812,6 +789,8 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ant-pro-sider-logo + div {
|
.ant-pro-sider-logo + div {
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export function useQueryDataList<
|
|||||||
Response = Array<ListItem>
|
Response = Array<ListItem>
|
||||||
>(option: {
|
>(option: {
|
||||||
key: string;
|
key: string;
|
||||||
|
manual?: boolean;
|
||||||
responseType?: 'array' | 'object';
|
responseType?: 'array' | 'object';
|
||||||
fetchList: (
|
fetchList: (
|
||||||
params: Params,
|
params: Params,
|
||||||
@@ -38,6 +39,7 @@ export function useQueryDataList<
|
|||||||
fetchList,
|
fetchList,
|
||||||
getLabel,
|
getLabel,
|
||||||
getValue,
|
getValue,
|
||||||
|
manual = true,
|
||||||
responseType = 'array',
|
responseType = 'array',
|
||||||
errorMsg
|
errorMsg
|
||||||
} = option;
|
} = option;
|
||||||
@@ -70,7 +72,7 @@ export function useQueryDataList<
|
|||||||
return responseType === 'array' ? res.items || [] : res;
|
return responseType === 'array' ? res.items || [] : res;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
manual: true,
|
manual: manual,
|
||||||
debounceWait: option.debounceWait || 300,
|
debounceWait: option.debounceWait || 300,
|
||||||
onSuccess: () => {},
|
onSuccess: () => {},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
@@ -105,16 +107,18 @@ export function useQueryDataList<
|
|||||||
export function useQueryData<Detail, Params = any>(option: {
|
export function useQueryData<Detail, Params = any>(option: {
|
||||||
key: string;
|
key: string;
|
||||||
delay?: number;
|
delay?: number;
|
||||||
|
manual?: boolean;
|
||||||
fetchDetail: (params: Params, options?: any) => Promise<Detail>;
|
fetchDetail: (params: Params, options?: any) => Promise<Detail>;
|
||||||
getData?: (response: Detail, params?: any) => any;
|
getData?: (response: Detail, params?: any) => any;
|
||||||
errorMsg?: string;
|
errorMsg?: string;
|
||||||
}): {
|
}): {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
detailData: Detail;
|
detailData: Detail;
|
||||||
|
manual?: boolean;
|
||||||
cancelRequest: () => void;
|
cancelRequest: () => void;
|
||||||
fetchData: (params: Params, extra?: any) => Promise<Detail>;
|
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 axiosTokenRef = useRef<CancelTokenSource | null>(null);
|
||||||
const [detailData, setDetailData] = useState<Detail>({} as Detail);
|
const [detailData, setDetailData] = useState<Detail>({} as Detail);
|
||||||
|
|
||||||
@@ -142,7 +146,7 @@ export function useQueryData<Detail, Params = any>(option: {
|
|||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
manual: true,
|
manual: manual,
|
||||||
onSuccess: () => {},
|
onSuccess: () => {},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
message.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];
|
key?: (typeof PaginationKey)[keyof typeof PaginationKey];
|
||||||
fetchAPI: (params: any, options?: any) => Promise<Global.PageResponse<T>>;
|
fetchAPI: (params: any, options?: any) => Promise<Global.PageResponse<T>>;
|
||||||
deleteAPI?: (id: number, params?: any) => Promise<any>;
|
deleteAPI?: (id: number, params?: any) => Promise<any>;
|
||||||
|
afterDelete?: (id?: number | number[]) => void;
|
||||||
contentForDelete?: string;
|
contentForDelete?: string;
|
||||||
defaultData?: any[];
|
defaultData?: any[];
|
||||||
events?: EventsType[];
|
events?: EventsType[];
|
||||||
@@ -48,6 +49,7 @@ export default function useTableFetch<T>(
|
|||||||
const {
|
const {
|
||||||
fetchAPI,
|
fetchAPI,
|
||||||
deleteAPI,
|
deleteAPI,
|
||||||
|
afterDelete,
|
||||||
contentForDelete,
|
contentForDelete,
|
||||||
API,
|
API,
|
||||||
polling = false,
|
polling = false,
|
||||||
@@ -363,7 +365,7 @@ export default function useTableFetch<T>(
|
|||||||
|
|
||||||
// remove the deleted id from selected ids in row selection
|
// remove the deleted id from selected ids in row selection
|
||||||
rowSelection.removeSelectedKeys([row.id]);
|
rowSelection.removeSelectedKeys([row.id]);
|
||||||
|
afterDelete?.(row.id);
|
||||||
// ======== to avoid fetch data twice, because of debounceFetchData has been run =======
|
// ======== to avoid fetch data twice, because of debounceFetchData has been run =======
|
||||||
if (!updateManually) {
|
if (!updateManually) {
|
||||||
fetchData();
|
fetchData();
|
||||||
@@ -390,6 +392,7 @@ export default function useTableFetch<T>(
|
|||||||
successIds.push(id);
|
successIds.push(id);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
afterDelete?.(successIds);
|
||||||
rowSelection.removeSelectedKeys(successIds);
|
rowSelection.removeSelectedKeys(successIds);
|
||||||
fetchData();
|
fetchData();
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ type EventsType = 'CREATE' | 'UPDATE' | 'DELETE' | 'INSERT';
|
|||||||
export function useUpdateChunkedList(options: {
|
export function useUpdateChunkedList(options: {
|
||||||
events?: EventsType[];
|
events?: EventsType[];
|
||||||
dataList?: any[];
|
dataList?: any[];
|
||||||
triggerAt?: React.MutableRefObject<number>;
|
|
||||||
limit?: number;
|
limit?: number;
|
||||||
onCreate?: (args: any) => void;
|
onCreate?: (args: any) => void;
|
||||||
onUpdate?: (args: any) => void;
|
onUpdate?: (args: any) => void;
|
||||||
@@ -24,9 +23,9 @@ export function useUpdateChunkedList(options: {
|
|||||||
filterFun?: (args: any) => boolean;
|
filterFun?: (args: any) => boolean;
|
||||||
mapFun?: (args: any) => any;
|
mapFun?: (args: any) => any;
|
||||||
computedID?: (d: object) => string;
|
computedID?: (d: object) => string;
|
||||||
|
isNewItem?: (item: any) => boolean;
|
||||||
}) {
|
}) {
|
||||||
const { events = ['CREATE', 'DELETE', 'UPDATE', 'INSERT'], triggerAt } =
|
const { events = ['CREATE', 'DELETE', 'UPDATE', 'INSERT'] } = options;
|
||||||
options;
|
|
||||||
const deletedIdsRef = useRef<Set<number | string>>(new Set());
|
const deletedIdsRef = useRef<Set<number | string>>(new Set());
|
||||||
const cacheDataListRef = useRef<any[]>(options.dataList || []);
|
const cacheDataListRef = useRef<any[]>(options.dataList || []);
|
||||||
const timerRef = useRef<any>(null);
|
const timerRef = useRef<any>(null);
|
||||||
@@ -71,17 +70,14 @@ export function useUpdateChunkedList(options: {
|
|||||||
(sItem: any) => sItem.id === item.id
|
(sItem: any) => sItem.id === item.id
|
||||||
);
|
);
|
||||||
const updateItem = { ...item };
|
const updateItem = { ...item };
|
||||||
if (updateIndex === -1 && !triggerAt?.current) {
|
if (updateIndex === -1) {
|
||||||
acc.push(updateItem);
|
acc.push(updateItem);
|
||||||
} else if (!triggerAt?.current) {
|
} else {
|
||||||
cacheDataListRef.current[updateIndex] = updateItem;
|
cacheDataListRef.current[updateIndex] = updateItem;
|
||||||
}
|
}
|
||||||
|
// only push items created after the watch started
|
||||||
|
|
||||||
// TODO: only push items created after triggerAt
|
if (options.isNewItem?.(item)) {
|
||||||
if (
|
|
||||||
triggerAt?.current &&
|
|
||||||
Date.parse(item.created_at) >= triggerAt.current
|
|
||||||
) {
|
|
||||||
latestCreateList.push(updateItem);
|
latestCreateList.push(updateItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +89,10 @@ export function useUpdateChunkedList(options: {
|
|||||||
...cacheDataListRef.current
|
...cacheDataListRef.current
|
||||||
].slice(0, limit);
|
].slice(0, limit);
|
||||||
|
|
||||||
|
options.setDataList?.([...cacheDataListRef.current], {
|
||||||
|
createdIds: newDataList.map((item) => item.id)
|
||||||
|
});
|
||||||
|
|
||||||
options.onCreate?.(latestCreateList);
|
options.onCreate?.(latestCreateList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,10 +103,8 @@ export function useUpdateChunkedList(options: {
|
|||||||
cacheDataListRef.current = cacheDataListRef.current?.filter(
|
cacheDataListRef.current = cacheDataListRef.current?.filter(
|
||||||
(item: any) => {
|
(item: any) => {
|
||||||
// collect deleted items
|
// collect deleted items
|
||||||
if (triggerAt?.current) {
|
if (ids?.includes(item.id) && !options.isNewItem?.(item)) {
|
||||||
if (ids?.includes(item.id)) {
|
deletedList.push(item);
|
||||||
deletedList.push(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return !ids?.includes(item.id);
|
return !ids?.includes(item.id);
|
||||||
}
|
}
|
||||||
@@ -133,10 +131,8 @@ export function useUpdateChunkedList(options: {
|
|||||||
updateItem,
|
updateItem,
|
||||||
...cacheDataListRef.current.slice(0, limit - 1)
|
...cacheDataListRef.current.slice(0, limit - 1)
|
||||||
];
|
];
|
||||||
if (options.onUpdate && triggerAt?.current) {
|
if (options.onUpdate && options.isNewItem?.(item)) {
|
||||||
if (Date.parse(item.created_at) >= triggerAt.current) {
|
options.onUpdate?.([updateItem]);
|
||||||
options.onUpdate?.([updateItem]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -63,8 +63,7 @@ export default function useWatchList<T = Record<string, any>>(API: string) {
|
|||||||
listRequestTokenRef.current?.cancel?.();
|
listRequestTokenRef.current?.cancel?.();
|
||||||
listRequestTokenRef.current = createAxiosToken();
|
listRequestTokenRef.current = createAxiosToken();
|
||||||
const params = {
|
const params = {
|
||||||
page: 1,
|
page: -1
|
||||||
perPage: 100
|
|
||||||
};
|
};
|
||||||
const res: any = await queryAllDataList(params, {
|
const res: any = await queryAllDataList(params, {
|
||||||
token: listRequestTokenRef.current.token
|
token: listRequestTokenRef.current.token
|
||||||
@@ -86,6 +85,7 @@ export default function useWatchList<T = Record<string, any>>(API: string) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
watchDataList,
|
watchDataList,
|
||||||
|
setWatchDataList,
|
||||||
deleteItemFromCache: handleDeleteItemFromCache
|
deleteItemFromCache: handleDeleteItemFromCache
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { useAtom } from 'jotai';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { DEFAULT_ENTER_PAGE } from '../config/settings';
|
import { DEFAULT_ENTER_PAGE } from '../config/settings';
|
||||||
|
import GithubStar from './github-star';
|
||||||
|
|
||||||
const NewLabel = styled.span`
|
const NewLabel = styled.span`
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -210,14 +211,14 @@ export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
|||||||
key: 'settings',
|
key: 'settings',
|
||||||
label: (
|
label: (
|
||||||
<span className="flex flex-center">
|
<span className="flex flex-center">
|
||||||
<IconFont type="icon-settings-02" />
|
<IconFont type="icon-preferences" />
|
||||||
<span className="m-l-8" style={{ marginLeft: 8 }}>
|
<span className="m-l-8" style={{ marginLeft: 8 }}>
|
||||||
{intl?.formatMessage?.({ id: 'common.button.settings' })}
|
{intl?.formatMessage?.({ id: 'common.preferences' })}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
history.push('/profile');
|
history.push('/preferences');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -260,6 +261,7 @@ export const ExtraContent = (props: { isDarkTheme?: boolean }) => {
|
|||||||
<Wrapper>
|
<Wrapper>
|
||||||
{contextHolder}
|
{contextHolder}
|
||||||
<PluginExtraField name="OrgSwitcher" isDarkTheme={isDarkTheme} />
|
<PluginExtraField name="OrgSwitcher" isDarkTheme={isDarkTheme} />
|
||||||
|
{process.env.ENABLE_ENTERPRISE !== 'true' && <GithubStar />}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
import externalLinks from '@/constants/external-links';
|
||||||
|
import { GithubFilled } from '@ant-design/icons';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Tooltip } from 'antd';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const REPO = 'gpustack/gpustack';
|
||||||
|
const CACHE_KEY = 'gpustack:github-stars';
|
||||||
|
const CACHE_TTL = 24 * 60 * 60 * 1000;
|
||||||
|
const FETCH_TIMEOUT = 4000;
|
||||||
|
|
||||||
|
const StarLink = styled.a`
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: stretch;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: var(--ant-border-radius);
|
||||||
|
border: 1px solid var(--ant-color-border-secondary);
|
||||||
|
background-color: var(--ant-color-bg-container);
|
||||||
|
color: var(--ant-color-text-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
transition:
|
||||||
|
border-color 0.2s,
|
||||||
|
color 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--ant-color-border);
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.seg {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seg + .seg {
|
||||||
|
border-left: 1px solid var(--ant-color-border-secondary);
|
||||||
|
background-color: var(--ant-color-fill-quaternary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.anticon {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.count {
|
||||||
|
font-weight: 500;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
min-width: 1.5em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const formatCount = (n: number): string => {
|
||||||
|
if (n >= 1000) {
|
||||||
|
const k = n / 1000;
|
||||||
|
return k >= 10 ? `${Math.round(k)}k` : `${k.toFixed(1)}k`;
|
||||||
|
}
|
||||||
|
return String(n);
|
||||||
|
};
|
||||||
|
|
||||||
|
type CacheEntry = { value: number; time: number };
|
||||||
|
|
||||||
|
const readCache = (): CacheEntry | null => {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(CACHE_KEY);
|
||||||
|
if (!raw) return null;
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
if (typeof parsed?.value !== 'number' || typeof parsed?.time !== 'number') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const writeCache = (value: number) => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(
|
||||||
|
CACHE_KEY,
|
||||||
|
JSON.stringify({ value, time: Date.now() })
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
// ignore quota errors
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const GithubStar = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const [count, setCount] = useState<number | null>(
|
||||||
|
() => readCache()?.value ?? null
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const cached = readCache();
|
||||||
|
const fresh = cached && Date.now() - cached.time < CACHE_TTL;
|
||||||
|
if (fresh) return;
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT);
|
||||||
|
|
||||||
|
fetch(`https://api.github.com/repos/${REPO}`, { signal: controller.signal })
|
||||||
|
.then((r) => (r.ok ? r.json() : null))
|
||||||
|
.then((data) => {
|
||||||
|
if (!data || typeof data.stargazers_count !== 'number') return;
|
||||||
|
setCount(data.stargazers_count);
|
||||||
|
writeCache(data.stargazers_count);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// offline, blocked, rate-limited — stay hidden if no cache
|
||||||
|
})
|
||||||
|
.finally(() => clearTimeout(timer));
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
controller.abort();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip title={intl.formatMessage({ id: 'common.github.star.tooltip' })}>
|
||||||
|
<StarLink href={externalLinks.github} target="_blank" rel="noreferrer">
|
||||||
|
<span className="seg">
|
||||||
|
<GithubFilled />
|
||||||
|
</span>
|
||||||
|
<span className="seg">
|
||||||
|
<span className="count">
|
||||||
|
{count != null ? formatCount(count) : 'Star'}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</StarLink>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GithubStar;
|
||||||
+22
-37
@@ -1,6 +1,7 @@
|
|||||||
import { routeCacheAtom, setRouteCache } from '@/atoms/route-cache';
|
import { routeCacheAtom, setRouteCache } from '@/atoms/route-cache';
|
||||||
import { userAtom } from '@/atoms/user';
|
import { userAtom } from '@/atoms/user';
|
||||||
import DarkMask from '@/components/dark-mask';
|
import DarkMask from '@/components/dark-mask';
|
||||||
|
import '@/components/iconfont/iconfont.js';
|
||||||
import PluginExtraFields from '@/components/plugin-extra-fields';
|
import PluginExtraFields from '@/components/plugin-extra-fields';
|
||||||
import routeCachekey from '@/config/route-cachekey';
|
import routeCachekey from '@/config/route-cachekey';
|
||||||
import { DEFAULT_ENTER_PAGE, GPUSTACK_API_BASE_URL } from '@/config/settings';
|
import { DEFAULT_ENTER_PAGE, GPUSTACK_API_BASE_URL } from '@/config/settings';
|
||||||
@@ -20,11 +21,7 @@ import {
|
|||||||
import { useAccessMarkedRoutes } from '@@/plugin-access';
|
import { useAccessMarkedRoutes } from '@@/plugin-access';
|
||||||
import { useModel } from '@@/plugin-model';
|
import { useModel } from '@@/plugin-model';
|
||||||
import { ProLayout } from '@ant-design/pro-components';
|
import { ProLayout } from '@ant-design/pro-components';
|
||||||
import {
|
import { CoreUIProvider, IconFont } from '@gpustack/core-ui';
|
||||||
CoreUIProvider,
|
|
||||||
IconFont,
|
|
||||||
useOverlayScroller
|
|
||||||
} from '@gpustack/core-ui';
|
|
||||||
import {
|
import {
|
||||||
Access,
|
Access,
|
||||||
Outlet,
|
Outlet,
|
||||||
@@ -71,9 +68,9 @@ const NO_CONTAINER_PAGES = [
|
|||||||
|
|
||||||
const CHECK_RESOURCE_PATH = [
|
const CHECK_RESOURCE_PATH = [
|
||||||
'/resources/workers',
|
'/resources/workers',
|
||||||
'/cluster-management/clusters/list',
|
'/resources/clusters/list',
|
||||||
'/cluster-management/credentials',
|
'/resources/credentials',
|
||||||
'/cluster-management/clusters/create'
|
'/resources/clusters/create'
|
||||||
];
|
];
|
||||||
|
|
||||||
type NewRoute = IRoute & {
|
type NewRoute = IRoute & {
|
||||||
@@ -134,9 +131,6 @@ const mapRoutes = (routes: IRoute[], role: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default (props: any) => {
|
export default (props: any) => {
|
||||||
const { initialize: initialize } = useOverlayScroller({
|
|
||||||
defer: false
|
|
||||||
});
|
|
||||||
const [, contextHolder] = Modal.useModal();
|
const [, contextHolder] = Modal.useModal();
|
||||||
const { themeData, setUserSettings, userSettings } = useUserSettings();
|
const { themeData, setUserSettings, userSettings } = useUserSettings();
|
||||||
const [userInfo] = useAtom(userAtom);
|
const [userInfo] = useAtom(userAtom);
|
||||||
@@ -240,30 +234,7 @@ export default (props: any) => {
|
|||||||
}, [userSettings.collapsed]);
|
}, [userSettings.collapsed]);
|
||||||
|
|
||||||
const renderMenuHeader = (logo: React.ReactNode, title: React.ReactNode) => {
|
const renderMenuHeader = (logo: React.ReactNode, title: React.ReactNode) => {
|
||||||
return (
|
return <>{logo}</>;
|
||||||
<>
|
|
||||||
{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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const menuContentRender = (menuProps: any, defaultDom: React.ReactNode) => {
|
const menuContentRender = (menuProps: any, defaultDom: React.ReactNode) => {
|
||||||
@@ -356,7 +327,7 @@ export default (props: any) => {
|
|||||||
config={{
|
config={{
|
||||||
apiBaseUrl: GPUSTACK_API_BASE_URL,
|
apiBaseUrl: GPUSTACK_API_BASE_URL,
|
||||||
theme: userSettings.theme,
|
theme: userSettings.theme,
|
||||||
iconUrl: '//at.alicdn.com/t/c/font_4613488_jmdepcs90im.js',
|
iconUrl: '',
|
||||||
isDarkTheme: userSettings.isDarkTheme,
|
isDarkTheme: userSettings.isDarkTheme,
|
||||||
defaultColorPrimary: COLOR_PRIMARY
|
defaultColorPrimary: COLOR_PRIMARY
|
||||||
}}
|
}}
|
||||||
@@ -407,9 +378,23 @@ export default (props: any) => {
|
|||||||
openKeys={false}
|
openKeys={false}
|
||||||
disableMobile={true}
|
disableMobile={true}
|
||||||
siderWidth={220}
|
siderWidth={220}
|
||||||
|
menuFooterRender={() => (
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
border: 'none'
|
||||||
|
}}
|
||||||
|
size="small"
|
||||||
|
type={'text'}
|
||||||
|
onClick={handleToggleCollapse}
|
||||||
|
>
|
||||||
|
<IconFont
|
||||||
|
type={collapsed ? 'icon-expand-left' : 'icon-expand-right'}
|
||||||
|
className="font-size-18"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
onCollapse={onCollapse}
|
onCollapse={onCollapse}
|
||||||
onMenuHeaderClick={onMenuHeaderClick}
|
onMenuHeaderClick={onMenuHeaderClick}
|
||||||
menuHeaderRender={renderMenuHeader}
|
|
||||||
collapsed={userSettings.collapsed}
|
collapsed={userSettings.collapsed}
|
||||||
onPageChange={onPageChange}
|
onPageChange={onPageChange}
|
||||||
formatMessage={formatMessage}
|
formatMessage={formatMessage}
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ export const getRightRenderContent = (opts: {
|
|||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
history.push('/profile');
|
history.push('/preferences');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+187
-158
@@ -1,9 +1,11 @@
|
|||||||
|
import { collapsedMenuGroupsAtom } from '@/atoms/settings';
|
||||||
import { CaretDownOutlined } from '@ant-design/icons';
|
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 { Link, useLocation } from '@umijs/max';
|
||||||
import { Tooltip } from 'antd';
|
import { Tooltip } from 'antd';
|
||||||
import { createStyles } from 'antd-style';
|
import { createStyles, type FullToken } from 'antd-style';
|
||||||
import React, { useMemo, useState } from 'react';
|
import { useAtom } from 'jotai';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
interface MenuItem {
|
interface MenuItem {
|
||||||
icon?: string;
|
icon?: string;
|
||||||
@@ -20,124 +22,143 @@ interface SiderMenuProps {
|
|||||||
initialState: Global.InitialStateType;
|
initialState: Global.InitialStateType;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = createStyles(({ css, token }) => {
|
const useStyles = createStyles(
|
||||||
console.log('useStyles', token);
|
({ css, token }: { css: any; token: FullToken }) => {
|
||||||
|
console.log('useStyles', token);
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const { Menu } = token;
|
const { Menu } = token;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
siderMenu: css`
|
siderMenu: css`
|
||||||
&.sider-menu-collapsed {
|
|
||||||
.menu-item {
|
|
||||||
justify-content: center;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
groupTitle: css`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
white-space: nowrap;
|
|
||||||
padding: var(--ant-padding-xs) var(--ant-padding);
|
|
||||||
font-size: 12px;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
overflow: hidden;
|
|
||||||
height: 30px;
|
|
||||||
&:hover {
|
|
||||||
.group-title-text {
|
|
||||||
color: var(--ant-color-text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.anticon {
|
|
||||||
transform: scale(0.8);
|
|
||||||
}
|
|
||||||
.group-title-text {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--ant-color-text-tertiary);
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.menu-item-group-title-collapsed {
|
|
||||||
position: relative;
|
|
||||||
height: 1px;
|
|
||||||
padding-block: 0;
|
|
||||||
padding-inline: 0;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
menuItemContent: css`
|
|
||||||
margin: 2px 0;
|
|
||||||
border-radius: 4px;
|
|
||||||
overflow: hidden;
|
|
||||||
`,
|
|
||||||
menuItemWrapper: css`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
gap: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
padding-inline: calc(var(--ant-font-size) * 2) var(--ant-padding);
|
|
||||||
padding-left: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
height: ${Menu.itemHeight}px;
|
|
||||||
line-height: ${Menu.itemHeight}px;
|
|
||||||
color: var(--ant-color-text-tertiary);
|
|
||||||
&:hover {
|
|
||||||
background-color: ${Menu.itemHoverBg};
|
|
||||||
color: ${Menu.itemHoverColor};
|
|
||||||
}
|
|
||||||
&.menu-item-selected {
|
|
||||||
background-color: ${Menu.itemSelectedBg};
|
|
||||||
color: ${Menu.itemSelectedColor};
|
|
||||||
.anticon {
|
|
||||||
color: ${Menu.itemSelectedColor};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:active {
|
|
||||||
background-color: ${Menu.itemActiveBg};
|
|
||||||
color: ${Menu.itemActiveColor};
|
|
||||||
}
|
|
||||||
.anticon {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
.icon-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
&.sider-menu-collapsed {
|
||||||
}
|
.menu-item {
|
||||||
`,
|
justify-content: center;
|
||||||
menuItemGroup: css`
|
padding: 0;
|
||||||
&.menu-item-group-hidden {
|
}
|
||||||
display: none;
|
}
|
||||||
}
|
.os-scrollbar-vertical .os-scrollbar-handle {
|
||||||
`,
|
min-width: 4px;
|
||||||
line: css`
|
max-width: 4px;
|
||||||
height: 1px;
|
}
|
||||||
margin-block: 6px;
|
`,
|
||||||
background-color: ${token.colorSplit};
|
groupTitle: css`
|
||||||
position: absolute;
|
display: flex;
|
||||||
left: -2px;
|
align-items: center;
|
||||||
right: -2px;
|
gap: 8px;
|
||||||
`
|
cursor: pointer;
|
||||||
};
|
white-space: nowrap;
|
||||||
});
|
padding: var(--ant-padding-xs) var(--ant-padding);
|
||||||
|
font-size: 12px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 30px;
|
||||||
|
&:hover {
|
||||||
|
.group-title-text {
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.anticon {
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
.group-title-text {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.menu-item-group-title-collapsed {
|
||||||
|
position: relative;
|
||||||
|
height: 1px;
|
||||||
|
padding-block: 0;
|
||||||
|
padding-inline: 0;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
menuItemContent: css`
|
||||||
|
margin: 2px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
`,
|
||||||
|
menuItemWrapper: css`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
padding-inline: calc(var(--ant-font-size) * 2) var(--ant-padding);
|
||||||
|
padding-left: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
height: ${Menu.itemHeight}px;
|
||||||
|
line-height: ${Menu.itemHeight}px;
|
||||||
|
color: var(--ant-color-text-tertiary);
|
||||||
|
&:hover {
|
||||||
|
background-color: ${Menu.itemHoverBg};
|
||||||
|
color: ${Menu.itemHoverColor};
|
||||||
|
}
|
||||||
|
&.menu-item-selected {
|
||||||
|
background-color: ${Menu.menuItemSelectedBg};
|
||||||
|
color: ${Menu.itemSelectedColor};
|
||||||
|
|
||||||
|
.anticon {
|
||||||
|
color: ${Menu.itemSelectedColor};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
background-color: ${Menu.itemActiveBg};
|
||||||
|
color: ${Menu.itemActiveColor};
|
||||||
|
}
|
||||||
|
.anticon {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.icon-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
menuItemGroup: css`
|
||||||
|
&.menu-item-group-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
line: css`
|
||||||
|
height: 1px;
|
||||||
|
margin-block: 6px;
|
||||||
|
background-color: ${token.colorSplit};
|
||||||
|
position: absolute;
|
||||||
|
left: -2px;
|
||||||
|
right: -2px;
|
||||||
|
`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const SiderMenu: React.FC<SiderMenuProps> = (props) => {
|
const SiderMenu: React.FC<SiderMenuProps> = (props) => {
|
||||||
const { menuData, collapsed } = props;
|
const { menuData, collapsed } = props;
|
||||||
const { styles, cx } = useStyles();
|
const { styles, cx } = useStyles();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [collapseKeys, setCollapseKeys] = useState<Set<string>>(new Set());
|
const [storedCollapsedGroups, setCollapsedGroups] = useAtom(
|
||||||
console.log('SiderMenu', location.pathname);
|
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(() => {
|
const dividerStyles = useMemo(() => {
|
||||||
if (collapsed) {
|
if (collapsed) {
|
||||||
@@ -156,14 +177,11 @@ const SiderMenu: React.FC<SiderMenuProps> = (props) => {
|
|||||||
const handleToggleGroup = (e: any, menuGroup: any) => {
|
const handleToggleGroup = (e: any, menuGroup: any) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
console.log('handleToggleGroup', menuGroup.key);
|
setCollapsedGroups(
|
||||||
|
collapsedGroups.includes(menuGroup.key)
|
||||||
if (collapseKeys.has(menuGroup.key)) {
|
? collapsedGroups.filter((key) => key !== menuGroup.key)
|
||||||
collapseKeys.delete(menuGroup.key);
|
: [...collapsedGroups, menuGroup.key]
|
||||||
} else {
|
);
|
||||||
collapseKeys.add(menuGroup.key);
|
|
||||||
}
|
|
||||||
setCollapseKeys(new Set(collapseKeys));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const menuItemRender = (menuItem: MenuItem, key: string) => {
|
const menuItemRender = (menuItem: MenuItem, key: string) => {
|
||||||
@@ -219,44 +237,55 @@ const SiderMenu: React.FC<SiderMenuProps> = (props) => {
|
|||||||
'sider-menu-collapsed': collapsed
|
'sider-menu-collapsed': collapsed
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{menuData.map((item: MenuItem, index: number) => (
|
<OverlayScroller
|
||||||
<div key={item.key}>
|
styles={{
|
||||||
{item.children && item.children.length > 0 ? (
|
wrapper: {
|
||||||
<>
|
paddingInline: 0,
|
||||||
<div
|
maxHeight: '100%'
|
||||||
className={cx(styles.groupTitle, {
|
}
|
||||||
'menu-item-group-title-collapsed': collapsed
|
}}
|
||||||
})}
|
>
|
||||||
onClick={(e) => handleToggleGroup(e, item)}
|
<div style={{ paddingRight: 8 }}>
|
||||||
>
|
{menuData.map((item: MenuItem, index: number) => (
|
||||||
{!collapsed ? (
|
<div key={item.key}>
|
||||||
<span className="group-title-text">
|
{item.children && item.children.length > 0 ? (
|
||||||
<span>{item.name}</span>
|
<>
|
||||||
<CaretDownOutlined
|
<div
|
||||||
rotate={collapseKeys.has(item.key) ? -90 : 0}
|
className={cx(styles.groupTitle, {
|
||||||
></CaretDownOutlined>
|
'menu-item-group-title-collapsed': collapsed
|
||||||
</span>
|
})}
|
||||||
) : (
|
onClick={(e) => handleToggleGroup(e, item)}
|
||||||
<span className={styles.line}></span>
|
>
|
||||||
)}
|
{!collapsed ? (
|
||||||
</div>
|
<span className="group-title-text">
|
||||||
<div
|
<span>{item.name}</span>
|
||||||
className={cx(styles.menuItemGroup, {
|
<CaretDownOutlined
|
||||||
'menu-item-group-collapsed': collapsed,
|
rotate={collapseKeys.has(item.key) ? -90 : 0}
|
||||||
'menu-item-group-hidden':
|
></CaretDownOutlined>
|
||||||
!collapsed && collapseKeys.has(item.key)
|
</span>
|
||||||
})}
|
) : (
|
||||||
>
|
<span className={styles.line}></span>
|
||||||
{item.children?.map((child: MenuItem) =>
|
)}
|
||||||
menuItemRender(child, child.key)
|
</div>
|
||||||
)}
|
<div
|
||||||
</div>
|
className={cx(styles.menuItemGroup, {
|
||||||
</>
|
'menu-item-group-collapsed': collapsed,
|
||||||
) : (
|
'menu-item-group-hidden':
|
||||||
menuItemRender(item, item.key)
|
!collapsed && collapseKeys.has(item.key)
|
||||||
)}
|
})}
|
||||||
|
>
|
||||||
|
{item.children?.map((child: MenuItem) =>
|
||||||
|
menuItemRender(child, child.key)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
menuItemRender(item, item.key)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
</OverlayScroller>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default {
|
|||||||
'ai.provider.ollama': 'Ollama',
|
'ai.provider.ollama': 'Ollama',
|
||||||
'ai.provider.openai': 'OpenAI',
|
'ai.provider.openai': 'OpenAI',
|
||||||
'ai.provider.openrouter': 'OpenRouter',
|
'ai.provider.openrouter': 'OpenRouter',
|
||||||
'ai.provider.qwen': 'Qwen',
|
'ai.provider.qwen': 'Alibaba Cloud Model Studio',
|
||||||
'ai.provider.spark': 'Spark',
|
'ai.provider.spark': 'Spark',
|
||||||
'ai.provider.stepfun': 'StepFun',
|
'ai.provider.stepfun': 'StepFun',
|
||||||
'ai.provider.together-ai': 'TogetherAI',
|
'ai.provider.together-ai': 'TogetherAI',
|
||||||
|
|||||||
@@ -23,5 +23,7 @@ export default {
|
|||||||
'apikeys.accessScope.inference': 'Inference APIs',
|
'apikeys.accessScope.inference': 'Inference APIs',
|
||||||
'apikeys.access.permissions': 'Access Permissions',
|
'apikeys.access.permissions': 'Access Permissions',
|
||||||
'apikeys.type.auto': 'Auto-generated',
|
'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': 'Default Execution Command',
|
||||||
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}', '{{'port'}}', '{{'worker_ip'}}' and '{{'model_name'}}' are placeholders that will be substituted with the actual values during deployment.`,
|
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}', '{{'port'}}', '{{'worker_ip'}}' and '{{'model_name'}}' are placeholders that will be substituted with the actual values during deployment.`,
|
||||||
'backend.form.defaultBackendParameters': 'Default Backend Parameters',
|
'backend.form.defaultBackendParameters': 'Default Backend Parameters',
|
||||||
'backend.form.parameterFormat': 'Parameter Format',
|
'backend.form.flagFormat': 'Flag Format',
|
||||||
'backend.form.parameterFormat.default': 'Backend Default',
|
'backend.form.flagFormat.tips':
|
||||||
'backend.form.parameterFormat.space': 'Space (--key value)',
|
'The format applied between an option and its value. Leave empty to keep each parameter as entered, without normalizing it.',
|
||||||
'backend.form.parameterFormat.equal': 'Equal (--key=value)',
|
'backend.form.flagFormat.space': 'Space Separated (--key value)',
|
||||||
'backend.form.commonParameters': 'Common Parameters',
|
'backend.form.flagFormat.equal': 'Equal Sign (--key=value)',
|
||||||
|
'backend.form.commonParameters': 'Common Backend Parameters',
|
||||||
'backend.form.commonParameters.tips':
|
'backend.form.commonParameters.tips':
|
||||||
'Shown as suggestions in the backend parameters input during deployment.',
|
'Shown as suggestions in the backend parameters input during deployment.',
|
||||||
'backend.form.versionConfig': 'Versions Config',
|
'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.',
|
'Stress test for long-context handling. Evaluates KV cache behavior, memory usage, and backend stability.',
|
||||||
'benchmark.form.profile.heavy.tips':
|
'benchmark.form.profile.heavy.tips':
|
||||||
'Decode-heavy generation benchmark. Measures sustained decoding speed and output token throughput.',
|
'Decode-heavy generation benchmark. Measures sustained decoding speed and output token throughput.',
|
||||||
'benchmark.table.filter.bygpu': 'Filter by GPU',
|
'benchmark.table.filter.bygpu': 'Search by GPU',
|
||||||
'benchmark.table.filter.bymodel': 'Filter by Model',
|
'benchmark.table.filter.bymodel': 'Search by model',
|
||||||
'benchmark.table.filter.bydataset': 'Filter by Dataset',
|
'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.avg': 'Avg',
|
||||||
'benchmark.table.columnSettings': 'Column Settings',
|
'benchmark.table.columnSettings': 'Column Settings',
|
||||||
'benchmark.detail.summary.results': 'Test Results',
|
'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 GPUStack 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.workerpool.batchSize.desc':
|
||||||
'Number of workers created simultaneously in the Worker pool',
|
'Number of workers created simultaneously in the Worker pool',
|
||||||
'clusters.create.addworker.tips':
|
'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':
|
'clusters.create.addCommand.tips':
|
||||||
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
||||||
'clusters.create.addCommand.k8s.tips':
|
'clusters.create.addCommand.k8s.tips':
|
||||||
@@ -70,10 +70,13 @@ export default {
|
|||||||
'For <span class="bold-text">non-Docker</span> clusters, please register clusters or manage worker pools from the Clusters page.',
|
'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': 'Select GPU Vendor',
|
||||||
'clusters.addworker.selectGPU.multiTag': 'Multi-select',
|
'clusters.addworker.selectGPU.multiTag': 'Multi-select',
|
||||||
'clusters.addworker.selectGPU.singleOnly':
|
'clusters.addworker.selectGPU.subtitle':
|
||||||
'The selected vendor is not in this cluster’s GPU vendor overrides — single-select only. Pick a vendor configured in the overrides to enable multi-select.',
|
'You can select multiple GPU Vendors or none for CPU-only clusters',
|
||||||
'clusters.addworker.checkEnv': 'Check Environment',
|
'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.specifyArgs': 'Specify Arguments',
|
||||||
|
'clusters.addworker.dtkVersion': 'DTK Version',
|
||||||
'clusters.addworker.runCommand': 'Run Command',
|
'clusters.addworker.runCommand': 'Run Command',
|
||||||
'clusters.addworker.specifyWorkerIP': 'Worker IP',
|
'clusters.addworker.specifyWorkerIP': 'Worker IP',
|
||||||
'clusters.addworker.detectWorkerIP': 'Auto-detect Worker IP',
|
'clusters.addworker.detectWorkerIP': 'Auto-detect Worker IP',
|
||||||
@@ -112,6 +115,8 @@ export default {
|
|||||||
'{count} new workers have been added to the cluster.',
|
'{count} new workers have been added to the cluster.',
|
||||||
'clusters.create.serverUrl': 'GPUStack Server URL',
|
'clusters.create.serverUrl': 'GPUStack Server URL',
|
||||||
'clusters.create.workerConfig': 'Worker Configuration',
|
'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': 'Worker Container Name',
|
||||||
'clusters.addworker.containerName.tips':
|
'clusters.addworker.containerName.tips':
|
||||||
'Specify a name for the worker container.',
|
'Specify a name for the worker container.',
|
||||||
@@ -139,7 +144,7 @@ export default {
|
|||||||
'clusters.addworker.theadNotes-02':
|
'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.',
|
'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':
|
'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 GPUStack 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.title': 'Volume Mounts',
|
||||||
'clusters.volume.name': 'Volume Name',
|
'clusters.volume.name': 'Volume Name',
|
||||||
'clusters.volume.mountPath': 'Container Path',
|
'clusters.volume.mountPath': 'Container Path',
|
||||||
@@ -164,6 +169,10 @@ export default {
|
|||||||
'clusters.volume.configMap.name': 'ConfigMap Name',
|
'clusters.volume.configMap.name': 'ConfigMap Name',
|
||||||
'clusters.volume.configMap.optional': 'Optional',
|
'clusters.volume.configMap.optional': 'Optional',
|
||||||
'clusters.volume.add': 'Add Volume Mount',
|
'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': 'Kubernetes Deployment Options',
|
||||||
'clusters.imageCredentials.title': 'Image Credentials',
|
'clusters.imageCredentials.title': 'Image Credentials',
|
||||||
'clusters.imageCredentials.add': 'Add Credential',
|
'clusters.imageCredentials.add': 'Add Credential',
|
||||||
'clusters.imageCredentials.registry': 'Registry',
|
'clusters.imageCredentials.registry': 'Registry',
|
||||||
@@ -172,16 +181,20 @@ export default {
|
|||||||
'clusters.nodeSelector.title': 'Node Selector',
|
'clusters.nodeSelector.title': 'Node Selector',
|
||||||
'clusters.nodeSelector.tip':
|
'clusters.nodeSelector.tip':
|
||||||
'Pod nodeSelector applied to every worker DaemonSet — only nodes whose labels match are eligible to run the worker.',
|
'Pod nodeSelector applied to every worker DaemonSet — only nodes whose labels match are eligible to run the worker.',
|
||||||
'clusters.gpuVendorOverrides.title': 'GPU Vendor Overrides',
|
'clusters.operatorImage.title': 'Operator Image',
|
||||||
'clusters.gpuVendorOverrides.validate.emptySelector':
|
'clusters.operatorImage.tip':
|
||||||
'The override for {vendor} needs at least one nodeSelector entry — an empty override would leave the multi-vendor manifest unable to target nodes for this runtime.',
|
'Override for the GPUStack Operator container image. Leave empty to use the server default.',
|
||||||
'clusters.gpuVendorOverrides.validate.duplicate':
|
'clusters.namespace.title': 'Namespace',
|
||||||
'Overrides for {v1} and {v2} use the same nodeSelector. Each vendor must scope to a distinct node set, otherwise their worker DaemonSets would compete for the same nodes.',
|
'clusters.namespace.tip':
|
||||||
'clusters.gpuVendorOverrides.validate.keyConflict':
|
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
||||||
'Override for {vendor} reuses key(s) {keys} from the base Node Selector — the CPU worker would simultaneously require and forbid the key and never schedule.',
|
'clusters.clusterType.title': 'Cluster Type',
|
||||||
'clusters.gpuVendorOverrides.tip':
|
'clusters.modelService.title': 'Model Service',
|
||||||
'Required when running multiple GPU vendors in one cluster. Each entry pins that vendor’s worker DaemonSet to nodes matching its nodeSelector, and the CPU worker is steered away from those nodes via a DoesNotExist node-affinity.',
|
'clusters.modelService.tip':
|
||||||
'clusters.gpuVendorOverrides.add': 'Add Override',
|
'For LLM inference and API serving — e.g. exposing model APIs and token-based services.',
|
||||||
'clusters.gpuVendorOverrides.vendor': 'GPU Vendor',
|
'clusters.gpuInstances.title': 'GPU Service',
|
||||||
'clusters.gpuVendorOverrides.nodeSelector': 'Node Selector'
|
'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.enabled': 'Enabled',
|
||||||
'common.button.disabled': 'Disabled',
|
'common.button.disabled': 'Disabled',
|
||||||
'common.button.upgrade': 'Upgrade',
|
'common.button.upgrade': 'Upgrade',
|
||||||
|
'common.enterprise.feature': 'Available in GPUStack Enterprise',
|
||||||
'common.input.holder': 'Please enter',
|
'common.input.holder': 'Please enter',
|
||||||
'common.validate.value': '{name} value is required',
|
'common.validate.value': '{name} value is required',
|
||||||
'common.button.edit': 'Edit',
|
'common.button.edit': 'Edit',
|
||||||
@@ -55,6 +56,7 @@ export default {
|
|||||||
'common.button.viewevent': 'View Events',
|
'common.button.viewevent': 'View Events',
|
||||||
'common.button.recreate': 'Recreate',
|
'common.button.recreate': 'Recreate',
|
||||||
'common.table.operation': 'Operations',
|
'common.table.operation': 'Operations',
|
||||||
|
'common.table.creator': 'Creator',
|
||||||
'common.table.createTime': 'Created',
|
'common.table.createTime': 'Created',
|
||||||
'common.table.updateTime': 'Updated',
|
'common.table.updateTime': 'Updated',
|
||||||
'common.table.description': 'Description',
|
'common.table.description': 'Description',
|
||||||
@@ -65,6 +67,7 @@ export default {
|
|||||||
'common.search.name.placeholder': 'filter by name',
|
'common.search.name.placeholder': 'filter by name',
|
||||||
'common.search.id.placeholder': 'filter by ID',
|
'common.search.id.placeholder': 'filter by ID',
|
||||||
'common.filter.byId': 'filter by ID',
|
'common.filter.byId': 'filter by ID',
|
||||||
|
'common.filter.byCreator': 'Filter by creator',
|
||||||
'common.table.type': 'Type',
|
'common.table.type': 'Type',
|
||||||
'common.table.default': 'Default Value',
|
'common.table.default': 'Default Value',
|
||||||
'common.copy.success': 'Copied success!',
|
'common.copy.success': 'Copied success!',
|
||||||
@@ -166,6 +169,7 @@ export default {
|
|||||||
'common.time.hour': 'hour',
|
'common.time.hour': 'hour',
|
||||||
'common.time.minute': 'minutes',
|
'common.time.minute': 'minutes',
|
||||||
'common.issue.report': 'Report an issue',
|
'common.issue.report': 'Report an issue',
|
||||||
|
'common.github.star.tooltip': 'Star us on GitHub',
|
||||||
'common.social.discord': 'Join Our Discord',
|
'common.social.discord': 'Join Our Discord',
|
||||||
'common.table.mark': 'Comment',
|
'common.table.mark': 'Comment',
|
||||||
'common.table.rollback.mark': 'Rollback Comment',
|
'common.table.rollback.mark': 'Rollback Comment',
|
||||||
@@ -257,6 +261,10 @@ export default {
|
|||||||
'common.login.auth': 'Authenticating...',
|
'common.login.auth': 'Authenticating...',
|
||||||
'common.login.auth.failed': 'Authentication failed',
|
'common.login.auth.failed': 'Authentication failed',
|
||||||
'common.login.password': 'Log in with Password',
|
'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.external.login': 'Log in with {type}',
|
||||||
'common.sso.noConfig':
|
'common.sso.noConfig':
|
||||||
'Single sign-on is not enabled on this system. Please contact your administrator.',
|
'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.image.limit.height': 'Image height must be {height}.',
|
||||||
'common.remaining': 'Remaining {count}',
|
'common.remaining': 'Remaining {count}',
|
||||||
'common.max': 'Max {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':
|
'gpuservice.template.command.placeholder':
|
||||||
'Separate arguments with spaces; wrap arguments containing spaces in quotes, e.g.: /bin/bash -c "echo hello world"',
|
'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': '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': 'Container Disk (GB)',
|
||||||
|
'gpuservice.template.containerDisk.tips':
|
||||||
|
'The size of the container system disk.',
|
||||||
'gpuservice.template.memory': 'Memory (GB)',
|
'gpuservice.template.memory': 'Memory (GB)',
|
||||||
'gpuservice.instance.containerDisk.remaining':
|
'gpuservice.instance.containerDisk.remaining':
|
||||||
'Container Disk (Max {count} GB)',
|
'Container Disk (Max {count} GB)',
|
||||||
@@ -97,21 +101,32 @@ export default {
|
|||||||
'gpuservice.instance.templates': 'Instance Templates',
|
'gpuservice.instance.templates': 'Instance Templates',
|
||||||
'gpuservice.instance.section.storage': 'Storage',
|
'gpuservice.instance.section.storage': 'Storage',
|
||||||
'gpuservice.instance.type.required': 'Please select an instance type',
|
'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': 'GPU Count',
|
||||||
'gpuservice.instance.gpuCount.required': 'Please enter the GPU count',
|
'gpuservice.instance.gpuCount.required': 'Please enter the GPU count',
|
||||||
'gpuservice.instance.gpuCount.max':
|
'gpuservice.instance.gpuCount.max':
|
||||||
'Please select at most {count} GPU card(s)',
|
'Please select at most {count} GPU card(s)',
|
||||||
'gpuservice.instance.gpuCount.min':
|
'gpuservice.instance.gpuCount.min':
|
||||||
'Please select at least {count} GPU card(s)',
|
'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':
|
'gpuservice.instance.gpuCount.noAvailable':
|
||||||
'No available GPU resources, please choose another instance type.',
|
'No available GPU resources, please choose another instance type.',
|
||||||
'gpuservice.instance.gpuCount.zero':
|
'gpuservice.instance.gpuCount.zero':
|
||||||
'CPU-only setup for environment preparation.',
|
'CPU-only setup for environment preparation.',
|
||||||
'gpuservice.instance.stock': 'Stock',
|
'gpuservice.instance.stock': 'Stock',
|
||||||
'gpuservice.instance.sliced': 'Sliced',
|
'gpuservice.instance.sliced': 'Sliced',
|
||||||
'gpuservice.instance.memory': 'Memory',
|
'gpuservice.instance.memory': 'VRAM',
|
||||||
'gpuservice.instance.ram': 'RAM',
|
'gpuservice.instance.ram': 'RAM',
|
||||||
|
'gpuservice.instance.os': 'OS',
|
||||||
|
'gpuservice.instance.arch': 'Arch',
|
||||||
'gpuservice.instance.disk': 'Disk',
|
'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.type.placeholder': 'Search by name',
|
||||||
'gpuservice.instance.search.template.placeholder':
|
'gpuservice.instance.search.template.placeholder':
|
||||||
'Search by template name, image or mount path',
|
'Search by template name, image or mount path',
|
||||||
@@ -142,14 +157,13 @@ export default {
|
|||||||
'gpuservice.storage.capacity': 'Capacity',
|
'gpuservice.storage.capacity': 'Capacity',
|
||||||
'gpuservice.storage.accessMode': 'Access Mode',
|
'gpuservice.storage.accessMode': 'Access Mode',
|
||||||
'gpuservice.storage.persistent': 'Persistent',
|
'gpuservice.storage.persistent': 'Persistent',
|
||||||
'gpuservice.storage.temporary': 'Temporary',
|
'gpuservice.storage.temporary': 'Ephemeral',
|
||||||
'gpuservice.storage.persistentVolume': 'Persistent Volume',
|
'gpuservice.storage.persistentVolume': 'Persistent',
|
||||||
'gpuservice.storage.temporary.tips':
|
'gpuservice.storage.temporary.tips':
|
||||||
'Data is cleared when the instance stops.',
|
'Data is cleared when the instance stops.',
|
||||||
'gpuservice.storage.persistentVolume.tips':
|
'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.storage.persistentVolume.required':
|
'gpuservice.storage.persistentVolume.required': 'Please select a storage',
|
||||||
'Please select a persistent volume',
|
|
||||||
'gpuservice.storage.persistentVolume.capacity': 'Capacity (GB)',
|
'gpuservice.storage.persistentVolume.capacity': 'Capacity (GB)',
|
||||||
'gpuservice.storage.persistentVolume.capacity.required':
|
'gpuservice.storage.persistentVolume.capacity.required':
|
||||||
'Please enter capacity',
|
'Please enter capacity',
|
||||||
@@ -159,5 +173,10 @@ export default {
|
|||||||
'gpuservice.storage.tempCapacity.required':
|
'gpuservice.storage.tempCapacity.required':
|
||||||
'Please enter the temporary storage capacity',
|
'Please enter the temporary storage capacity',
|
||||||
'gpuservice.form.rule.name':
|
'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'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export default {
|
|||||||
'menu.playground.text2images': 'Image',
|
'menu.playground.text2images': 'Image',
|
||||||
'menu.playground.video': 'Video',
|
'menu.playground.video': 'Video',
|
||||||
'menu.compare': 'Compare',
|
'menu.compare': 'Compare',
|
||||||
'menu.models': 'Models',
|
'menu.models': 'Model Service',
|
||||||
'menu.models.modelList': 'Deploy & Manage',
|
'menu.models.modelList': 'Deploy & Manage',
|
||||||
'menu.models.modelCatalog': 'Catalog',
|
'menu.models.modelCatalog': 'Catalog',
|
||||||
'menu.models.catalog': 'Model Catalog',
|
'menu.models.catalog': 'Model Catalog',
|
||||||
@@ -27,20 +27,24 @@ export default {
|
|||||||
'menu.users': 'Users',
|
'menu.users': 'Users',
|
||||||
'menu.resources.workers': 'Workers',
|
'menu.resources.workers': 'Workers',
|
||||||
'menu.resources.gpus': 'GPUs',
|
'menu.resources.gpus': 'GPUs',
|
||||||
'menu.resources.modelfiles': 'Model Files',
|
'menu.models.modelfiles': 'Model Files',
|
||||||
'menu.accessControl': 'Access Control',
|
'menu.accessControl': 'Access Control',
|
||||||
'menu.accessControl.apikeys': 'API Keys',
|
'menu.accessControl.apikeys': 'API Keys',
|
||||||
'menu.accessControl.users': 'Users',
|
'menu.accessControl.users': 'Users',
|
||||||
'menu.profile': 'Profile',
|
'menu.accessControl.organizations': 'Organizations',
|
||||||
|
'menu.profile': 'Preferences',
|
||||||
'menu.login': 'Login',
|
'menu.login': 'Login',
|
||||||
'menu.usage': 'Usage',
|
'menu.usage': 'Usage',
|
||||||
|
'menu.usage.usage': 'Usage',
|
||||||
|
'menu.billingAndUsage': 'Usage & Billing',
|
||||||
|
'menu.billingAndUsage.usage': 'Usage',
|
||||||
|
'menu.billingAndUsage.billing': 'Billing',
|
||||||
'menu.404': '404',
|
'menu.404': '404',
|
||||||
'menu.clusterManagement': 'Cluster Management',
|
'menu.resources.clusters': 'Clusters',
|
||||||
'menu.clusterManagement.clusters': 'Clusters',
|
'menu.resources.credentials': 'Cloud Credentials',
|
||||||
'menu.clusterManagement.credentials': 'Cloud Credentials',
|
'menu.resources.clusterDetail': 'Cluster Detail',
|
||||||
'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
'menu.resources.clusterCreate': 'Create Cluster',
|
||||||
'menu.clusterManagement.clusterCreate': 'Create Cluster',
|
'menu.models.backendsList': 'Inference Backends',
|
||||||
'menu.resources.backendsList': 'Inference Backends',
|
|
||||||
'menu.gpuService': 'GPU Service',
|
'menu.gpuService': 'GPU Service',
|
||||||
'menu.gpuService.instances': 'GPU Instances',
|
'menu.gpuService.instances': 'GPU Instances',
|
||||||
'menu.gpuService.templates': 'Instance Templates',
|
'menu.gpuService.templates': 'Instance Templates',
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ export default {
|
|||||||
'models.instance.startHistory': 'Run History',
|
'models.instance.startHistory': 'Run History',
|
||||||
'models.instance.startHistory.tips':
|
'models.instance.startHistory.tips':
|
||||||
'Shows logs from the run before the last error-triggered restart.',
|
'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.add': 'Add LoRA Adapter',
|
||||||
'models.form.lora.select': 'Select LoRA',
|
'models.form.lora.select': 'Select LoRA',
|
||||||
'models.form.lora.name': 'LoRA name',
|
'models.form.lora.name': 'LoRA name',
|
||||||
|
|||||||
@@ -36,9 +36,12 @@ export default {
|
|||||||
'noresult.catalog.nofound': 'No matching models found.',
|
'noresult.catalog.nofound': 'No matching models found.',
|
||||||
'noresult.resources.cluster':
|
'noresult.resources.cluster':
|
||||||
'No clusters available. Add a cluster to get started.',
|
'No clusters available. Add a cluster to get started.',
|
||||||
|
'noresult.resources.k8sCluster':
|
||||||
|
'No clusters available. Add a Kubernetes cluster to get started.',
|
||||||
'noresult.resources.worker':
|
'noresult.resources.worker':
|
||||||
'No workers available. Add a worker to get started.',
|
'No workers available. Add a worker to get started.',
|
||||||
'noresult.resources.gotocluster': 'Create Your First Cluster',
|
'noresult.resources.gotocluster': 'Create Your First Cluster',
|
||||||
|
'noresult.resources.addk8scluster': 'Add a Kubernetes Cluster',
|
||||||
'noresult.resources.gotoworker': 'Add Worker',
|
'noresult.resources.gotoworker': 'Add Worker',
|
||||||
'noresult.benchmark.title': 'No Benchmarks',
|
'noresult.benchmark.title': 'No Benchmarks',
|
||||||
'noresult.benchmark.subTitle': 'No benchmarks have been added yet.',
|
'noresult.benchmark.subTitle': 'No benchmarks have been added yet.',
|
||||||
|
|||||||
@@ -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 GPUStack 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'
|
||||||
|
};
|
||||||
@@ -17,9 +17,15 @@ export default {
|
|||||||
'usage.table.user.apiKeysUsed': 'API Keys Used',
|
'usage.table.user.apiKeysUsed': 'API Keys Used',
|
||||||
'usage.table.lastActive': 'Last Active',
|
'usage.table.lastActive': 'Last Active',
|
||||||
'usage.filter.granularity': 'Granularity',
|
'usage.filter.granularity': 'Granularity',
|
||||||
|
'usage.filter.granularity.hour': 'Hour',
|
||||||
'usage.filter.granularity.day': 'Day',
|
'usage.filter.granularity.day': 'Day',
|
||||||
'usage.filter.granularity.week': 'Week',
|
'usage.filter.granularity.week': 'Week',
|
||||||
'usage.filter.granularity.month': 'Month',
|
'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.models': 'Models',
|
||||||
'usage.tabs.apikeys': 'API Keys',
|
'usage.tabs.apikeys': 'API Keys',
|
||||||
'usage.tabs.users': 'User',
|
'usage.tabs.users': 'User',
|
||||||
@@ -32,5 +38,70 @@ export default {
|
|||||||
'usage.chart.cached': 'Cached',
|
'usage.chart.cached': 'Cached',
|
||||||
'usage.chart.uncached': 'Uncached',
|
'usage.chart.uncached': 'Uncached',
|
||||||
'usage.chart.inputTokensCached': 'Input Tokens (Cached/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'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export default {
|
|||||||
'vendor.hygon': 'Hygon',
|
'vendor.hygon': 'Hygon',
|
||||||
'vendor.moorthreads': 'Moore Threads',
|
'vendor.moorthreads': 'Moore Threads',
|
||||||
'vendor.iluvatar': 'Iluvatar',
|
'vendor.iluvatar': 'Iluvatar',
|
||||||
'vendor.metax': 'Metax',
|
'vendor.metax': 'MetaX',
|
||||||
'vendor.cambricon': 'Cambricon',
|
'vendor.cambricon': 'Cambricon',
|
||||||
'vendor.thead': 'T-Head PPU'
|
'vendor.thead': 'T-Head PPU'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default {
|
|||||||
'ai.provider.ollama': 'Ollama',
|
'ai.provider.ollama': 'Ollama',
|
||||||
'ai.provider.openai': 'OpenAI',
|
'ai.provider.openai': 'OpenAI',
|
||||||
'ai.provider.openrouter': 'OpenRouter',
|
'ai.provider.openrouter': 'OpenRouter',
|
||||||
'ai.provider.qwen': 'Qwen',
|
'ai.provider.qwen': 'Alibaba Cloud Model Studio',
|
||||||
'ai.provider.spark': 'Spark',
|
'ai.provider.spark': 'Spark',
|
||||||
'ai.provider.stepfun': 'StepFun',
|
'ai.provider.stepfun': 'StepFun',
|
||||||
'ai.provider.together-ai': 'TogetherAI',
|
'ai.provider.together-ai': 'TogetherAI',
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ export default {
|
|||||||
'apikeys.accessScope.inference': 'Inference APIs',
|
'apikeys.accessScope.inference': 'Inference APIs',
|
||||||
'apikeys.access.permissions': 'Access Permissions',
|
'apikeys.access.permissions': 'Access Permissions',
|
||||||
'apikeys.type.auto': 'Auto-generated',
|
'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) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
|
|||||||
@@ -23,11 +23,12 @@ export default {
|
|||||||
'backend.form.defaultExecuteCommand': 'Default Execution Command',
|
'backend.form.defaultExecuteCommand': 'Default Execution Command',
|
||||||
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}', '{{'port'}}', '{{'worker_ip'}}' and '{{'model_name'}}' are placeholders that will be substituted with the actual values during deployment.`,
|
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}', '{{'port'}}', '{{'worker_ip'}}' and '{{'model_name'}}' are placeholders that will be substituted with the actual values during deployment.`,
|
||||||
'backend.form.defaultBackendParameters': 'Default Backend Parameters',
|
'backend.form.defaultBackendParameters': 'Default Backend Parameters',
|
||||||
'backend.form.parameterFormat': 'パラメータ形式',
|
'backend.form.flagFormat': 'フラグ形式',
|
||||||
'backend.form.parameterFormat.default': 'バックエンドのデフォルト',
|
'backend.form.flagFormat.tips':
|
||||||
'backend.form.parameterFormat.space': 'スペース区切り (--key value)',
|
'オプションとその値を連結する形式。空欄の場合は各パラメータを入力されたまま保持し、形式を統一しません。',
|
||||||
'backend.form.parameterFormat.equal': 'イコール連結 (--key=value)',
|
'backend.form.flagFormat.space': 'スペース区切り (--key value)',
|
||||||
'backend.form.commonParameters': 'よく使うパラメータ',
|
'backend.form.flagFormat.equal': 'イコール記号 (--key=value)',
|
||||||
|
'backend.form.commonParameters': 'よく使うバックエンドパラメータ',
|
||||||
'backend.form.commonParameters.tips':
|
'backend.form.commonParameters.tips':
|
||||||
'モデルのデプロイ時にバックエンドパラメータ入力欄の候補として表示されます。',
|
'モデルのデプロイ時にバックエンドパラメータ入力欄の候補として表示されます。',
|
||||||
'backend.form.versionConfig': 'Versions Config',
|
'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.',
|
'Stress test for long-context handling. Evaluates KV cache behavior, memory usage, and backend stability.',
|
||||||
'benchmark.form.profile.heavy.tips':
|
'benchmark.form.profile.heavy.tips':
|
||||||
'Decode-heavy generation benchmark. Measures sustained decoding speed and output token throughput.',
|
'Decode-heavy generation benchmark. Measures sustained decoding speed and output token throughput.',
|
||||||
'benchmark.table.filter.bygpu': 'Filter by GPU',
|
'benchmark.table.filter.bygpu': 'GPU 検索',
|
||||||
'benchmark.table.filter.bymodel': 'Filter by Model',
|
'benchmark.table.filter.bymodel': 'モデル検索',
|
||||||
'benchmark.table.filter.bydataset': 'Filter by Dataset',
|
'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.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 GPUStack 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.workerpool.batchSize.desc':
|
||||||
'Number of workers created simultaneously in the Worker pool',
|
'Number of workers created simultaneously in the Worker pool',
|
||||||
'clusters.create.addworker.tips':
|
'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':
|
'clusters.create.addCommand.tips':
|
||||||
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
'On the Worker that needs to be added, run the following command to join it to the cluster.',
|
||||||
'clusters.create.addCommand.k8s.tips':
|
'clusters.create.addCommand.k8s.tips':
|
||||||
@@ -70,10 +70,13 @@ export default {
|
|||||||
'For <span class="bold-text">non-Docker</span> clusters, please register clusters or manage worker pools from the Clusters page.',
|
'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': 'Select GPU Vendor',
|
||||||
'clusters.addworker.selectGPU.multiTag': 'Multi-select',
|
'clusters.addworker.selectGPU.multiTag': 'Multi-select',
|
||||||
'clusters.addworker.selectGPU.singleOnly':
|
'clusters.addworker.selectGPU.subtitle':
|
||||||
'The selected vendor is not in this cluster’s GPU vendor overrides — single-select only. Pick a vendor configured in the overrides to enable multi-select.',
|
'複数の GPU ベンダーを選択するか、CPU クラスター専用の場合は選択不要です',
|
||||||
'clusters.addworker.checkEnv': 'Check Environment',
|
'clusters.addworker.checkEnv': 'Check Environment',
|
||||||
|
'clusters.addworker.checkEnv.cpuOnlyTips':
|
||||||
|
'以下のコマンドを使用して、Kubernetes クラスターに少なくとも 1 つのレディーノードがあることを確認してください。CPU クラスターを登録しています。',
|
||||||
'clusters.addworker.specifyArgs': 'Specify Arguments',
|
'clusters.addworker.specifyArgs': 'Specify Arguments',
|
||||||
|
'clusters.addworker.dtkVersion': 'DTK バージョン',
|
||||||
'clusters.addworker.runCommand': 'Run Command',
|
'clusters.addworker.runCommand': 'Run Command',
|
||||||
'clusters.addworker.specifyWorkerIP': 'Worker IP',
|
'clusters.addworker.specifyWorkerIP': 'Worker IP',
|
||||||
'clusters.addworker.detectWorkerIP': 'Worker IP を自動検出',
|
'clusters.addworker.detectWorkerIP': 'Worker IP を自動検出',
|
||||||
@@ -112,6 +115,8 @@ export default {
|
|||||||
'{count} new workers have been added to the cluster.',
|
'{count} new workers have been added to the cluster.',
|
||||||
'clusters.create.serverUrl': 'GPUStack Server URL',
|
'clusters.create.serverUrl': 'GPUStack Server URL',
|
||||||
'clusters.create.workerConfig': 'Worker Configuration',
|
'clusters.create.workerConfig': 'Worker Configuration',
|
||||||
|
'clusters.edit.k8sOptions.changed.tip':
|
||||||
|
'Kubernetes オプションを変更しました。変更を有効にするには、対象クラスターで登録コマンドを再実行してください。',
|
||||||
'clusters.addworker.containerName': 'Worker Container Name',
|
'clusters.addworker.containerName': 'Worker Container Name',
|
||||||
'clusters.addworker.containerName.tips':
|
'clusters.addworker.containerName.tips':
|
||||||
'Specify a name for the worker container.',
|
'Specify a name for the worker container.',
|
||||||
@@ -139,7 +144,7 @@ export default {
|
|||||||
'clusters.addworker.theadNotes-02':
|
'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.',
|
'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':
|
'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 GPUStack 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.title': 'Volume Mounts',
|
||||||
'clusters.volume.name': 'Volume Name',
|
'clusters.volume.name': 'Volume Name',
|
||||||
'clusters.volume.mountPath': 'Container Path',
|
'clusters.volume.mountPath': 'Container Path',
|
||||||
@@ -164,6 +169,10 @@ export default {
|
|||||||
'clusters.volume.configMap.name': 'ConfigMap Name',
|
'clusters.volume.configMap.name': 'ConfigMap Name',
|
||||||
'clusters.volume.configMap.optional': 'Optional',
|
'clusters.volume.configMap.optional': 'Optional',
|
||||||
'clusters.volume.add': 'Add Volume Mount',
|
'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': 'Kubernetes Deployment Options',
|
||||||
'clusters.imageCredentials.title': 'Image Credentials',
|
'clusters.imageCredentials.title': 'Image Credentials',
|
||||||
'clusters.imageCredentials.add': 'Add Credential',
|
'clusters.imageCredentials.add': 'Add Credential',
|
||||||
'clusters.imageCredentials.registry': 'Registry',
|
'clusters.imageCredentials.registry': 'Registry',
|
||||||
@@ -172,18 +181,22 @@ export default {
|
|||||||
'clusters.nodeSelector.title': 'Node Selector',
|
'clusters.nodeSelector.title': 'Node Selector',
|
||||||
'clusters.nodeSelector.tip':
|
'clusters.nodeSelector.tip':
|
||||||
'Pod nodeSelector applied to every worker DaemonSet — only nodes whose labels match are eligible to run the worker.',
|
'Pod nodeSelector applied to every worker DaemonSet — only nodes whose labels match are eligible to run the worker.',
|
||||||
'clusters.gpuVendorOverrides.title': 'GPU Vendor Overrides',
|
'clusters.operatorImage.title': 'Operator Image',
|
||||||
'clusters.gpuVendorOverrides.validate.emptySelector':
|
'clusters.operatorImage.tip':
|
||||||
'The override for {vendor} needs at least one nodeSelector entry — an empty override would leave the multi-vendor manifest unable to target nodes for this runtime.',
|
'Override for the GPUStack Operator container image. Leave empty to use the server default.',
|
||||||
'clusters.gpuVendorOverrides.validate.duplicate':
|
'clusters.namespace.title': 'Namespace',
|
||||||
'Overrides for {v1} and {v2} use the same nodeSelector. Each vendor must scope to a distinct node set, otherwise their worker DaemonSets would compete for the same nodes.',
|
'clusters.namespace.tip':
|
||||||
'clusters.gpuVendorOverrides.validate.keyConflict':
|
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
||||||
'Override for {vendor} reuses key(s) {keys} from the base Node Selector — the CPU worker would simultaneously require and forbid the key and never schedule.',
|
'clusters.clusterType.title': 'Cluster Type',
|
||||||
'clusters.gpuVendorOverrides.tip':
|
'clusters.modelService.title': 'Model Service',
|
||||||
'Required when running multiple GPU vendors in one cluster. Each entry pins that vendor’s worker DaemonSet to nodes matching its nodeSelector, and the CPU worker is steered away from those nodes via a DoesNotExist node-affinity.',
|
'clusters.modelService.tip':
|
||||||
'clusters.gpuVendorOverrides.add': 'Add Override',
|
'For LLM inference and API serving — e.g. exposing model APIs and token-based services.',
|
||||||
'clusters.gpuVendorOverrides.vendor': 'GPU Vendor',
|
'clusters.gpuInstances.title': 'GPU Service',
|
||||||
'clusters.gpuVendorOverrides.nodeSelector': 'Node Selector'
|
'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.'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export default {
|
|||||||
'common.button.enabled': '有効',
|
'common.button.enabled': '有効',
|
||||||
'common.button.disabled': '無効',
|
'common.button.disabled': '無効',
|
||||||
'common.button.upgrade': 'アップグレード',
|
'common.button.upgrade': 'アップグレード',
|
||||||
|
'common.enterprise.feature': 'Available in GPUStack Enterprise',
|
||||||
'common.input.holder': '入力してください',
|
'common.input.holder': '入力してください',
|
||||||
'common.validate.value': '{name} の値は必須です',
|
'common.validate.value': '{name} の値は必須です',
|
||||||
'common.button.edit': '編集',
|
'common.button.edit': '編集',
|
||||||
@@ -55,6 +56,7 @@ export default {
|
|||||||
'common.button.viewevent': 'イベントを表示',
|
'common.button.viewevent': 'イベントを表示',
|
||||||
'common.button.recreate': '再作成',
|
'common.button.recreate': '再作成',
|
||||||
'common.table.operation': '操作',
|
'common.table.operation': '操作',
|
||||||
|
'common.table.creator': '作成者',
|
||||||
'common.table.createTime': '作成日時',
|
'common.table.createTime': '作成日時',
|
||||||
'common.table.updateTime': '更新日時',
|
'common.table.updateTime': '更新日時',
|
||||||
'common.table.description': '説明',
|
'common.table.description': '説明',
|
||||||
@@ -65,6 +67,7 @@ export default {
|
|||||||
'common.search.name.placeholder': '名前でフィルタ',
|
'common.search.name.placeholder': '名前でフィルタ',
|
||||||
'common.search.id.placeholder': 'IDでフィルタ',
|
'common.search.id.placeholder': 'IDでフィルタ',
|
||||||
'common.filter.byId': 'IDでフィルタ',
|
'common.filter.byId': 'IDでフィルタ',
|
||||||
|
'common.filter.byCreator': '作成者でフィルタ',
|
||||||
'common.table.type': 'タイプ',
|
'common.table.type': 'タイプ',
|
||||||
'common.table.default': 'デフォルト値',
|
'common.table.default': 'デフォルト値',
|
||||||
'common.copy.success': 'コピー成功!',
|
'common.copy.success': 'コピー成功!',
|
||||||
@@ -167,6 +170,7 @@ export default {
|
|||||||
'common.time.hour': '時間',
|
'common.time.hour': '時間',
|
||||||
'common.time.minute': '分',
|
'common.time.minute': '分',
|
||||||
'common.issue.report': '問題を報告',
|
'common.issue.report': '問題を報告',
|
||||||
|
'common.github.star.tooltip': 'GitHub でスターをつける',
|
||||||
'common.social.discord': 'Discordに参加',
|
'common.social.discord': 'Discordに参加',
|
||||||
'common.table.mark': 'コメント',
|
'common.table.mark': 'コメント',
|
||||||
'common.table.rollback.mark': 'ロールバックコメント',
|
'common.table.rollback.mark': 'ロールバックコメント',
|
||||||
@@ -257,6 +261,10 @@ export default {
|
|||||||
'common.login.auth': 'Authenticating...',
|
'common.login.auth': 'Authenticating...',
|
||||||
'common.login.auth.failed': 'Authentication failed',
|
'common.login.auth.failed': 'Authentication failed',
|
||||||
'common.login.password': 'Log in with Password',
|
'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.external.login': 'Log in with {type}',
|
||||||
'common.sso.noConfig':
|
'common.sso.noConfig':
|
||||||
'Single sign-on is not enabled on this system. Please contact your administrator.',
|
'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.image.limit.height': 'Image height must be {height}.',
|
||||||
'common.remaining': '残り {count}',
|
'common.remaining': '残り {count}',
|
||||||
'common.max': '最大 {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) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ export default {
|
|||||||
'gpuservice.template.command.placeholder':
|
'gpuservice.template.command.placeholder':
|
||||||
'引数はスペースで区切り、スペースを含む引数は引用符で囲んでください。例:/bin/bash -c "echo hello world"',
|
'引数はスペースで区切り、スペースを含む引数は引用符で囲んでください。例:/bin/bash -c "echo hello world"',
|
||||||
'gpuservice.template.mountPath': 'マウントパス',
|
'gpuservice.template.mountPath': 'マウントパス',
|
||||||
|
'gpuservice.template.mountPath.tips':
|
||||||
|
'このテンプレートからインスタンスを作成する際に、ストレージボリュームがデフォルトでマウントされるパスです。インスタンスの実行中に保持する必要があるデータの永続化に使用できます。',
|
||||||
'gpuservice.template.containerDisk': 'コンテナディスク (GB)',
|
'gpuservice.template.containerDisk': 'コンテナディスク (GB)',
|
||||||
|
'gpuservice.template.containerDisk.tips':
|
||||||
|
'コンテナシステムディスクのサイズです。',
|
||||||
'gpuservice.template.memory': 'メモリ (GB)',
|
'gpuservice.template.memory': 'メモリ (GB)',
|
||||||
'gpuservice.instance.containerDisk.remaining':
|
'gpuservice.instance.containerDisk.remaining':
|
||||||
'コンテナディスク (最大 {count} GB)',
|
'コンテナディスク (最大 {count} GB)',
|
||||||
@@ -96,20 +100,32 @@ export default {
|
|||||||
'gpuservice.instance.templates': 'インスタンステンプレート',
|
'gpuservice.instance.templates': 'インスタンステンプレート',
|
||||||
'gpuservice.instance.section.storage': 'ストレージボリューム',
|
'gpuservice.instance.section.storage': 'ストレージボリューム',
|
||||||
'gpuservice.instance.type.required': 'インスタンスタイプを選択してください',
|
'gpuservice.instance.type.required': 'インスタンスタイプを選択してください',
|
||||||
|
'gpuservice.instance.type.noAvailable':
|
||||||
|
'利用可能なインスタンスタイプがありません',
|
||||||
'gpuservice.instance.gpuCount': 'GPU 数',
|
'gpuservice.instance.gpuCount': 'GPU 数',
|
||||||
'gpuservice.instance.gpuCount.required': 'GPU 数を入力してください',
|
'gpuservice.instance.gpuCount.required': 'GPU 数を入力してください',
|
||||||
'gpuservice.instance.gpuCount.max':
|
'gpuservice.instance.gpuCount.max':
|
||||||
'最大 {count} 枚の GPU カードを選択してください',
|
'最大 {count} 枚の GPU カードを選択してください',
|
||||||
'gpuservice.instance.gpuCount.min':
|
'gpuservice.instance.gpuCount.min':
|
||||||
'少なくとも {count} 枚の GPU カードを選択してください',
|
'少なくとも {count} 枚の GPU カードを選択してください',
|
||||||
|
'gpuservice.instance.cpuCount.max':
|
||||||
|
'最大 {count} 個の CPU コアを選択してください',
|
||||||
|
'gpuservice.instance.cpuCount.min':
|
||||||
|
'少なくとも {count} 個の CPU コアを選択してください',
|
||||||
'gpuservice.instance.gpuCount.noAvailable':
|
'gpuservice.instance.gpuCount.noAvailable':
|
||||||
'利用可能な GPU リソースがありません。別のインスタンスタイプを選択してください。',
|
'利用可能な GPU リソースがありません。別のインスタンスタイプを選択してください。',
|
||||||
'gpuservice.instance.gpuCount.zero': 'CPU のみを使用し、環境準備用です。',
|
'gpuservice.instance.gpuCount.zero': 'CPU のみを使用し、環境準備用です。',
|
||||||
'gpuservice.instance.stock': '在庫',
|
'gpuservice.instance.stock': '在庫',
|
||||||
'gpuservice.instance.sliced': '分割',
|
'gpuservice.instance.sliced': '分割',
|
||||||
'gpuservice.instance.memory': 'Memory',
|
'gpuservice.instance.memory': 'VRAM',
|
||||||
'gpuservice.instance.ram': 'RAM',
|
'gpuservice.instance.ram': 'RAM',
|
||||||
|
'gpuservice.instance.os': 'OS',
|
||||||
|
'gpuservice.instance.arch': 'アーキテクチャ',
|
||||||
'gpuservice.instance.disk': 'ディスク',
|
'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.type.placeholder': '名前で検索',
|
||||||
'gpuservice.instance.search.template.placeholder':
|
'gpuservice.instance.search.template.placeholder':
|
||||||
'テンプレート名、イメージまたはマウントパスで検索',
|
'テンプレート名、イメージまたはマウントパスで検索',
|
||||||
@@ -141,9 +157,9 @@ export default {
|
|||||||
'gpuservice.storage.accessMode': 'アクセスモード',
|
'gpuservice.storage.accessMode': 'アクセスモード',
|
||||||
'gpuservice.storage.persistent': '永続',
|
'gpuservice.storage.persistent': '永続',
|
||||||
'gpuservice.storage.temporary': '一時',
|
'gpuservice.storage.temporary': '一時',
|
||||||
'gpuservice.storage.persistentVolume': '永続ボリューム',
|
'gpuservice.storage.persistentVolume': '永続',
|
||||||
'gpuservice.storage.persistentVolume.required':
|
'gpuservice.storage.persistentVolume.required':
|
||||||
'永続ボリュームを選択してください',
|
'ストレージを選択してください',
|
||||||
'gpuservice.storage.persistentVolume.capacity': '容量 (GB)',
|
'gpuservice.storage.persistentVolume.capacity': '容量 (GB)',
|
||||||
'gpuservice.storage.persistentVolume.capacity.required':
|
'gpuservice.storage.persistentVolume.capacity.required':
|
||||||
'容量を入力してください',
|
'容量を入力してください',
|
||||||
@@ -157,5 +173,10 @@ export default {
|
|||||||
'gpuservice.storage.temporary.tips':
|
'gpuservice.storage.temporary.tips':
|
||||||
'Data is cleared when the instance stops.',
|
'Data is cleared when the instance stops.',
|
||||||
'gpuservice.storage.persistentVolume.tips':
|
'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': 'グローバルテンプレート'
|
||||||
};
|
};
|
||||||
|
|||||||
+15
-11
@@ -8,7 +8,7 @@ export default {
|
|||||||
'menu.playground.text2images': '画像生成',
|
'menu.playground.text2images': '画像生成',
|
||||||
'menu.playground.video': '動画',
|
'menu.playground.video': '動画',
|
||||||
'menu.compare': '比較',
|
'menu.compare': '比較',
|
||||||
'menu.models': 'モデル',
|
'menu.models': 'モデルサービス',
|
||||||
'menu.models.modelList': 'デプロイと管理',
|
'menu.models.modelList': 'デプロイと管理',
|
||||||
'menu.models.modelCatalog': 'カタログ',
|
'menu.models.modelCatalog': 'カタログ',
|
||||||
'menu.models.catalog': 'モデルカタログ',
|
'menu.models.catalog': 'モデルカタログ',
|
||||||
@@ -23,24 +23,28 @@ export default {
|
|||||||
'menu.resources': 'リソース',
|
'menu.resources': 'リソース',
|
||||||
'menu.apikeys': 'APIキー',
|
'menu.apikeys': 'APIキー',
|
||||||
'menu.users': 'ユーザー',
|
'menu.users': 'ユーザー',
|
||||||
'menu.profile': 'プロフィール',
|
'menu.profile': 'Preferences',
|
||||||
'menu.login': 'ログイン',
|
'menu.login': 'ログイン',
|
||||||
'menu.usage': '使用状況',
|
'menu.usage': '使用状況',
|
||||||
|
'menu.usage.usage': '使用状況',
|
||||||
|
'menu.billingAndUsage': '使用状況と請求',
|
||||||
|
'menu.billingAndUsage.usage': '使用状況',
|
||||||
|
'menu.billingAndUsage.billing': '請求',
|
||||||
'menu.404': '404',
|
'menu.404': '404',
|
||||||
'menu.settings': 'Settings',
|
'menu.settings': 'Settings',
|
||||||
'menu.resources.workers': 'Workers',
|
'menu.resources.workers': 'Workers',
|
||||||
'menu.resources.gpus': 'GPUs',
|
'menu.resources.gpus': 'GPUs',
|
||||||
'menu.resources.modelfiles': 'Model Files',
|
'menu.models.modelfiles': 'Model Files',
|
||||||
'menu.accessControl': 'Access Control',
|
'menu.accessControl': 'Access Control',
|
||||||
'menu.accessControl.apikeys': 'API Keys',
|
'menu.accessControl.apikeys': 'API Keys',
|
||||||
'menu.accessControl.users': 'Users',
|
'menu.accessControl.users': 'Users',
|
||||||
'menu.clusterManagement': 'Cluster Management',
|
'menu.accessControl.organizations': 'Organizations',
|
||||||
'menu.clusterManagement.clusters': 'Clusters',
|
'menu.resources.clusters': 'Clusters',
|
||||||
'menu.clusterManagement.credentials': 'Cloud Credentials',
|
'menu.resources.credentials': 'Cloud Credentials',
|
||||||
'menu.models.userModels': 'My Models',
|
'menu.models.userModels': 'My Models',
|
||||||
'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
'menu.resources.clusterDetail': 'Cluster Detail',
|
||||||
'menu.clusterManagement.clusterCreate': 'Create Cluster',
|
'menu.resources.clusterCreate': 'Create Cluster',
|
||||||
'menu.resources.backendsList': 'Inference Backends',
|
'menu.models.backendsList': 'Inference Backends',
|
||||||
'menu.gpuService': 'GPU Service',
|
'menu.gpuService': 'GPU Service',
|
||||||
'menu.gpuService.instances': 'GPU Instances',
|
'menu.gpuService.instances': 'GPU Instances',
|
||||||
'menu.gpuService.templates': 'Instance Templates',
|
'menu.gpuService.templates': 'Instance Templates',
|
||||||
@@ -53,7 +57,7 @@ export default {
|
|||||||
// 1. 'menu.models.deployment': 'Deployment',
|
// 1. 'menu.models.deployment': 'Deployment',
|
||||||
// 2. 'menu.resources.workers': 'Workers',
|
// 2. 'menu.resources.workers': 'Workers',
|
||||||
// 3. 'menu.resources.gpus': 'GPUs',
|
// 3. 'menu.resources.gpus': 'GPUs',
|
||||||
// 4. 'menu.resources.modelfiles': 'Model Files',
|
// 4. 'menu.models.modelfiles': 'Model Files',
|
||||||
// 5. 'menu.accessControl': 'Access Control',
|
// 5. 'menu.accessControl': 'Access Control',
|
||||||
// 6. 'menu.accessControl.apikeys': 'API Keys',
|
// 6. 'menu.accessControl.apikeys': 'API Keys',
|
||||||
// 7. 'menu.accessControl.users': 'Users',
|
// 7. 'menu.accessControl.users': 'Users',
|
||||||
@@ -63,7 +67,7 @@ export default {
|
|||||||
// 11. 'menu.models.userModels': 'My Models'
|
// 11. 'menu.models.userModels': 'My Models'
|
||||||
// 12. 'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
// 12. 'menu.clusterManagement.clusterDetail': 'Cluster Detail',
|
||||||
// 13. 'menu.clusterManagement.clusterCreate': 'Create Cluster',
|
// 13. 'menu.clusterManagement.clusterCreate': 'Create Cluster',
|
||||||
// 14. 'menu.resources.backendsList': 'Inference Backends',
|
// 14. 'menu.models.backendsList': 'Inference Backends',
|
||||||
// 15. 'menu.models.benchmark': 'Benchmarks',
|
// 15. 'menu.models.benchmark': 'Benchmarks',
|
||||||
// 15. 'menu.models.provider': 'Provider',
|
// 15. 'menu.models.provider': 'Provider',
|
||||||
// 15. 'menu.models.providers': 'Provider',
|
// 15. 'menu.models.providers': 'Provider',
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ export default {
|
|||||||
'models.instance.startHistory': 'Run History',
|
'models.instance.startHistory': 'Run History',
|
||||||
'models.instance.startHistory.tips':
|
'models.instance.startHistory.tips':
|
||||||
'Shows logs from the run before the last error-triggered restart.',
|
'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.add': 'Add LoRA Adapter',
|
||||||
'models.form.lora.select': 'Select LoRA',
|
'models.form.lora.select': 'Select LoRA',
|
||||||
'models.form.lora.name': 'LoRA name',
|
'models.form.lora.name': 'LoRA name',
|
||||||
|
|||||||
@@ -36,9 +36,12 @@ export default {
|
|||||||
'noresult.catalog.nofound': 'No matching models found.',
|
'noresult.catalog.nofound': 'No matching models found.',
|
||||||
'noresult.resources.cluster':
|
'noresult.resources.cluster':
|
||||||
'No clusters available. Add a cluster to get started.',
|
'No clusters available. Add a cluster to get started.',
|
||||||
|
'noresult.resources.k8sCluster':
|
||||||
|
'No clusters available. Add a Kubernetes cluster to get started.',
|
||||||
'noresult.resources.worker':
|
'noresult.resources.worker':
|
||||||
'No workers available. Add a worker to get started.',
|
'No workers available. Add a worker to get started.',
|
||||||
'noresult.resources.gotocluster': 'Create Your First Cluster',
|
'noresult.resources.gotocluster': 'Create Your First Cluster',
|
||||||
|
'noresult.resources.addk8scluster': 'Add a Kubernetes Cluster',
|
||||||
'noresult.resources.gotoworker': 'Add Worker',
|
'noresult.resources.gotoworker': 'Add Worker',
|
||||||
'noresult.benchmark.title': 'No Benchmarks',
|
'noresult.benchmark.title': 'No Benchmarks',
|
||||||
'noresult.benchmark.subTitle': 'No benchmarks have been added yet.',
|
'noresult.benchmark.subTitle': 'No benchmarks have been added yet.',
|
||||||
|
|||||||
@@ -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 GPUStack 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'
|
||||||
|
};
|
||||||
@@ -17,9 +17,15 @@ export default {
|
|||||||
'usage.table.user.apiKeysUsed': 'API Keys Used',
|
'usage.table.user.apiKeysUsed': 'API Keys Used',
|
||||||
'usage.table.lastActive': 'Last Active',
|
'usage.table.lastActive': 'Last Active',
|
||||||
'usage.filter.granularity': 'Granularity',
|
'usage.filter.granularity': 'Granularity',
|
||||||
|
'usage.filter.granularity.hour': '時間',
|
||||||
'usage.filter.granularity.day': 'Day',
|
'usage.filter.granularity.day': 'Day',
|
||||||
'usage.filter.granularity.week': 'Week',
|
'usage.filter.granularity.week': 'Week',
|
||||||
'usage.filter.granularity.month': 'Month',
|
'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.models': 'Models',
|
||||||
'usage.tabs.apikeys': 'API Keys',
|
'usage.tabs.apikeys': 'API Keys',
|
||||||
'usage.tabs.users': 'User',
|
'usage.tabs.users': 'User',
|
||||||
@@ -32,5 +38,70 @@ export default {
|
|||||||
'usage.chart.cached': 'Cached',
|
'usage.chart.cached': 'Cached',
|
||||||
'usage.chart.uncached': 'Uncached',
|
'usage.chart.uncached': 'Uncached',
|
||||||
'usage.chart.inputTokensCached': 'Input Tokens (Cached/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': 'デタッチ済み'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export default {
|
|||||||
'vendor.hygon': 'Hygon',
|
'vendor.hygon': 'Hygon',
|
||||||
'vendor.moorthreads': 'Moore Threads',
|
'vendor.moorthreads': 'Moore Threads',
|
||||||
'vendor.iluvatar': 'Iluvatar',
|
'vendor.iluvatar': 'Iluvatar',
|
||||||
'vendor.metax': 'Metax',
|
'vendor.metax': 'MetaX',
|
||||||
'vendor.cambricon': 'Cambricon',
|
'vendor.cambricon': 'Cambricon',
|
||||||
'vendor.thead': 'T-Head PPU'
|
'vendor.thead': 'T-Head PPU'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default {
|
|||||||
'ai.provider.ollama': 'Ollama',
|
'ai.provider.ollama': 'Ollama',
|
||||||
'ai.provider.openai': 'OpenAI',
|
'ai.provider.openai': 'OpenAI',
|
||||||
'ai.provider.openrouter': 'OpenRouter',
|
'ai.provider.openrouter': 'OpenRouter',
|
||||||
'ai.provider.qwen': 'Qwen',
|
'ai.provider.qwen': 'Alibaba Cloud Model Studio',
|
||||||
'ai.provider.spark': 'Spark',
|
'ai.provider.spark': 'Spark',
|
||||||
'ai.provider.stepfun': 'StepFun',
|
'ai.provider.stepfun': 'StepFun',
|
||||||
'ai.provider.together-ai': 'TogetherAI',
|
'ai.provider.together-ai': 'TogetherAI',
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ export default {
|
|||||||
'apikeys.accessScope.inference': 'Inference APIs',
|
'apikeys.accessScope.inference': 'Inference APIs',
|
||||||
'apikeys.access.permissions': 'Access Permissions',
|
'apikeys.access.permissions': 'Access Permissions',
|
||||||
'apikeys.type.auto': 'Auto-generated',
|
'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) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
|
|||||||
@@ -23,11 +23,12 @@ export default {
|
|||||||
'backend.form.defaultExecuteCommand': 'Команда выполнения по умолчанию',
|
'backend.form.defaultExecuteCommand': 'Команда выполнения по умолчанию',
|
||||||
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}', '{{'port'}}', '{{'worker_ip'}}' и '{{'model_name'}}' заполняются реальными значениями во время запуска`,
|
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}', '{{'port'}}', '{{'worker_ip'}}' и '{{'model_name'}}' заполняются реальными значениями во время запуска`,
|
||||||
'backend.form.defaultBackendParameters': 'Параметры бэкенда по умолчанию',
|
'backend.form.defaultBackendParameters': 'Параметры бэкенда по умолчанию',
|
||||||
'backend.form.parameterFormat': 'Формат параметров',
|
'backend.form.flagFormat': 'Формат флага',
|
||||||
'backend.form.parameterFormat.default': 'По умолчанию бэкенда',
|
'backend.form.flagFormat.tips':
|
||||||
'backend.form.parameterFormat.space': 'Пробел (--key value)',
|
'Формат соединения опции и её значения. Оставьте пустым, чтобы сохранить каждый параметр в исходном виде, без приведения к единому формату.',
|
||||||
'backend.form.parameterFormat.equal': 'Знак равенства (--key=value)',
|
'backend.form.flagFormat.space': 'Разделение пробелом (--key value)',
|
||||||
'backend.form.commonParameters': 'Часто используемые параметры',
|
'backend.form.flagFormat.equal': 'Знак равенства (--key=value)',
|
||||||
|
'backend.form.commonParameters': 'Общие параметры бэкенда',
|
||||||
'backend.form.commonParameters.tips':
|
'backend.form.commonParameters.tips':
|
||||||
'Отображаются как подсказки в поле параметров бэкенда при развёртывании.',
|
'Отображаются как подсказки в поле параметров бэкенда при развёртывании.',
|
||||||
'backend.form.versionConfig': 'Конфигурация версий',
|
'backend.form.versionConfig': 'Конфигурация версий',
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ export default {
|
|||||||
'Stress test for long-context handling. Evaluates KV cache behavior, memory usage, and backend stability.',
|
'Stress test for long-context handling. Evaluates KV cache behavior, memory usage, and backend stability.',
|
||||||
'benchmark.form.profile.heavy.tips':
|
'benchmark.form.profile.heavy.tips':
|
||||||
'Decode-heavy generation benchmark. Measures sustained decoding speed and output token throughput.',
|
'Decode-heavy generation benchmark. Measures sustained decoding speed and output token throughput.',
|
||||||
'benchmark.table.filter.bygpu': 'Filter by GPU',
|
'benchmark.table.filter.bygpu': 'Поиск GPU',
|
||||||
'benchmark.table.filter.bymodel': 'Filter by Model',
|
'benchmark.table.filter.bymodel': 'Поиск модели',
|
||||||
'benchmark.table.filter.bydataset': 'Filter by Dataset',
|
'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.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 GPUStack 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.workerpool.batchSize.desc':
|
||||||
'Количество воркеров, создаваемых одновременно в пуле воркеров',
|
'Количество воркеров, создаваемых одновременно в пуле воркеров',
|
||||||
'clusters.create.addworker.tips':
|
'clusters.create.addworker.tips':
|
||||||
'Пожалуйста, убедитесь, что выполнены <a href={link} target="_blank">предварительные условия</a> для {label} перед выполнением следующей команды.',
|
'Пожалуйста, убедитесь, что выполнены <a href={link} target="_blank">предварительные условия</a> перед выполнением следующей команды.',
|
||||||
'clusters.create.addCommand.tips':
|
'clusters.create.addCommand.tips':
|
||||||
'На воркере, который необходимо добавить, выполните следующую команду, чтобы присоединить его к кластеру.',
|
'На воркере, который необходимо добавить, выполните следующую команду, чтобы присоединить его к кластеру.',
|
||||||
'clusters.create.addCommand.k8s.tips':
|
'clusters.create.addCommand.k8s.tips':
|
||||||
@@ -70,10 +70,13 @@ export default {
|
|||||||
'Для <span class="bold-text">не-Docker</span> кластеров, пожалуйста, регистрируйте кластеры или управляйте пулами воркеров на странице Кластеры.',
|
'Для <span class="bold-text">не-Docker</span> кластеров, пожалуйста, регистрируйте кластеры или управляйте пулами воркеров на странице Кластеры.',
|
||||||
'clusters.addworker.selectGPU': 'Выбрать производителя GPU',
|
'clusters.addworker.selectGPU': 'Выбрать производителя GPU',
|
||||||
'clusters.addworker.selectGPU.multiTag': 'Multi-select',
|
'clusters.addworker.selectGPU.multiTag': 'Multi-select',
|
||||||
'clusters.addworker.selectGPU.singleOnly':
|
'clusters.addworker.selectGPU.subtitle':
|
||||||
'The selected vendor is not in this cluster’s GPU vendor overrides — single-select only. Pick a vendor configured in the overrides to enable multi-select.',
|
'Вы можете выбрать несколько производителей GPU или не выбирать для кластера только с CPU',
|
||||||
'clusters.addworker.checkEnv': 'Проверить окружение',
|
'clusters.addworker.checkEnv': 'Проверить окружение',
|
||||||
|
'clusters.addworker.checkEnv.cpuOnlyTips':
|
||||||
|
'Используйте следующую команду, чтобы убедиться, что в кластере Kubernetes есть хотя бы один готовый узел. Вы регистрируете кластер только с CPU.',
|
||||||
'clusters.addworker.specifyArgs': 'Указать аргументы',
|
'clusters.addworker.specifyArgs': 'Указать аргументы',
|
||||||
|
'clusters.addworker.dtkVersion': 'Версия DTK',
|
||||||
'clusters.addworker.runCommand': 'Выполнить команду',
|
'clusters.addworker.runCommand': 'Выполнить команду',
|
||||||
'clusters.addworker.specifyWorkerIP': 'Указать IP воркера',
|
'clusters.addworker.specifyWorkerIP': 'Указать IP воркера',
|
||||||
'clusters.addworker.detectWorkerIP': 'Автоматически определить IP воркера',
|
'clusters.addworker.detectWorkerIP': 'Автоматически определить IP воркера',
|
||||||
@@ -112,6 +115,8 @@ export default {
|
|||||||
'{count} новых воркеров были добавлены в кластер.',
|
'{count} новых воркеров были добавлены в кластер.',
|
||||||
'clusters.create.serverUrl': 'URL сервера GPUStack',
|
'clusters.create.serverUrl': 'URL сервера GPUStack',
|
||||||
'clusters.create.workerConfig': 'Конфигурация воркера',
|
'clusters.create.workerConfig': 'Конфигурация воркера',
|
||||||
|
'clusters.edit.k8sOptions.changed.tip':
|
||||||
|
'Вы изменили параметры Kubernetes. Чтобы изменения вступили в силу, повторно выполните команду регистрации в целевом кластере.',
|
||||||
'clusters.addworker.containerName': 'Имя контейнера воркера',
|
'clusters.addworker.containerName': 'Имя контейнера воркера',
|
||||||
'clusters.addworker.containerName.tips':
|
'clusters.addworker.containerName.tips':
|
||||||
'Укажите имя для контейнера воркера.',
|
'Укажите имя для контейнера воркера.',
|
||||||
@@ -140,7 +145,7 @@ export default {
|
|||||||
'clusters.addworker.theadNotes-02':
|
'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.',
|
'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':
|
'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 GPUStack 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.title': 'Volume Mounts',
|
||||||
'clusters.volume.name': 'Volume Name',
|
'clusters.volume.name': 'Volume Name',
|
||||||
'clusters.volume.mountPath': 'Container Path',
|
'clusters.volume.mountPath': 'Container Path',
|
||||||
@@ -165,6 +170,10 @@ export default {
|
|||||||
'clusters.volume.configMap.name': 'ConfigMap Name',
|
'clusters.volume.configMap.name': 'ConfigMap Name',
|
||||||
'clusters.volume.configMap.optional': 'Optional',
|
'clusters.volume.configMap.optional': 'Optional',
|
||||||
'clusters.volume.add': 'Add Volume Mount',
|
'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': 'Kubernetes Deployment Options',
|
||||||
'clusters.imageCredentials.title': 'Image Credentials',
|
'clusters.imageCredentials.title': 'Image Credentials',
|
||||||
'clusters.imageCredentials.add': 'Add Credential',
|
'clusters.imageCredentials.add': 'Add Credential',
|
||||||
'clusters.imageCredentials.registry': 'Registry',
|
'clusters.imageCredentials.registry': 'Registry',
|
||||||
@@ -173,18 +182,22 @@ export default {
|
|||||||
'clusters.nodeSelector.title': 'Node Selector',
|
'clusters.nodeSelector.title': 'Node Selector',
|
||||||
'clusters.nodeSelector.tip':
|
'clusters.nodeSelector.tip':
|
||||||
'Pod nodeSelector applied to every worker DaemonSet — only nodes whose labels match are eligible to run the worker.',
|
'Pod nodeSelector applied to every worker DaemonSet — only nodes whose labels match are eligible to run the worker.',
|
||||||
'clusters.gpuVendorOverrides.title': 'GPU Vendor Overrides',
|
'clusters.operatorImage.title': 'Operator Image',
|
||||||
'clusters.gpuVendorOverrides.validate.emptySelector':
|
'clusters.operatorImage.tip':
|
||||||
'The override for {vendor} needs at least one nodeSelector entry — an empty override would leave the multi-vendor manifest unable to target nodes for this runtime.',
|
'Override for the GPUStack Operator container image. Leave empty to use the server default.',
|
||||||
'clusters.gpuVendorOverrides.validate.duplicate':
|
'clusters.namespace.title': 'Namespace',
|
||||||
'Overrides for {v1} and {v2} use the same nodeSelector. Each vendor must scope to a distinct node set, otherwise their worker DaemonSets would compete for the same nodes.',
|
'clusters.namespace.tip':
|
||||||
'clusters.gpuVendorOverrides.validate.keyConflict':
|
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
||||||
'Override for {vendor} reuses key(s) {keys} from the base Node Selector — the CPU worker would simultaneously require and forbid the key and never schedule.',
|
'clusters.clusterType.title': 'Cluster Type',
|
||||||
'clusters.gpuVendorOverrides.tip':
|
'clusters.modelService.title': 'Model Service',
|
||||||
'Required when running multiple GPU vendors in one cluster. Each entry pins that vendor’s worker DaemonSet to nodes matching its nodeSelector, and the CPU worker is steered away from those nodes via a DoesNotExist node-affinity.',
|
'clusters.modelService.tip':
|
||||||
'clusters.gpuVendorOverrides.add': 'Add Override',
|
'For LLM inference and API serving — e.g. exposing model APIs and token-based services.',
|
||||||
'clusters.gpuVendorOverrides.vendor': 'GPU Vendor',
|
'clusters.gpuInstances.title': 'GPU Service',
|
||||||
'clusters.gpuVendorOverrides.nodeSelector': 'Node Selector'
|
'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.'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export default {
|
|||||||
'common.button.enabled': 'Активно',
|
'common.button.enabled': 'Активно',
|
||||||
'common.button.disabled': 'Отключено',
|
'common.button.disabled': 'Отключено',
|
||||||
'common.button.upgrade': 'Обновить',
|
'common.button.upgrade': 'Обновить',
|
||||||
|
'common.enterprise.feature': 'Available in GPUStack Enterprise',
|
||||||
'common.input.holder': 'Введите значение',
|
'common.input.holder': 'Введите значение',
|
||||||
'common.validate.value': 'Поле {name} обязательно',
|
'common.validate.value': 'Поле {name} обязательно',
|
||||||
'common.button.edit': 'Редактировать',
|
'common.button.edit': 'Редактировать',
|
||||||
@@ -55,6 +56,7 @@ export default {
|
|||||||
'common.button.viewevent': 'Просмотр событий',
|
'common.button.viewevent': 'Просмотр событий',
|
||||||
'common.button.recreate': 'Пересоздать',
|
'common.button.recreate': 'Пересоздать',
|
||||||
'common.table.operation': 'Действия',
|
'common.table.operation': 'Действия',
|
||||||
|
'common.table.creator': 'Создатель',
|
||||||
'common.table.createTime': 'Создано',
|
'common.table.createTime': 'Создано',
|
||||||
'common.table.updateTime': 'Обновлено',
|
'common.table.updateTime': 'Обновлено',
|
||||||
'common.table.description': 'Описание',
|
'common.table.description': 'Описание',
|
||||||
@@ -65,6 +67,7 @@ export default {
|
|||||||
'common.search.name.placeholder': 'Фильтр по названию',
|
'common.search.name.placeholder': 'Фильтр по названию',
|
||||||
'common.search.id.placeholder': 'Фильтр по ID',
|
'common.search.id.placeholder': 'Фильтр по ID',
|
||||||
'common.filter.byId': 'Фильтр по ID',
|
'common.filter.byId': 'Фильтр по ID',
|
||||||
|
'common.filter.byCreator': 'Фильтр по создателю',
|
||||||
'common.table.type': 'Тип',
|
'common.table.type': 'Тип',
|
||||||
'common.table.default': 'Значение по умолчанию',
|
'common.table.default': 'Значение по умолчанию',
|
||||||
'common.copy.success': 'Скопировано!',
|
'common.copy.success': 'Скопировано!',
|
||||||
@@ -164,6 +167,7 @@ export default {
|
|||||||
'common.time.hour': 'Час',
|
'common.time.hour': 'Час',
|
||||||
'common.time.minute': 'Минута',
|
'common.time.minute': 'Минута',
|
||||||
'common.issue.report': 'Сообщить о проблеме',
|
'common.issue.report': 'Сообщить о проблеме',
|
||||||
|
'common.github.star.tooltip': 'Поставьте нам звезду на GitHub',
|
||||||
'common.social.discord': 'Присоединиться к Discord',
|
'common.social.discord': 'Присоединиться к Discord',
|
||||||
'common.table.mark': 'Комментарий',
|
'common.table.mark': 'Комментарий',
|
||||||
'common.table.rollback.mark': 'Комментарий к откату',
|
'common.table.rollback.mark': 'Комментарий к откату',
|
||||||
@@ -256,11 +260,15 @@ export default {
|
|||||||
'common.login.auth': 'Аутентификация...',
|
'common.login.auth': 'Аутентификация...',
|
||||||
'common.login.auth.failed': 'Ошибка аутентификации',
|
'common.login.auth.failed': 'Ошибка аутентификации',
|
||||||
'common.login.password': 'Войти с паролем',
|
'common.login.password': 'Войти с паролем',
|
||||||
|
'common.login.username.holder': 'Введите имя пользователя',
|
||||||
|
'common.login.password.holder': 'Введите пароль',
|
||||||
|
'common.login.newpassword.holder': 'Введите новый пароль',
|
||||||
|
'common.login.confirm.holder': 'Введите пароль ещё раз',
|
||||||
'common.external.login': 'Войти через {type}',
|
'common.external.login': 'Войти через {type}',
|
||||||
'common.sso.noConfig':
|
'common.sso.noConfig':
|
||||||
'Единый вход не настроен в этой системе. Пожалуйста, обратитесь к администратору.',
|
'Единый вход не настроен в этой системе. Пожалуйста, обратитесь к администратору.',
|
||||||
'common.button.edit.item': 'Редактировать {name}',
|
'common.button.edit.item': 'Редактировать {name}',
|
||||||
'common.button.copy.item': 'Duplicate {name}',
|
'common.button.copy.item': 'Дублировать {name}',
|
||||||
'common.button.terminal': 'Терминал',
|
'common.button.terminal': 'Терминал',
|
||||||
'common.button.addItem': 'Добавить элемент',
|
'common.button.addItem': 'Добавить элемент',
|
||||||
'common.help.default': 'По умолчанию: {content}',
|
'common.help.default': 'По умолчанию: {content}',
|
||||||
@@ -284,7 +292,9 @@ export default {
|
|||||||
'common.image.limit.height': 'Image height must be {height}.',
|
'common.image.limit.height': 'Image height must be {height}.',
|
||||||
'common.remaining': 'Остаток {count}',
|
'common.remaining': 'Остаток {count}',
|
||||||
'common.max': 'Макс. {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) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ export default {
|
|||||||
'gpuservice.template.command.placeholder':
|
'gpuservice.template.command.placeholder':
|
||||||
'Разделяйте аргументы пробелами; аргументы с пробелами заключайте в кавычки, например: /bin/bash -c "echo hello world"',
|
'Разделяйте аргументы пробелами; аргументы с пробелами заключайте в кавычки, например: /bin/bash -c "echo hello world"',
|
||||||
'gpuservice.template.mountPath': 'Путь монтирования',
|
'gpuservice.template.mountPath': 'Путь монтирования',
|
||||||
|
'gpuservice.template.mountPath.tips':
|
||||||
|
'Путь, по которому том хранилища монтируется по умолчанию при создании экземпляра из этого шаблона. Может использоваться для сохранения данных, которые нужно сохранить во время работы экземпляра.',
|
||||||
'gpuservice.template.containerDisk': 'Диск контейнера (GB)',
|
'gpuservice.template.containerDisk': 'Диск контейнера (GB)',
|
||||||
|
'gpuservice.template.containerDisk.tips':
|
||||||
|
'Размер системного диска контейнера.',
|
||||||
'gpuservice.template.memory': 'Память (GB)',
|
'gpuservice.template.memory': 'Память (GB)',
|
||||||
'gpuservice.instance.containerDisk.remaining':
|
'gpuservice.instance.containerDisk.remaining':
|
||||||
'Диск контейнера (Макс. {count} GB)',
|
'Диск контейнера (Макс. {count} GB)',
|
||||||
@@ -100,18 +104,27 @@ export default {
|
|||||||
'gpuservice.instance.templates': 'Шаблоны экземпляров',
|
'gpuservice.instance.templates': 'Шаблоны экземпляров',
|
||||||
'gpuservice.instance.section.storage': 'Том хранилища',
|
'gpuservice.instance.section.storage': 'Том хранилища',
|
||||||
'gpuservice.instance.type.required': 'Выберите тип экземпляра',
|
'gpuservice.instance.type.required': 'Выберите тип экземпляра',
|
||||||
|
'gpuservice.instance.type.noAvailable': 'Нет доступных типов экземпляров',
|
||||||
'gpuservice.instance.gpuCount': 'Количество GPU',
|
'gpuservice.instance.gpuCount': 'Количество GPU',
|
||||||
'gpuservice.instance.gpuCount.required': 'Введите количество GPU',
|
'gpuservice.instance.gpuCount.required': 'Введите количество GPU',
|
||||||
'gpuservice.instance.gpuCount.max': 'Выберите максимум {count} GPU-карт',
|
'gpuservice.instance.gpuCount.max': 'Выберите максимум {count} GPU-карт',
|
||||||
'gpuservice.instance.gpuCount.min': 'Выберите минимум {count} GPU-карт',
|
'gpuservice.instance.gpuCount.min': 'Выберите минимум {count} GPU-карт',
|
||||||
|
'gpuservice.instance.cpuCount.max': 'Выберите максимум {count} ядер CPU',
|
||||||
|
'gpuservice.instance.cpuCount.min': 'Выберите минимум {count} ядер CPU',
|
||||||
'gpuservice.instance.gpuCount.noAvailable':
|
'gpuservice.instance.gpuCount.noAvailable':
|
||||||
'Нет доступных ресурсов GPU, выберите другой тип экземпляра.',
|
'Нет доступных ресурсов GPU, выберите другой тип экземпляра.',
|
||||||
'gpuservice.instance.gpuCount.zero': 'Только CPU, для подготовки окружения.',
|
'gpuservice.instance.gpuCount.zero': 'Только CPU, для подготовки окружения.',
|
||||||
'gpuservice.instance.stock': 'Остаток',
|
'gpuservice.instance.stock': 'Остаток',
|
||||||
'gpuservice.instance.sliced': 'Разделено',
|
'gpuservice.instance.sliced': 'Разделено',
|
||||||
'gpuservice.instance.memory': 'Память',
|
'gpuservice.instance.memory': 'VRAM',
|
||||||
'gpuservice.instance.ram': 'RAM',
|
'gpuservice.instance.ram': 'RAM',
|
||||||
|
'gpuservice.instance.os': 'ОС',
|
||||||
|
'gpuservice.instance.arch': 'Архитектура',
|
||||||
'gpuservice.instance.disk': 'Диск',
|
'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.type.placeholder': 'Поиск по имени',
|
||||||
'gpuservice.instance.search.template.placeholder':
|
'gpuservice.instance.search.template.placeholder':
|
||||||
'Поиск по имени шаблона, образу или пути монтирования',
|
'Поиск по имени шаблона, образу или пути монтирования',
|
||||||
@@ -143,8 +156,8 @@ export default {
|
|||||||
'gpuservice.storage.accessMode': 'Режим доступа',
|
'gpuservice.storage.accessMode': 'Режим доступа',
|
||||||
'gpuservice.storage.persistent': 'Постоянное',
|
'gpuservice.storage.persistent': 'Постоянное',
|
||||||
'gpuservice.storage.temporary': 'Временное',
|
'gpuservice.storage.temporary': 'Временное',
|
||||||
'gpuservice.storage.persistentVolume': 'Постоянный том',
|
'gpuservice.storage.persistentVolume': 'Постоянное',
|
||||||
'gpuservice.storage.persistentVolume.required': 'Выберите постоянный том',
|
'gpuservice.storage.persistentVolume.required': 'Выберите хранилище',
|
||||||
'gpuservice.storage.persistentVolume.capacity': 'Ёмкость (ГБ)',
|
'gpuservice.storage.persistentVolume.capacity': 'Ёмкость (ГБ)',
|
||||||
'gpuservice.storage.persistentVolume.capacity.required': 'Введите ёмкость',
|
'gpuservice.storage.persistentVolume.capacity.required': 'Введите ёмкость',
|
||||||
'gpuservice.storage.persistentVolume.releaseWithInstance':
|
'gpuservice.storage.persistentVolume.releaseWithInstance':
|
||||||
@@ -157,5 +170,10 @@ export default {
|
|||||||
'gpuservice.storage.temporary.tips':
|
'gpuservice.storage.temporary.tips':
|
||||||
'Data is cleared when the instance stops.',
|
'Data is cleared when the instance stops.',
|
||||||
'gpuservice.storage.persistentVolume.tips':
|
'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': 'Глобальные шаблоны'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export default {
|
|||||||
'menu.playground.text2images': 'Генерация изображений',
|
'menu.playground.text2images': 'Генерация изображений',
|
||||||
'menu.playground.video': 'Видео',
|
'menu.playground.video': 'Видео',
|
||||||
'menu.compare': 'Сравнение',
|
'menu.compare': 'Сравнение',
|
||||||
'menu.models': 'Модели',
|
'menu.models': 'Сервисы моделей',
|
||||||
'menu.models.modelList': 'Развертывание и управление',
|
'menu.models.modelList': 'Развертывание и управление',
|
||||||
'menu.models.modelCatalog': 'Каталог',
|
'menu.models.modelCatalog': 'Каталог',
|
||||||
'menu.models.catalog': 'Каталог моделей',
|
'menu.models.catalog': 'Каталог моделей',
|
||||||
@@ -23,23 +23,27 @@ export default {
|
|||||||
'menu.resources': 'Ресурсы',
|
'menu.resources': 'Ресурсы',
|
||||||
'menu.apikeys': 'API-ключи',
|
'menu.apikeys': 'API-ключи',
|
||||||
'menu.users': 'Пользователи',
|
'menu.users': 'Пользователи',
|
||||||
'menu.profile': 'Профиль',
|
'menu.profile': 'Preferences',
|
||||||
'menu.login': 'Авторизация',
|
'menu.login': 'Авторизация',
|
||||||
'menu.usage': 'Использование',
|
'menu.usage': 'Использование',
|
||||||
|
'menu.usage.usage': 'Использование',
|
||||||
|
'menu.billingAndUsage': 'Использование и биллинг',
|
||||||
|
'menu.billingAndUsage.usage': 'Использование',
|
||||||
|
'menu.billingAndUsage.billing': 'Биллинг',
|
||||||
'menu.404': 'Ошибка 404',
|
'menu.404': 'Ошибка 404',
|
||||||
'menu.resources.workers': 'Воркеры',
|
'menu.resources.workers': 'Воркеры',
|
||||||
'menu.resources.gpus': 'GPUs',
|
'menu.resources.gpus': 'GPUs',
|
||||||
'menu.resources.modelfiles': 'Файлы модлей',
|
'menu.models.modelfiles': 'Файлы модлей',
|
||||||
'menu.accessControl': 'Управление доступом',
|
'menu.accessControl': 'Управление доступом',
|
||||||
'menu.accessControl.apikeys': 'API Ключи',
|
'menu.accessControl.apikeys': 'API Ключи',
|
||||||
'menu.accessControl.users': 'Пользователи',
|
'menu.accessControl.users': 'Пользователи',
|
||||||
'menu.clusterManagement': 'Управление кластерами',
|
'menu.accessControl.organizations': 'Организации',
|
||||||
'menu.clusterManagement.clusters': 'Кластеры',
|
'menu.resources.clusters': 'Кластеры',
|
||||||
'menu.clusterManagement.credentials': 'Облачные аккаунты',
|
'menu.resources.credentials': 'Облачные аккаунты',
|
||||||
'menu.models.userModels': 'Мои модели',
|
'menu.models.userModels': 'Мои модели',
|
||||||
'menu.clusterManagement.clusterDetail': 'Детали кластера',
|
'menu.resources.clusterDetail': 'Детали кластера',
|
||||||
'menu.clusterManagement.clusterCreate': 'Создать кластер',
|
'menu.resources.clusterCreate': 'Создать кластер',
|
||||||
'menu.resources.backendsList': 'Бэкенды запуска',
|
'menu.models.backendsList': 'Бэкенды запуска',
|
||||||
'menu.settings': 'Settings',
|
'menu.settings': 'Settings',
|
||||||
'menu.gpuService': 'GPU Service',
|
'menu.gpuService': 'GPU Service',
|
||||||
'menu.gpuService.instances': 'GPU Instances',
|
'menu.gpuService.instances': 'GPU Instances',
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ export default {
|
|||||||
'models.instance.startHistory': 'Run History',
|
'models.instance.startHistory': 'Run History',
|
||||||
'models.instance.startHistory.tips':
|
'models.instance.startHistory.tips':
|
||||||
'Shows logs from the run before the last error-triggered restart.',
|
'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.add': 'Add LoRA Adapter',
|
||||||
'models.form.lora.select': 'Select LoRA',
|
'models.form.lora.select': 'Select LoRA',
|
||||||
'models.form.lora.name': 'LoRA name',
|
'models.form.lora.name': 'LoRA name',
|
||||||
|
|||||||
@@ -37,9 +37,12 @@ export default {
|
|||||||
'noresult.catalog.nofound': 'Подходящие модели не найдены.',
|
'noresult.catalog.nofound': 'Подходящие модели не найдены.',
|
||||||
'noresult.resources.cluster':
|
'noresult.resources.cluster':
|
||||||
'No clusters available. Add a cluster to get started.',
|
'No clusters available. Add a cluster to get started.',
|
||||||
|
'noresult.resources.k8sCluster':
|
||||||
|
'No clusters available. Add a Kubernetes cluster to get started.',
|
||||||
'noresult.resources.worker':
|
'noresult.resources.worker':
|
||||||
'No workers available. Add a worker to get started.',
|
'No workers available. Add a worker to get started.',
|
||||||
'noresult.resources.gotocluster': 'Create Your First Cluster',
|
'noresult.resources.gotocluster': 'Create Your First Cluster',
|
||||||
|
'noresult.resources.addk8scluster': 'Add a Kubernetes Cluster',
|
||||||
'noresult.resources.gotoworker': 'Add Worker',
|
'noresult.resources.gotoworker': 'Add Worker',
|
||||||
'noresult.benchmark.title': 'No Benchmarks',
|
'noresult.benchmark.title': 'No Benchmarks',
|
||||||
'noresult.benchmark.subTitle': 'No benchmarks have been added yet.',
|
'noresult.benchmark.subTitle': 'No benchmarks have been added yet.',
|
||||||
@@ -67,8 +70,7 @@ export default {
|
|||||||
'noresult.gpuservice.storage.subTitle': 'Хранилища ещё не добавлены.',
|
'noresult.gpuservice.storage.subTitle': 'Хранилища ещё не добавлены.',
|
||||||
'noresult.gpuservice.storage.nofound': 'Подходящие хранилища не найдены.',
|
'noresult.gpuservice.storage.nofound': 'Подходящие хранилища не найдены.',
|
||||||
'noresult.gpuservice.storageType.title': 'Нет типов хранилищ',
|
'noresult.gpuservice.storageType.title': 'Нет типов хранилищ',
|
||||||
'noresult.gpuservice.storageType.subTitle':
|
'noresult.gpuservice.storageType.subTitle': 'Типы хранилищ ещё не добавлены.',
|
||||||
'Типы хранилищ ещё не добавлены.',
|
|
||||||
'noresult.gpuservice.storageType.nofound':
|
'noresult.gpuservice.storageType.nofound':
|
||||||
'Подходящие типы хранилищ не найдены.',
|
'Подходящие типы хранилищ не найдены.',
|
||||||
'noresult.gpuservice.sshkey.title': 'Нет открытых ключей SSH',
|
'noresult.gpuservice.sshkey.title': 'Нет открытых ключей SSH',
|
||||||
|
|||||||
@@ -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 GPUStack 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'
|
||||||
|
};
|
||||||
@@ -17,9 +17,15 @@ export default {
|
|||||||
'usage.table.user.apiKeysUsed': 'API Keys Used',
|
'usage.table.user.apiKeysUsed': 'API Keys Used',
|
||||||
'usage.table.lastActive': 'Last Active',
|
'usage.table.lastActive': 'Last Active',
|
||||||
'usage.filter.granularity': 'Granularity',
|
'usage.filter.granularity': 'Granularity',
|
||||||
|
'usage.filter.granularity.hour': 'Час',
|
||||||
'usage.filter.granularity.day': 'Day',
|
'usage.filter.granularity.day': 'Day',
|
||||||
'usage.filter.granularity.week': 'Week',
|
'usage.filter.granularity.week': 'Week',
|
||||||
'usage.filter.granularity.month': 'Month',
|
'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.models': 'Models',
|
||||||
'usage.tabs.apikeys': 'API Keys',
|
'usage.tabs.apikeys': 'API Keys',
|
||||||
'usage.tabs.users': 'User',
|
'usage.tabs.users': 'User',
|
||||||
@@ -32,5 +38,70 @@ export default {
|
|||||||
'usage.chart.cached': 'Cached',
|
'usage.chart.cached': 'Cached',
|
||||||
'usage.chart.uncached': 'Uncached',
|
'usage.chart.uncached': 'Uncached',
|
||||||
'usage.chart.inputTokensCached': 'Input Tokens (Cached/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': 'Отключён'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export default {
|
|||||||
'vendor.hygon': 'Hygon',
|
'vendor.hygon': 'Hygon',
|
||||||
'vendor.moorthreads': 'Moore Threads',
|
'vendor.moorthreads': 'Moore Threads',
|
||||||
'vendor.iluvatar': 'Iluvatar',
|
'vendor.iluvatar': 'Iluvatar',
|
||||||
'vendor.metax': 'Metax',
|
'vendor.metax': 'MetaX',
|
||||||
'vendor.cambricon': 'Cambricon',
|
'vendor.cambricon': 'Cambricon',
|
||||||
'vendor.thead': 'T-Head PPU'
|
'vendor.thead': 'T-Head PPU'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default {
|
|||||||
'ai.provider.ollama': 'Ollama',
|
'ai.provider.ollama': 'Ollama',
|
||||||
'ai.provider.openai': 'OpenAI',
|
'ai.provider.openai': 'OpenAI',
|
||||||
'ai.provider.openrouter': 'OpenRouter',
|
'ai.provider.openrouter': 'OpenRouter',
|
||||||
'ai.provider.qwen': 'Qwen',
|
'ai.provider.qwen': 'Alibaba Cloud Model Studio',
|
||||||
'ai.provider.spark': 'Spark',
|
'ai.provider.spark': 'Spark',
|
||||||
'ai.provider.stepfun': 'StepFun',
|
'ai.provider.stepfun': 'StepFun',
|
||||||
'ai.provider.together-ai': 'TogetherAI',
|
'ai.provider.together-ai': 'TogetherAI',
|
||||||
|
|||||||
@@ -23,5 +23,7 @@ export default {
|
|||||||
'apikeys.accessScope.inference': 'Inference APIs',
|
'apikeys.accessScope.inference': 'Inference APIs',
|
||||||
'apikeys.access.permissions': 'Access Permissions',
|
'apikeys.access.permissions': 'Access Permissions',
|
||||||
'apikeys.type.auto': 'Auto-generated',
|
'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': '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.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.defaultBackendParameters': 'Varsayılan Altyapı Parametreleri',
|
||||||
'backend.form.parameterFormat': 'Parametre Biçimi',
|
'backend.form.flagFormat': 'Bayrak Biçimi',
|
||||||
'backend.form.parameterFormat.default': 'Varsayılan',
|
'backend.form.flagFormat.tips':
|
||||||
'backend.form.parameterFormat.space': 'Boşluk (--key value)',
|
'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.parameterFormat.equal': 'Eşittir (--key=value)',
|
'backend.form.flagFormat.space': 'Boşlukla Ayır (--key value)',
|
||||||
'backend.form.commonParameters': 'Yaygın Parametreler',
|
'backend.form.flagFormat.equal': 'Eşittir İşareti (--key=value)',
|
||||||
|
'backend.form.commonParameters': 'Ortak Altyapı Parametreleri',
|
||||||
'backend.form.commonParameters.tips':
|
'backend.form.commonParameters.tips':
|
||||||
'Dağıtım sırasında altyapı parametreleri girişinde öneri olarak gösterilir.',
|
'Dağıtım sırasında altyapı parametreleri girişinde öneri olarak gösterilir.',
|
||||||
'backend.form.versionConfig': 'Sürüm Yapılandırması',
|
'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.',
|
'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':
|
'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.',
|
'Çö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.bygpu': 'GPU ara',
|
||||||
'benchmark.table.filter.bymodel': 'Modele göre filtrele',
|
'benchmark.table.filter.bymodel': 'Model ara',
|
||||||
'benchmark.table.filter.bydataset': 'Veri Kümesine göre filtrele',
|
'benchmark.table.filter.bydataset': 'Veri Kümesine göre filtrele',
|
||||||
'benchmark.table.filter.byProfile': 'Profile göre filtrele',
|
'benchmark.table.filter.byProfile': 'Profile göre filtrele',
|
||||||
'benchmark.table.avg': 'Ort.',
|
'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 GPUStack 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.workerpool.batchSize.desc':
|
||||||
'İşçi havuzunda eşzamanlı olarak oluşturulan işçi düğüm sayısı',
|
'İşçi havuzunda eşzamanlı olarak oluşturulan işçi düğüm sayısı',
|
||||||
'clusters.create.addworker.tips':
|
'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':
|
'clusters.create.addCommand.tips':
|
||||||
'Eklenmesi gereken İşçi Düğümde, kümeye katılması için aşağıdaki komutu çalıştırın.',
|
'Eklenmesi gereken İşçi Düğümde, kümeye katılması için aşağıdaki komutu çalıştırın.',
|
||||||
'clusters.create.addCommand.k8s.tips':
|
'clusters.create.addCommand.k8s.tips':
|
||||||
@@ -70,10 +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.',
|
'<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': 'GPU Üreticisi Seç',
|
||||||
'clusters.addworker.selectGPU.multiTag': 'Multi-select',
|
'clusters.addworker.selectGPU.multiTag': 'Multi-select',
|
||||||
'clusters.addworker.selectGPU.singleOnly':
|
'clusters.addworker.selectGPU.subtitle':
|
||||||
'The selected vendor is not in this cluster’s GPU vendor overrides — single-select only. Pick a vendor configured in the overrides to enable multi-select.',
|
'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': '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.specifyArgs': 'Argümanları Belirle',
|
||||||
|
'clusters.addworker.dtkVersion': 'DTK Sürümü',
|
||||||
'clusters.addworker.runCommand': 'Komutu Çalıştır',
|
'clusters.addworker.runCommand': 'Komutu Çalıştır',
|
||||||
'clusters.addworker.specifyWorkerIP': "İşçi Düğüm IP'si",
|
'clusters.addworker.specifyWorkerIP': "İşçi Düğüm IP'si",
|
||||||
'clusters.addworker.detectWorkerIP': "İşçi Düğüm IP'sini Otomatik Algıla",
|
'clusters.addworker.detectWorkerIP': "İşçi Düğüm IP'sini Otomatik Algıla",
|
||||||
@@ -112,6 +115,8 @@ export default {
|
|||||||
'{count} yeni işçi düğüm kümeye eklendi.',
|
'{count} yeni işçi düğüm kümeye eklendi.',
|
||||||
'clusters.create.serverUrl': "GPUStack Sunucu URL'si",
|
'clusters.create.serverUrl': "GPUStack Sunucu URL'si",
|
||||||
'clusters.create.workerConfig': 'İşçi Düğüm Yapılandırması',
|
'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': 'İşçi Düğüm Konteyner Adı',
|
||||||
'clusters.addworker.containerName.tips':
|
'clusters.addworker.containerName.tips':
|
||||||
'İşçi düğüm konteyneri için bir ad belirtin.',
|
'İşçi düğüm konteyneri için bir ad belirtin.',
|
||||||
@@ -140,7 +145,7 @@ export default {
|
|||||||
'clusters.addworker.theadNotes-02':
|
'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.',
|
'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':
|
'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.',
|
'GPUStack\'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.title': 'Volume Mounts',
|
||||||
'clusters.volume.name': 'Volume Name',
|
'clusters.volume.name': 'Volume Name',
|
||||||
'clusters.volume.mountPath': 'Container Path',
|
'clusters.volume.mountPath': 'Container Path',
|
||||||
@@ -165,6 +170,10 @@ export default {
|
|||||||
'clusters.volume.configMap.name': 'ConfigMap Name',
|
'clusters.volume.configMap.name': 'ConfigMap Name',
|
||||||
'clusters.volume.configMap.optional': 'Optional',
|
'clusters.volume.configMap.optional': 'Optional',
|
||||||
'clusters.volume.add': 'Add Volume Mount',
|
'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': 'Kubernetes Deployment Options',
|
||||||
'clusters.imageCredentials.title': 'Image Credentials',
|
'clusters.imageCredentials.title': 'Image Credentials',
|
||||||
'clusters.imageCredentials.add': 'Add Credential',
|
'clusters.imageCredentials.add': 'Add Credential',
|
||||||
'clusters.imageCredentials.registry': 'Registry',
|
'clusters.imageCredentials.registry': 'Registry',
|
||||||
@@ -173,16 +182,20 @@ export default {
|
|||||||
'clusters.nodeSelector.title': 'Node Selector',
|
'clusters.nodeSelector.title': 'Node Selector',
|
||||||
'clusters.nodeSelector.tip':
|
'clusters.nodeSelector.tip':
|
||||||
'Pod nodeSelector applied to every worker DaemonSet — only nodes whose labels match are eligible to run the worker.',
|
'Pod nodeSelector applied to every worker DaemonSet — only nodes whose labels match are eligible to run the worker.',
|
||||||
'clusters.gpuVendorOverrides.title': 'GPU Vendor Overrides',
|
'clusters.operatorImage.title': 'Operator Image',
|
||||||
'clusters.gpuVendorOverrides.validate.emptySelector':
|
'clusters.operatorImage.tip':
|
||||||
'The override for {vendor} needs at least one nodeSelector entry — an empty override would leave the multi-vendor manifest unable to target nodes for this runtime.',
|
'Override for the GPUStack Operator container image. Leave empty to use the server default.',
|
||||||
'clusters.gpuVendorOverrides.validate.duplicate':
|
'clusters.namespace.title': 'Namespace',
|
||||||
'Overrides for {v1} and {v2} use the same nodeSelector. Each vendor must scope to a distinct node set, otherwise their worker DaemonSets would compete for the same nodes.',
|
'clusters.namespace.tip':
|
||||||
'clusters.gpuVendorOverrides.validate.keyConflict':
|
'Kubernetes namespace the cluster’s manifests render into. Leave empty to use gpustack-system.',
|
||||||
'Override for {vendor} reuses key(s) {keys} from the base Node Selector — the CPU worker would simultaneously require and forbid the key and never schedule.',
|
'clusters.clusterType.title': 'Cluster Type',
|
||||||
'clusters.gpuVendorOverrides.tip':
|
'clusters.modelService.title': 'Model Service',
|
||||||
'Required when running multiple GPU vendors in one cluster. Each entry pins that vendor’s worker DaemonSet to nodes matching its nodeSelector, and the CPU worker is steered away from those nodes via a DoesNotExist node-affinity.',
|
'clusters.modelService.tip':
|
||||||
'clusters.gpuVendorOverrides.add': 'Add Override',
|
'For LLM inference and API serving — e.g. exposing model APIs and token-based services.',
|
||||||
'clusters.gpuVendorOverrides.vendor': 'GPU Vendor',
|
'clusters.gpuInstances.title': 'GPU Service',
|
||||||
'clusters.gpuVendorOverrides.nodeSelector': 'Node Selector'
|
'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.enabled': 'Etkin',
|
||||||
'common.button.disabled': 'Devre dışı',
|
'common.button.disabled': 'Devre dışı',
|
||||||
'common.button.upgrade': 'Yükselt',
|
'common.button.upgrade': 'Yükselt',
|
||||||
|
'common.enterprise.feature': 'Available in GPUStack Enterprise',
|
||||||
'common.input.holder': 'Lütfen girin',
|
'common.input.holder': 'Lütfen girin',
|
||||||
'common.validate.value': '{name} değeri gereklidir',
|
'common.validate.value': '{name} değeri gereklidir',
|
||||||
'common.button.edit': 'Düzenle',
|
'common.button.edit': 'Düzenle',
|
||||||
@@ -55,6 +56,7 @@ export default {
|
|||||||
'common.button.viewevent': 'Olayları Görüntüle',
|
'common.button.viewevent': 'Olayları Görüntüle',
|
||||||
'common.button.recreate': 'Yeniden Oluştur',
|
'common.button.recreate': 'Yeniden Oluştur',
|
||||||
'common.table.operation': 'İşlemler',
|
'common.table.operation': 'İşlemler',
|
||||||
|
'common.table.creator': 'Oluşturan',
|
||||||
'common.table.createTime': 'Oluşturulma',
|
'common.table.createTime': 'Oluşturulma',
|
||||||
'common.table.updateTime': 'Güncellenme',
|
'common.table.updateTime': 'Güncellenme',
|
||||||
'common.table.description': 'Açıklama',
|
'common.table.description': 'Açıklama',
|
||||||
@@ -65,6 +67,7 @@ export default {
|
|||||||
'common.search.name.placeholder': 'ada göre filtrele',
|
'common.search.name.placeholder': 'ada göre filtrele',
|
||||||
'common.search.id.placeholder': 'kimliğe göre filtrele',
|
'common.search.id.placeholder': 'kimliğe göre filtrele',
|
||||||
'common.filter.byId': '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.type': 'Tür',
|
||||||
'common.table.default': 'Varsayılan Değer',
|
'common.table.default': 'Varsayılan Değer',
|
||||||
'common.copy.success': 'Kopyalama başarılı!',
|
'common.copy.success': 'Kopyalama başarılı!',
|
||||||
@@ -164,6 +167,7 @@ export default {
|
|||||||
'common.time.hour': 'saat',
|
'common.time.hour': 'saat',
|
||||||
'common.time.minute': 'dakika',
|
'common.time.minute': 'dakika',
|
||||||
'common.issue.report': 'Sorun bildir',
|
'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.social.discord': "Discord'umuza Katılın",
|
||||||
'common.table.mark': 'Yorum',
|
'common.table.mark': 'Yorum',
|
||||||
'common.table.rollback.mark': 'Geri Alma Yorumu',
|
'common.table.rollback.mark': 'Geri Alma Yorumu',
|
||||||
@@ -260,6 +264,10 @@ export default {
|
|||||||
'common.login.auth': 'Kimlik doğrulanıyor...',
|
'common.login.auth': 'Kimlik doğrulanıyor...',
|
||||||
'common.login.auth.failed': 'Kimlik doğrulama başarısız',
|
'common.login.auth.failed': 'Kimlik doğrulama başarısız',
|
||||||
'common.login.password': 'Şifre ile giriş yap',
|
'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.external.login': '{type} ile giriş yap',
|
||||||
'common.sso.noConfig':
|
'common.sso.noConfig':
|
||||||
'Bu sistemde çoklu oturum açma etkinleştirilmemiş. Lütfen yöneticinize başvurun.',
|
'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.image.limit.height': 'Image height must be {height}.',
|
||||||
'common.remaining': 'Kalan {count}',
|
'common.remaining': 'Kalan {count}',
|
||||||
'common.max': 'Maks. {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':
|
'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"',
|
'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': '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': 'Konteyner Diski (GB)',
|
||||||
|
'gpuservice.template.containerDisk.tips': 'Konteyner sistem diskinin boyutu.',
|
||||||
'gpuservice.template.memory': 'Bellek (GB)',
|
'gpuservice.template.memory': 'Bellek (GB)',
|
||||||
'gpuservice.instance.containerDisk.remaining':
|
'gpuservice.instance.containerDisk.remaining':
|
||||||
'Konteyner Diski (Maks. {count} GB)',
|
'Konteyner Diski (Maks. {count} GB)',
|
||||||
@@ -97,18 +100,27 @@ export default {
|
|||||||
'gpuservice.instance.templates': 'Örnek Şablonları',
|
'gpuservice.instance.templates': 'Örnek Şablonları',
|
||||||
'gpuservice.instance.section.storage': 'Depolama Hacmi',
|
'gpuservice.instance.section.storage': 'Depolama Hacmi',
|
||||||
'gpuservice.instance.type.required': 'Lütfen bir örnek türü seçin',
|
'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': 'GPU Sayısı',
|
||||||
'gpuservice.instance.gpuCount.required': 'Lütfen GPU sayısını girin',
|
'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.max': 'En fazla {count} GPU kartı seçin',
|
||||||
'gpuservice.instance.gpuCount.min': 'En az {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':
|
'gpuservice.instance.gpuCount.noAvailable':
|
||||||
'Kullanılabilir GPU kaynağı yok, lütfen başka bir örnek türü seçin.',
|
'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.',
|
'gpuservice.instance.gpuCount.zero': 'Yalnızca CPU, ortam hazırlığı için.',
|
||||||
'gpuservice.instance.stock': 'Stok',
|
'gpuservice.instance.stock': 'Stok',
|
||||||
'gpuservice.instance.sliced': 'Bölünmüş',
|
'gpuservice.instance.sliced': 'Bölünmüş',
|
||||||
'gpuservice.instance.memory': 'Bellek',
|
'gpuservice.instance.memory': 'VRAM',
|
||||||
'gpuservice.instance.ram': 'RAM',
|
'gpuservice.instance.ram': 'RAM',
|
||||||
|
'gpuservice.instance.os': 'OS',
|
||||||
|
'gpuservice.instance.arch': 'Mimari',
|
||||||
'gpuservice.instance.disk': 'Disk',
|
'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.type.placeholder': 'Ada göre ara',
|
||||||
'gpuservice.instance.search.template.placeholder':
|
'gpuservice.instance.search.template.placeholder':
|
||||||
'Şablon adına, imaja veya bağlama yoluna göre ara',
|
'Şablon adına, imaja veya bağlama yoluna göre ara',
|
||||||
@@ -141,9 +153,8 @@ export default {
|
|||||||
'gpuservice.storage.accessMode': 'Erişim Modu',
|
'gpuservice.storage.accessMode': 'Erişim Modu',
|
||||||
'gpuservice.storage.persistent': 'Kalıcı',
|
'gpuservice.storage.persistent': 'Kalıcı',
|
||||||
'gpuservice.storage.temporary': 'Geçici',
|
'gpuservice.storage.temporary': 'Geçici',
|
||||||
'gpuservice.storage.persistentVolume': 'Kalıcı Hacim',
|
'gpuservice.storage.persistentVolume': 'Kalıcı',
|
||||||
'gpuservice.storage.persistentVolume.required':
|
'gpuservice.storage.persistentVolume.required': 'Lütfen bir depolama seçin',
|
||||||
'Lütfen bir kalıcı hacim seçin',
|
|
||||||
'gpuservice.storage.persistentVolume.capacity': 'Kapasite (GB)',
|
'gpuservice.storage.persistentVolume.capacity': 'Kapasite (GB)',
|
||||||
'gpuservice.storage.persistentVolume.capacity.required':
|
'gpuservice.storage.persistentVolume.capacity.required':
|
||||||
'Lütfen kapasiteyi girin',
|
'Lütfen kapasiteyi girin',
|
||||||
@@ -157,5 +168,10 @@ export default {
|
|||||||
'gpuservice.storage.temporary.tips':
|
'gpuservice.storage.temporary.tips':
|
||||||
'Data is cleared when the instance stops.',
|
'Data is cleared when the instance stops.',
|
||||||
'gpuservice.storage.persistentVolume.tips':
|
'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'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export default {
|
|||||||
'menu.playground.text2images': 'Görsel',
|
'menu.playground.text2images': 'Görsel',
|
||||||
'menu.playground.video': 'Video',
|
'menu.playground.video': 'Video',
|
||||||
'menu.compare': 'Karşılaştır',
|
'menu.compare': 'Karşılaştır',
|
||||||
'menu.models': 'Modeller',
|
'menu.models': 'Model Hizmetleri',
|
||||||
'menu.models.modelList': 'Dağıt ve Yönet',
|
'menu.models.modelList': 'Dağıt ve Yönet',
|
||||||
'menu.models.modelCatalog': 'Katalog',
|
'menu.models.modelCatalog': 'Katalog',
|
||||||
'menu.models.catalog': 'Model Kataloğu',
|
'menu.models.catalog': 'Model Kataloğu',
|
||||||
@@ -25,20 +25,24 @@ export default {
|
|||||||
'menu.users': 'Kullanıcılar',
|
'menu.users': 'Kullanıcılar',
|
||||||
'menu.resources.workers': 'İşçi Düğümler',
|
'menu.resources.workers': 'İşçi Düğümler',
|
||||||
'menu.resources.gpus': "GPU'lar",
|
'menu.resources.gpus': "GPU'lar",
|
||||||
'menu.resources.modelfiles': 'Model Dosyaları',
|
'menu.models.modelfiles': 'Model Dosyaları',
|
||||||
'menu.accessControl': 'Erişim Kontrolü',
|
'menu.accessControl': 'Erişim Kontrolü',
|
||||||
'menu.accessControl.apikeys': 'API Anahtarları',
|
'menu.accessControl.apikeys': 'API Anahtarları',
|
||||||
'menu.accessControl.users': 'Kullanıcılar',
|
'menu.accessControl.users': 'Kullanıcılar',
|
||||||
'menu.profile': 'Profil',
|
'menu.accessControl.organizations': 'Organizasyonlar',
|
||||||
|
'menu.profile': 'Preferences',
|
||||||
'menu.login': 'Giriş',
|
'menu.login': 'Giriş',
|
||||||
'menu.usage': 'Kullanım',
|
'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.404': '404',
|
||||||
'menu.clusterManagement': 'Küme Yönetimi',
|
'menu.resources.clusters': 'Kümeler',
|
||||||
'menu.clusterManagement.clusters': 'Kümeler',
|
'menu.resources.credentials': 'Bulut Kimlik Bilgileri',
|
||||||
'menu.clusterManagement.credentials': 'Bulut Kimlik Bilgileri',
|
'menu.resources.clusterDetail': 'Küme Detayı',
|
||||||
'menu.clusterManagement.clusterDetail': 'Küme Detayı',
|
'menu.resources.clusterCreate': 'Küme Oluştur',
|
||||||
'menu.clusterManagement.clusterCreate': 'Küme Oluştur',
|
'menu.models.backendsList': 'Çıkarım Altyapıları',
|
||||||
'menu.resources.backendsList': 'Çıkarım Altyapıları',
|
|
||||||
'menu.models.instances': 'Instances',
|
'menu.models.instances': 'Instances',
|
||||||
'menu.settings': 'Settings',
|
'menu.settings': 'Settings',
|
||||||
'menu.gpuService': 'GPU Service',
|
'menu.gpuService': 'GPU Service',
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ export default {
|
|||||||
'models.instance.startHistory': 'Run History',
|
'models.instance.startHistory': 'Run History',
|
||||||
'models.instance.startHistory.tips':
|
'models.instance.startHistory.tips':
|
||||||
'Shows logs from the run before the last error-triggered restart.',
|
'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.add': 'Add LoRA Adapter',
|
||||||
'models.form.lora.select': 'Select LoRA',
|
'models.form.lora.select': 'Select LoRA',
|
||||||
'models.form.lora.name': 'LoRA name',
|
'models.form.lora.name': 'LoRA name',
|
||||||
|
|||||||
@@ -38,9 +38,12 @@ export default {
|
|||||||
'noresult.catalog.nofound': 'Eşleşen model bulunamadı.',
|
'noresult.catalog.nofound': 'Eşleşen model bulunamadı.',
|
||||||
'noresult.resources.cluster':
|
'noresult.resources.cluster':
|
||||||
'Kullanılabilir küme yok. Başlamak için bir küme ekleyin.',
|
'Kullanılabilir küme yok. Başlamak için bir küme ekleyin.',
|
||||||
|
'noresult.resources.k8sCluster':
|
||||||
|
'Kullanılabilir küme yok. Başlamak için bir Kubernetes kümesi ekleyin.',
|
||||||
'noresult.resources.worker':
|
'noresult.resources.worker':
|
||||||
'Kullanılabilir işçi düğüm yok. Başlamak için bir işçi düğüm ekleyin.',
|
'Kullanılabilir işçi düğüm yok. Başlamak için bir işçi düğüm ekleyin.',
|
||||||
'noresult.resources.gotocluster': 'İlk Kümenizi Oluşturun',
|
'noresult.resources.gotocluster': 'İlk Kümenizi Oluşturun',
|
||||||
|
'noresult.resources.addk8scluster': 'Kubernetes Kümesi Ekle',
|
||||||
'noresult.resources.gotoworker': 'İşçi Düğüm Ekle',
|
'noresult.resources.gotoworker': 'İşçi Düğüm Ekle',
|
||||||
'noresult.benchmark.title': 'Kıyaslama Yok',
|
'noresult.benchmark.title': 'Kıyaslama Yok',
|
||||||
'noresult.benchmark.subTitle': 'Henüz kıyaslama eklenmedi.',
|
'noresult.benchmark.subTitle': 'Henüz kıyaslama eklenmedi.',
|
||||||
|
|||||||
@@ -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 GPUStack 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'
|
||||||
|
};
|
||||||
@@ -17,9 +17,15 @@ export default {
|
|||||||
'usage.table.user.apiKeysUsed': 'API Keys Used',
|
'usage.table.user.apiKeysUsed': 'API Keys Used',
|
||||||
'usage.table.lastActive': 'Last Active',
|
'usage.table.lastActive': 'Last Active',
|
||||||
'usage.filter.granularity': 'Granularity',
|
'usage.filter.granularity': 'Granularity',
|
||||||
|
'usage.filter.granularity.hour': 'Saat',
|
||||||
'usage.filter.granularity.day': 'Day',
|
'usage.filter.granularity.day': 'Day',
|
||||||
'usage.filter.granularity.week': 'Week',
|
'usage.filter.granularity.week': 'Week',
|
||||||
'usage.filter.granularity.month': 'Month',
|
'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.models': 'Models',
|
||||||
'usage.tabs.apikeys': 'API Keys',
|
'usage.tabs.apikeys': 'API Keys',
|
||||||
'usage.tabs.users': 'User',
|
'usage.tabs.users': 'User',
|
||||||
@@ -32,5 +38,70 @@ export default {
|
|||||||
'usage.chart.cached': 'Cached',
|
'usage.chart.cached': 'Cached',
|
||||||
'usage.chart.uncached': 'Uncached',
|
'usage.chart.uncached': 'Uncached',
|
||||||
'usage.chart.inputTokensCached': 'Input Tokens (Cached/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ı'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export default {
|
|||||||
'vendor.hygon': 'Hygon',
|
'vendor.hygon': 'Hygon',
|
||||||
'vendor.moorthreads': 'Moore Threads',
|
'vendor.moorthreads': 'Moore Threads',
|
||||||
'vendor.iluvatar': 'Iluvatar',
|
'vendor.iluvatar': 'Iluvatar',
|
||||||
'vendor.metax': 'Metax',
|
'vendor.metax': 'MetaX',
|
||||||
'vendor.cambricon': 'Cambricon',
|
'vendor.cambricon': 'Cambricon',
|
||||||
'vendor.thead': 'T-Head PPU'
|
'vendor.thead': 'T-Head PPU'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default {
|
|||||||
'ai.provider.ollama': 'Ollama',
|
'ai.provider.ollama': 'Ollama',
|
||||||
'ai.provider.openai': 'OpenAI',
|
'ai.provider.openai': 'OpenAI',
|
||||||
'ai.provider.openrouter': 'OpenRouter',
|
'ai.provider.openrouter': 'OpenRouter',
|
||||||
'ai.provider.qwen': '通义千问',
|
'ai.provider.qwen': '阿里云百炼',
|
||||||
'ai.provider.spark': '讯飞星火',
|
'ai.provider.spark': '讯飞星火',
|
||||||
'ai.provider.stepfun': '阶跃星辰',
|
'ai.provider.stepfun': '阶跃星辰',
|
||||||
'ai.provider.together-ai': 'Together AI',
|
'ai.provider.together-ai': 'Together AI',
|
||||||
|
|||||||
@@ -22,5 +22,7 @@ export default {
|
|||||||
'apikeys.accessScope.inference': '推理接口',
|
'apikeys.accessScope.inference': '推理接口',
|
||||||
'apikeys.access.permissions': '访问权限',
|
'apikeys.access.permissions': '访问权限',
|
||||||
'apikeys.type.auto': '自动生成',
|
'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': '默认执行命令',
|
||||||
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}'、'{{'port'}}'、'{{'worker_ip'}}' 和 '{{'model_name'}}' 都是占位符,在部署过程中会被替换为实际的值。`,
|
'backend.form.defaultExecuteCommand.tips': `'{{'model_path'}}'、'{{'port'}}'、'{{'worker_ip'}}' 和 '{{'model_name'}}' 都是占位符,在部署过程中会被替换为实际的值。`,
|
||||||
'backend.form.defaultBackendParameters': '默认后端参数',
|
'backend.form.defaultBackendParameters': '默认后端参数',
|
||||||
'backend.form.parameterFormat': '参数输出格式',
|
'backend.form.flagFormat': '参数格式',
|
||||||
'backend.form.parameterFormat.default': '后端默认',
|
'backend.form.flagFormat.tips':
|
||||||
'backend.form.parameterFormat.space': '空格分隔 (--key value)',
|
'选项名与取值之间使用的连接格式。留空则保留每个参数原本的写法,不做统一转换。',
|
||||||
'backend.form.parameterFormat.equal': '等号连接 (--key=value)',
|
'backend.form.flagFormat.space': '空格分隔 (--key value)',
|
||||||
'backend.form.commonParameters': '常用参数',
|
'backend.form.flagFormat.equal': '等号连接 (--key=value)',
|
||||||
|
'backend.form.commonParameters': '常用后端参数',
|
||||||
'backend.form.commonParameters.tips': '部署模型时作为后端参数候选项展示。',
|
'backend.form.commonParameters.tips': '部署模型时作为后端参数候选项展示。',
|
||||||
'backend.form.versionConfig': '版本配置',
|
'backend.form.versionConfig': '版本配置',
|
||||||
'backend.form.addParameter': '添加参数',
|
'backend.form.addParameter': '添加参数',
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ export default {
|
|||||||
'长上下文压力测试。评估 KV Cache 行为、内存占用及后端稳定性。',
|
'长上下文压力测试。评估 KV Cache 行为、内存占用及后端稳定性。',
|
||||||
'benchmark.form.profile.heavy.tips':
|
'benchmark.form.profile.heavy.tips':
|
||||||
'以解码为主的生成测试。衡量持续解码速度与输出 Token 吞吐能力。',
|
'以解码为主的生成测试。衡量持续解码速度与输出 Token 吞吐能力。',
|
||||||
'benchmark.table.filter.bygpu': '按 GPU 过滤',
|
'benchmark.table.filter.bygpu': 'GPU 查询',
|
||||||
'benchmark.table.filter.bymodel': '按模型过滤',
|
'benchmark.table.filter.bymodel': '模型查询',
|
||||||
'benchmark.table.filter.bydataset': '按数据集过滤',
|
'benchmark.table.filter.bydataset': '按数据集过滤',
|
||||||
'benchmark.table.filter.byProfile': '按模式过滤',
|
'benchmark.table.filter.byProfile': '按模式过滤',
|
||||||
'benchmark.table.avg': '均值',
|
'benchmark.table.avg': '均值',
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export default {
|
||||||
|
'billing.upsell.title': '计费是企业版功能',
|
||||||
|
'billing.upsell.subtitle':
|
||||||
|
'在团队间跟踪花费、生成账单并执行预算。升级到 GPUStack 企业版即可管理计费。',
|
||||||
|
'billing.upsell.featuresTitle': '企业版包含的能力',
|
||||||
|
'billing.upsell.feature.usage': '按组织、用户与模型查看成本明细',
|
||||||
|
'billing.upsell.feature.invoices': '生成账单并导出计费报表',
|
||||||
|
'billing.upsell.feature.budgets': '设置预算与花费上限并触发告警',
|
||||||
|
'billing.upsell.feature.chargeback': '将用量归属并分摊到团队与项目',
|
||||||
|
'billing.upsell.cta': '了解企业版'
|
||||||
|
};
|
||||||
@@ -38,7 +38,7 @@ export default {
|
|||||||
'clusters.create.noRegions': '无可用的区域',
|
'clusters.create.noRegions': '无可用的区域',
|
||||||
'clusters.workerpool.batchSize.desc': '节点池中同时创建的节点数量。',
|
'clusters.workerpool.batchSize.desc': '节点池中同时创建的节点数量。',
|
||||||
'clusters.create.addworker.tips':
|
'clusters.create.addworker.tips':
|
||||||
'在执行以下命令之前,请确保已满足 {label} 的<a href={link} target="_blank">先决条件</a>。',
|
'在执行以下命令之前,请确保已满足<a href={link} target="_blank">先决条件</a>。',
|
||||||
'clusters.create.addCommand.tips':
|
'clusters.create.addCommand.tips':
|
||||||
'在需要添加的节点上运行以下命令,将其加入到集群中。',
|
'在需要添加的节点上运行以下命令,将其加入到集群中。',
|
||||||
'clusters.create.addCommand.k8s.tips':
|
'clusters.create.addCommand.k8s.tips':
|
||||||
@@ -68,10 +68,13 @@ export default {
|
|||||||
'<span class="bold-text">非 Docker</span> 集群请前往集群页面注册集群或管理节点池。',
|
'<span class="bold-text">非 Docker</span> 集群请前往集群页面注册集群或管理节点池。',
|
||||||
'clusters.addworker.selectGPU': '选择 GPU 厂商',
|
'clusters.addworker.selectGPU': '选择 GPU 厂商',
|
||||||
'clusters.addworker.selectGPU.multiTag': '可多选',
|
'clusters.addworker.selectGPU.multiTag': '可多选',
|
||||||
'clusters.addworker.selectGPU.singleOnly':
|
'clusters.addworker.selectGPU.subtitle':
|
||||||
'当前所选厂商未在集群的 GPU 厂商覆盖(override node selector)中配置,仅支持单选。如需多选,请先选择 override 中已配置的厂商。',
|
'可选择多个 GPU 厂商,或不选择以用于仅 CPU 的集群',
|
||||||
'clusters.addworker.checkEnv': '检查环境',
|
'clusters.addworker.checkEnv': '检查环境',
|
||||||
|
'clusters.addworker.checkEnv.cpuOnlyTips':
|
||||||
|
'使用以下命令验证 Kubernetes 集群中至少有一个就绪节点。你正在注册仅 CPU 的集群。',
|
||||||
'clusters.addworker.specifyArgs': '指定参数',
|
'clusters.addworker.specifyArgs': '指定参数',
|
||||||
|
'clusters.addworker.dtkVersion': 'DTK 版本',
|
||||||
'clusters.addworker.runCommand': '运行指令',
|
'clusters.addworker.runCommand': '运行指令',
|
||||||
'clusters.addworker.specifyWorkerIP': '节点 IP',
|
'clusters.addworker.specifyWorkerIP': '节点 IP',
|
||||||
'clusters.addworker.detectWorkerIP': '自动检测节点 IP',
|
'clusters.addworker.detectWorkerIP': '自动检测节点 IP',
|
||||||
@@ -110,6 +113,8 @@ export default {
|
|||||||
'已将 {count} 个新节点添加到集群中。',
|
'已将 {count} 个新节点添加到集群中。',
|
||||||
'clusters.create.serverUrl': 'GPUStack Server 节点地址',
|
'clusters.create.serverUrl': 'GPUStack Server 节点地址',
|
||||||
'clusters.create.workerConfig': '节点配置',
|
'clusters.create.workerConfig': '节点配置',
|
||||||
|
'clusters.edit.k8sOptions.changed.tip':
|
||||||
|
'您已修改 Kubernetes 选项,需要在目标集群上重新运行注册命令才会生效。',
|
||||||
'clusters.addworker.containerName': '节点容器名称',
|
'clusters.addworker.containerName': '节点容器名称',
|
||||||
'clusters.addworker.containerName.tips': '为节点容器指定一个名称。',
|
'clusters.addworker.containerName.tips': '为节点容器指定一个名称。',
|
||||||
'clusters.addworker.dataVolume': 'GPUStack 数据卷',
|
'clusters.addworker.dataVolume': 'GPUStack 数据卷',
|
||||||
@@ -133,7 +138,7 @@ export default {
|
|||||||
'clusters.addworker.theadNotes-02':
|
'clusters.addworker.theadNotes-02':
|
||||||
'平头哥(T-Head)PPU 使用容器设备接口(CDI)进行设备注入,因此需要确保 <span class="bold-text">/var/run/cdi</span> 目录可用以生成 CDI。',
|
'平头哥(T-Head)PPU 使用容器设备接口(CDI)进行设备注入,因此需要确保 <span class="bold-text">/var/run/cdi</span> 目录可用以生成 CDI。',
|
||||||
'clusters.addworker.nvidiaNotes':
|
'clusters.addworker.nvidiaNotes':
|
||||||
'GPUStack v2.1 内置推理后端依赖 <span class="bold-text">CUDA 12.6</span> 及以上版本,请确保 NVIDIA 驱动版本为 <span class="bold-text">560</span> 或以上。',
|
'GPUStack 内置推理后端依赖 <span class="bold-text">CUDA 12.8</span> 及以上版本,请确保 NVIDIA 驱动版本为 <span class="bold-text">570</span> 或以上。',
|
||||||
'clusters.volume.title': '卷挂载',
|
'clusters.volume.title': '卷挂载',
|
||||||
'clusters.volume.name': '卷名称',
|
'clusters.volume.name': '卷名称',
|
||||||
'clusters.volume.mountPath': '容器内路径',
|
'clusters.volume.mountPath': '容器内路径',
|
||||||
@@ -157,6 +162,10 @@ export default {
|
|||||||
'clusters.volume.configMap.name': '配置名称',
|
'clusters.volume.configMap.name': '配置名称',
|
||||||
'clusters.volume.configMap.optional': '可选',
|
'clusters.volume.configMap.optional': '可选',
|
||||||
'clusters.volume.add': '添加卷挂载',
|
'clusters.volume.add': '添加卷挂载',
|
||||||
|
'clusters.systemDefaultContainerRegistry.title': '默认容器镜像仓库',
|
||||||
|
'clusters.systemDefaultContainerRegistry.tip':
|
||||||
|
'用于解析该集群 GPUStack 镜像的默认镜像仓库。未设置时回退到服务端默认值。',
|
||||||
|
'clusters.k8sOptions.title': 'Kubernetes 部署选项',
|
||||||
'clusters.imageCredentials.title': '镜像仓库凭证',
|
'clusters.imageCredentials.title': '镜像仓库凭证',
|
||||||
'clusters.imageCredentials.add': '添加凭证',
|
'clusters.imageCredentials.add': '添加凭证',
|
||||||
'clusters.imageCredentials.registry': '镜像仓库地址',
|
'clusters.imageCredentials.registry': '镜像仓库地址',
|
||||||
@@ -165,16 +174,20 @@ export default {
|
|||||||
'clusters.nodeSelector.title': '节点选择器',
|
'clusters.nodeSelector.title': '节点选择器',
|
||||||
'clusters.nodeSelector.tip':
|
'clusters.nodeSelector.tip':
|
||||||
'应用到每个 worker DaemonSet 的 Pod nodeSelector,只有标签匹配的节点才会被调度运行 worker。',
|
'应用到每个 worker DaemonSet 的 Pod nodeSelector,只有标签匹配的节点才会被调度运行 worker。',
|
||||||
'clusters.gpuVendorOverrides.title': 'GPU 厂商覆盖配置',
|
'clusters.operatorImage.title': 'Operator 镜像',
|
||||||
'clusters.gpuVendorOverrides.validate.emptySelector':
|
'clusters.operatorImage.tip':
|
||||||
'{vendor} 的覆盖配置至少需要一项 nodeSelector —— 空覆盖会让多 vendor manifest 无法定位该 runtime 的节点。',
|
'GPUStack Operator 容器镜像的覆盖值。留空则使用服务端默认值。',
|
||||||
'clusters.gpuVendorOverrides.validate.duplicate':
|
'clusters.namespace.title': '命名空间',
|
||||||
'{v1} 与 {v2} 的覆盖配置使用了完全相同的 nodeSelector。每个 vendor 必须对应不同的节点集,否则它们的 worker DaemonSet 会相互争抢同一批节点。',
|
'clusters.namespace.tip':
|
||||||
'clusters.gpuVendorOverrides.validate.keyConflict':
|
'集群清单渲染所使用的 Kubernetes 命名空间。留空则使用 gpustack-system。',
|
||||||
'{vendor} 的覆盖配置重用了基础节点选择器中已有的 key({keys})—— 这会让 CPU worker 同时"要求"和"禁止"这些 key,永远无法被调度。',
|
'clusters.clusterType.title': '集群类型',
|
||||||
'clusters.gpuVendorOverrides.tip':
|
'clusters.modelService.title': '模型服务',
|
||||||
'集群中存在多个 GPU 厂商时必填。每项的 nodeSelector 将该厂商的 worker DaemonSet 固定到匹配的节点,同时 CPU worker 通过 DoesNotExist 节点亲和性避开这些节点。',
|
'clusters.modelService.tip':
|
||||||
'clusters.gpuVendorOverrides.add': '添加覆盖配置',
|
'适用于大模型推理与 API 服务化场景,例如对外提供模型 API 与 Token 服务能力。',
|
||||||
'clusters.gpuVendorOverrides.vendor': 'GPU 厂商',
|
'clusters.gpuInstances.title': 'GPU 服务',
|
||||||
'clusters.gpuVendorOverrides.nodeSelector': '节点选择器'
|
'clusters.gpuInstances.tip':
|
||||||
|
'适用于按需分配 GPU 计算资源的场景,例如交互式开发、训练任务或自定义运行环境。',
|
||||||
|
'clusters.gpuInstances.staticAddress': 'GPU 服务静态访问地址',
|
||||||
|
'clusters.gpuInstances.staticAddress.tip':
|
||||||
|
'Operator 访问该集群 GPU 实例所使用的静态地址(例如 LoadBalancer VIP)。可选。'
|
||||||
};
|
};
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user