Skip to content

Commit 7cd4a49

Browse files
authored
Merge pull request #9635 from zexi/automated-cherry-pick-of-#9634-upstream-master
Automated cherry pick of #9634: feat(ai): LLM 套餐本地路径导入与部署重启/操作增强
2 parents 380c8b4 + fa08198 commit 7cd4a49

32 files changed

Lines changed: 1421 additions & 149 deletions

containers/Ai/locales/en.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@
6060
"aice.instant_app": "Inference Model Library",
6161
"aice.instant_app.install": "Install Model Data",
6262
"aice.llm_sku": "Inference Template",
63+
"aice.llm_sku.source.huggingface": "Hugging Face",
64+
"aice.llm_sku.source.model_scope": "ModelScope",
65+
"aice.llm_sku.source.local_path": "Local Path",
66+
"aice.llm_sku.source.ollama": "Ollama Registry",
6367
"aice.app_llm_sku": "Agent Template",
6468
"aice.desktop_llm": "Desktop Instances",
6569
"aice.desktop_llm_create": "Create Desktop Instance",
@@ -323,6 +327,8 @@
323327
"aice.add_port_mapping": "Add Mapping",
324328
"aice.preferred_model": "Preferred model",
325329
"aice.customized_args": "Custom arguments",
330+
"aice.backend_parameters": "Custom arguments",
331+
"aice.backend_parameters.help": "Parameter names become vLLM/SGLang CLI flags, e.g. max-model-len. With a value: --max-model-len=8192; without a value: --enable-auto-tool-choice",
326332
"aice.add_customized_arg": "Add argument",
327333
"aice.customized_arg_key": "Key",
328334
"aice.customized_arg_value": "Value",
@@ -491,6 +497,12 @@
491497
"aice.llm_deployment.aiproxy_sync_in_progress": "Gateway sync is in progress. Try again later.",
492498
"aice.llm_deployment.aiproxy_not_registered": "Not registered with AI Gateway",
493499
"aice.llm_deployment.scale": "Scale",
500+
"aice.llm_deployment.restart": "Restart",
501+
"aice.llm_deployment.restart_confirm": "This will restart all replicas under this deployment. Service may be briefly unavailable. Continue?",
502+
"aice.llm_deployment.restart_disabled_invalid_status": "Restart is not allowed in current status ({0})",
503+
"aice.llm_deployment.syncstatus_disabled_invalid_status": "Sync status is not allowed in current status ({0})",
504+
"aice.llm_deployment.scale_disabled_invalid_status": "Scale is not allowed in current status ({0})",
505+
"aice.llm_deployment.restart_disabled_no_ready_replicas": "No ready replicas; restart is unavailable",
494506
"aice.llm_deployment.scale.help": "Ready {0} / Desired {1}",
495507
"aice.llm_deployment.auto_start": "Auto Start",
496508
"aice.llm_deployment.create.mode": "Create Mode",
@@ -520,6 +532,17 @@
520532
"aice.llm_deployment.deploy.from_huggingface": "From Hugging Face",
521533
"aice.llm_deployment.deploy.from_modelscope": "From ModelScope",
522534
"aice.llm_deployment.deploy.from_local": "From Local Path",
535+
"aice.local_path_import.host_model_path": "Host model path",
536+
"aice.local_path_import.prefer_hosts": "Hosts",
537+
"aice.local_path_import.prefer_hosts_extra": "Select hosts where the model directory exists (multiple allowed). Inherited when creating a deployment; replicas are round-robin scheduled across selected hosts",
538+
"aice.local_path_import.prefer_hosts_required": "Select at least one host",
539+
"aice.local_path_import.prefer_hosts_deploy_hint": "The model must exist at the same path on selected hosts",
540+
"aice.local_path_import.host_model_path_extra": "Absolute path to an existing model directory on the host; mounted into the container via host_paths",
541+
"aice.local_path_import.host_model_path_placeholder": "e.g. /data/models/Qwen3-8B",
542+
"aice.local_path_import.path_required": "Host model path is required",
543+
"aice.local_path_import.path_absolute": "Path must be an absolute path starting with /",
544+
"aice.local_path_import.mount_preview": "Mount: {0} → in container {1}",
545+
"aice.local_path_import.advanced_host_paths": "Advanced: edit host_paths manually",
523546
"aice.llm_deployment.deploy.coming_soon": "Coming soon",
524547
"aice.llm_deployment.deploy.selected_repo": "Selected: {0}",
525548
"aice.llm_deployment.deploy.change_repo": "Change",

containers/Ai/locales/ja-JP.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@
5959
"aice.instant_app": "推論モデルライブラリ",
6060
"aice.instant_app.install": "モデルデータをインストール",
6161
"aice.llm_sku": "推論テンプレート",
62+
"aice.llm_sku.source.huggingface": "Hugging Face",
63+
"aice.llm_sku.source.model_scope": "ModelScope",
64+
"aice.llm_sku.source.local_path": "ローカルパス",
65+
"aice.llm_sku.source.ollama": "Ollama 公式リポジトリ",
6266
"aice.app_llm_sku": "Agentテンプレート",
6367
"aice.desktop_llm": "デスクトップインスタンス",
6468
"aice.desktop_llm_create": "デスクトップインスタンス作成",
@@ -322,6 +326,8 @@
322326
"aice.add_port_mapping": "マッピングを追加",
323327
"aice.preferred_model": "優先モデル",
324328
"aice.customized_args": "カスタム引数",
329+
"aice.backend_parameters": "カスタム引数",
330+
"aice.backend_parameters.help": "パラメータ名は vLLM/SGLang の CLI フラグになります(例: max-model-len)。値あり: --max-model-len=8192、値なし: --enable-auto-tool-choice",
325331
"aice.add_customized_arg": "引数を追加",
326332
"aice.customized_arg_key": "キー",
327333
"aice.customized_arg_value": "",
@@ -490,6 +496,12 @@
490496
"aice.llm_deployment.aiproxy_sync_in_progress": "ゲートウェイ同期中です。しばらくしてから再試行してください。",
491497
"aice.llm_deployment.aiproxy_not_registered": "AI ゲートウェイ未登録",
492498
"aice.llm_deployment.scale": "スケール",
499+
"aice.llm_deployment.restart": "再起動",
500+
"aice.llm_deployment.restart_confirm": "このデプロイの全レプリカを再起動します。サービスが一時的に利用できなくなる場合があります。続行しますか?",
501+
"aice.llm_deployment.restart_disabled_invalid_status": "現在の状態({0})では再起動できません",
502+
"aice.llm_deployment.syncstatus_disabled_invalid_status": "現在の状態({0})では状態同期できません",
503+
"aice.llm_deployment.scale_disabled_invalid_status": "現在の状態({0})ではレプリカ数を変更できません",
504+
"aice.llm_deployment.restart_disabled_no_ready_replicas": "準備完了のレプリカがないため、再起動できません",
493505
"aice.llm_deployment.scale.help": "準備完了 {0} / 期待 {1}",
494506
"aice.llm_deployment.auto_start": "自動起動",
495507
"aice.llm_deployment.create.mode": "作成モード",
@@ -519,6 +531,17 @@
519531
"aice.llm_deployment.deploy.from_huggingface": "Hugging Face から",
520532
"aice.llm_deployment.deploy.from_modelscope": "ModelScope から",
521533
"aice.llm_deployment.deploy.from_local": "ローカルパスから",
534+
"aice.local_path_import.host_model_path": "ホストモデルパス",
535+
"aice.local_path_import.prefer_hosts": "ホスト",
536+
"aice.local_path_import.prefer_hosts_extra": "モデルディレクトリが存在するホストを選択(複数可)。デプロイ作成時にデフォルトで継承され、レプリカは選択ホストにラウンドロビンで割り当てられます",
537+
"aice.local_path_import.prefer_hosts_required": "少なくとも1台のホストを選択してください",
538+
"aice.local_path_import.prefer_hosts_deploy_hint": "モデルは選択したホスト上の同じパスに存在する必要があります",
539+
"aice.local_path_import.host_model_path_extra": "ホスト上の既存モデルディレクトリの絶対パス。host_paths でコンテナにマウントします",
540+
"aice.local_path_import.host_model_path_placeholder": "例: /data/models/Qwen3-8B",
541+
"aice.local_path_import.path_required": "ホストモデルパスを入力してください",
542+
"aice.local_path_import.path_absolute": "パスは / で始まる絶対パスである必要があります",
543+
"aice.local_path_import.mount_preview": "マウント: {0} → コンテナ内 {1}",
544+
"aice.local_path_import.advanced_host_paths": "詳細: host_paths を手動編集",
522545
"aice.llm_deployment.deploy.coming_soon": "近日対応予定",
523546
"aice.llm_deployment.deploy.selected_repo": "選択中:{0}",
524547
"aice.llm_deployment.deploy.change_repo": "変更",

containers/Ai/locales/zh-CN.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@
7373
"aice.instant_app": "推理模型库",
7474
"aice.instant_app.install": "安装模型数据",
7575
"aice.llm_sku": "推理模板",
76+
"aice.llm_sku.source.huggingface": "Hugging Face",
77+
"aice.llm_sku.source.model_scope": "ModelScope",
78+
"aice.llm_sku.source.local_path": "本地路径",
79+
"aice.llm_sku.source.ollama": "Ollama 官方仓库",
7680
"aice.app_llm_sku": "Agent模板",
7781
"aice.desktop_llm": "桌面实例",
7882
"aice.desktop_llm_create": "创建桌面实例",
@@ -336,6 +340,8 @@
336340
"aice.add_port_mapping": "新增映射",
337341
"aice.preferred_model": "首选模型标识",
338342
"aice.customized_args": "自定义参数",
343+
"aice.backend_parameters": "自定义参数",
344+
"aice.backend_parameters.help": "参数名将作为 vLLM/SGLang CLI 标志,例如 max-model-len;有值时提交为 --max-model-len=8192,无值时提交为 --enable-auto-tool-choice",
339345
"aice.add_customized_arg": "新增参数",
340346
"aice.customized_arg_key": "参数名",
341347
"aice.customized_arg_value": "参数值",
@@ -494,6 +500,12 @@
494500
"aice.llm_deployment.aiproxy_sync_in_progress": "网关同步进行中,请稍后再试",
495501
"aice.llm_deployment.aiproxy_not_registered": "尚未注册 AI 网关",
496502
"aice.llm_deployment.scale": "调整副本数",
503+
"aice.llm_deployment.restart": "重启",
504+
"aice.llm_deployment.restart_confirm": "将重启该部署下的全部副本,期间服务可能短暂不可用,是否继续?",
505+
"aice.llm_deployment.restart_disabled_invalid_status": "当前状态({0})不允许重启",
506+
"aice.llm_deployment.syncstatus_disabled_invalid_status": "当前状态({0})不允许同步状态",
507+
"aice.llm_deployment.scale_disabled_invalid_status": "当前状态({0})不允许调整副本数",
508+
"aice.llm_deployment.restart_disabled_no_ready_replicas": "暂无就绪副本,无法重启",
497509
"aice.llm_deployment.scale.help": "当前就绪 {0} / 期望 {1}",
498510
"aice.llm_deployment.auto_start": "自动启动",
499511
"aice.llm_deployment.create.mode": "创建模式",
@@ -523,6 +535,17 @@
523535
"aice.llm_deployment.deploy.from_huggingface": "从 Hugging Face",
524536
"aice.llm_deployment.deploy.from_modelscope": "从 ModelScope",
525537
"aice.llm_deployment.deploy.from_local": "从本地路径",
538+
"aice.local_path_import.host_model_path": "宿主机模型路径",
539+
"aice.local_path_import.prefer_hosts": "宿主机",
540+
"aice.local_path_import.prefer_hosts_extra": "选择模型目录所在的宿主机,可多选;创建部署时默认继承,多副本将轮询分配到所选宿主机",
541+
"aice.local_path_import.prefer_hosts_required": "请至少选择一个宿主机",
542+
"aice.local_path_import.prefer_hosts_deploy_hint": "模型须存在于所选宿主机上的相同路径",
543+
"aice.local_path_import.host_model_path_extra": "填写宿主机上已存在的模型目录绝对路径,将通过 host_paths 挂载到推理容器",
544+
"aice.local_path_import.host_model_path_placeholder": "例如 /data/models/Qwen3-8B",
545+
"aice.local_path_import.path_required": "请输入宿主机模型路径",
546+
"aice.local_path_import.path_absolute": "路径须以 / 开头的绝对路径",
547+
"aice.local_path_import.mount_preview": "将挂载:{0} → 容器内 {1}",
548+
"aice.local_path_import.advanced_host_paths": "高级:手动编辑 host_paths",
526549
"aice.llm_deployment.deploy.coming_soon": "敬请期待",
527550
"aice.llm_deployment.deploy.selected_repo": "已选择:{0}",
528551
"aice.llm_deployment.deploy.change_repo": "重新选择",

containers/Ai/router/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const LlmSku = () => import(/* webpackChunkName: "k8s" */ /* webpackPrefetch: tr
77
const LlmSkuCreate = () => import(/* webpackChunkName: "k8s" */ /* webpackPrefetch: true */ '@Ai/views/llm-sku/create/index')
88
const LlmSkuImportFromModelSets = () => import(/* webpackChunkName: "k8s" */ /* webpackPrefetch: true */ '@Ai/views/llm-sku/import-from-model-sets')
99
const LlmSkuImportFromHuggingFace = () => import(/* webpackChunkName: "k8s" */ /* webpackPrefetch: true */ '@Ai/views/llm-sku/import-from-huggingface')
10+
const LlmSkuImportFromLocalPath = () => import(/* webpackChunkName: "k8s" */ /* webpackPrefetch: true */ '@Ai/views/llm-sku/import-from-local-path')
1011
const LlmSkuImportFromCommunity = () => import(/* webpackChunkName: "k8s" */ /* webpackPrefetch: true */ '@Ai/views/llm-sku/import-from-community')
1112
const LlmInstantmodel = () => import(/* webpackChunkName: "k8s" */ /* webpackPrefetch: true */ '@Ai/views/llm-instantmodel')
1213
const LlmImage = () => import(/* webpackChunkName: "k8s" */ /* webpackPrefetch: true */ '@Ai/views/llm-image')
@@ -206,6 +207,11 @@ export default {
206207
path: 'import-from-huggingface',
207208
component: LlmSkuImportFromHuggingFace,
208209
},
210+
{
211+
name: 'LlmSkuImportFromLocalPath',
212+
path: 'import-from-local-path',
213+
component: LlmSkuImportFromLocalPath,
214+
},
209215
{
210216
name: 'LlmSkuCreate',
211217
path: 'create',

containers/Ai/sections/LlmSkuSelect.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<script>
3636
import { sizestr } from '@/utils/utils'
3737
import { getSkuModelDisplayText } from '@Ai/views/llm-sku/utils/modelDisplay'
38+
import { getLlmSkuSourceLabel } from '@Ai/views/llm-sku/utils/skuSourceDisplay'
3839
3940
export default {
4041
name: 'LlmSkuSelect',
@@ -115,6 +116,9 @@ export default {
115116
if (item.llm_type) {
116117
parts.push(`${this.$t('aice.llm_type')}${item.llm_type}`)
117118
}
119+
if (item.source) {
120+
parts.push(`${this.$t('aice.llm_deployment.source')}${getLlmSkuSourceLabel(item.source)}`)
121+
}
118122
if (item.app_name) {
119123
parts.push(`${this.$t('aice.llm_image.app_name')}${item.app_name}`)
120124
}

containers/Ai/utils/aiproxyDeploymentActions.js

Lines changed: 118 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,40 @@
1-
export function formatDeploymentAiproxyStatus (status, vm) {
1+
export function formatDeploymentStatus (status, vm) {
22
if (!status) return '-'
33
const key = `status.llmDeployment.${status}`
44
const text = vm.$t(key)
55
return text === key ? status : text
66
}
77

8+
export function formatAiproxySyncStatus (status, vm) {
9+
if (!status) return '-'
10+
const key = `status.llmDeploymentAiproxy.${status}`
11+
const text = vm.$t(key)
12+
return text === key ? status : text
13+
}
14+
15+
// Prefer aiproxy_sync_status; fall back to legacy status values during upgrade.
16+
export function resolveAiproxySyncStatus (row) {
17+
const syncStatus = row?.aiproxy_sync_status
18+
if (syncStatus) return syncStatus
19+
const legacy = row?.status
20+
switch (legacy) {
21+
case 'aiproxy_pending':
22+
return 'pending'
23+
case 'aiproxy_syncing':
24+
return 'syncing'
25+
case 'aiproxy_partial':
26+
return 'partial'
27+
case 'aiproxy_sync_failed':
28+
return 'failed'
29+
default:
30+
return row?.auto_register_aiproxy === false ? 'disabled' : ''
31+
}
32+
}
33+
834
export function isAiproxySyncInProgress (row) {
9-
const status = row?.status
10-
if (status === 'aiproxy_syncing') return true
11-
if (status === 'aiproxy_pending' && row?.auto_register_aiproxy !== false) return true
35+
const status = resolveAiproxySyncStatus(row)
36+
if (status === 'syncing') return true
37+
if (status === 'pending' && row?.auto_register_aiproxy !== false) return true
1238
return false
1339
}
1440

@@ -21,7 +47,44 @@ export function isAiproxyRegistered (row) {
2147
return true
2248
}
2349

50+
const CREATING_STATUSES = [
51+
'creating',
52+
'importing_model',
53+
'creating_sku',
54+
'deleting',
55+
'import_model_failed',
56+
'create_sku_failed',
57+
'create_fail',
58+
'delete_fail',
59+
]
60+
61+
export function getScaleMeta (row, vm) {
62+
if (CREATING_STATUSES.includes(row?.status)) {
63+
return {
64+
validate: false,
65+
tooltip: vm.$t('aice.llm_deployment.scale_disabled_invalid_status', [
66+
formatDeploymentStatus(row?.status, vm),
67+
]),
68+
}
69+
}
70+
if (isAiproxySyncInProgress(row)) {
71+
return {
72+
validate: false,
73+
tooltip: vm.$t('aice.llm_deployment.aiproxy_sync_in_progress'),
74+
}
75+
}
76+
return { validate: true }
77+
}
78+
2479
export function getRegisterAiproxyMeta (row, vm) {
80+
if (CREATING_STATUSES.includes(row?.status)) {
81+
return {
82+
validate: false,
83+
tooltip: vm.$t('aice.llm_deployment.scale_disabled_invalid_status', [
84+
formatDeploymentStatus(row?.status, vm),
85+
]),
86+
}
87+
}
2588
if (isAiproxySyncInProgress(row)) {
2689
return {
2790
validate: false,
@@ -47,6 +110,57 @@ export function getUnregisterAiproxyMeta (row, vm) {
47110
return { validate: true }
48111
}
49112

113+
const RESTARTABLE_STATUSES = ['ready', 'partial', 'running', 'start_fail']
114+
115+
export function getRestartMeta (row, vm) {
116+
if (CREATING_STATUSES.includes(row?.status)) {
117+
return {
118+
validate: false,
119+
tooltip: vm.$t('aice.llm_deployment.restart_disabled_invalid_status', [
120+
formatDeploymentStatus(row?.status, vm),
121+
]),
122+
}
123+
}
124+
if (isAiproxySyncInProgress(row)) {
125+
return {
126+
validate: false,
127+
tooltip: vm.$t('aice.llm_deployment.aiproxy_sync_in_progress'),
128+
}
129+
}
130+
if (!RESTARTABLE_STATUSES.includes(row?.status)) {
131+
const statusLabel = formatDeploymentStatus(row?.status, vm)
132+
return {
133+
validate: false,
134+
tooltip: vm.$t('aice.llm_deployment.restart_disabled_invalid_status', [statusLabel]),
135+
}
136+
}
137+
return { validate: true }
138+
}
139+
140+
const SYNC_FORBIDDEN_STATUSES = [
141+
'creating',
142+
'importing_model',
143+
'creating_sku',
144+
'deleting',
145+
'start_delete',
146+
'deleted',
147+
'delete_fail',
148+
'delete_failed',
149+
'syncing',
150+
]
151+
152+
export function getSyncStatusMeta (row, vm) {
153+
if (SYNC_FORBIDDEN_STATUSES.includes(row?.status)) {
154+
return {
155+
validate: false,
156+
tooltip: vm.$t('aice.llm_deployment.syncstatus_disabled_invalid_status', [
157+
formatDeploymentStatus(row?.status, vm),
158+
]),
159+
}
160+
}
161+
return { validate: true }
162+
}
163+
50164
const POST_ACTION_POLL_MS = 2000
51165
const POST_ACTION_MAX_POLLS = 15
52166

0 commit comments

Comments
 (0)