feat(llm): validate GPU devices on deployment create and improve error messages (#25063)

This commit is contained in:
Zexi Li
2026-06-24 14:27:25 +08:00
committed by GitHub
parent 143ca823c8
commit ba39e80fd8
8 changed files with 2957 additions and 2649 deletions

View File

@@ -185,6 +185,7 @@ y18n-lang := en-US,zh-CN
y18n-packages := \ y18n-packages := \
yunion.io/x/onecloud/cmd/apigateway \ yunion.io/x/onecloud/cmd/apigateway \
yunion.io/x/onecloud/cmd/keystone \ yunion.io/x/onecloud/cmd/keystone \
yunion.io/x/onecloud/cmd/llm \
yunion.io/x/onecloud/cmd/monitor \ yunion.io/x/onecloud/cmd/monitor \
yunion.io/x/onecloud/cmd/region \ yunion.io/x/onecloud/cmd/region \
yunion.io/x/onecloud/cmd/yunionconf \ yunion.io/x/onecloud/cmd/yunionconf \

File diff suppressed because it is too large Load Diff

View File

@@ -482,9 +482,9 @@
"translation": "无效的地址%s" "translation": "无效的地址%s"
}, },
{ {
"id": "comment too long (%d\u003e=%d)", "id": "comment too long (%d>=%d)",
"message": "comment too long (%d\u003e=%d)", "message": "comment too long (%d>=%d)",
"translation": "描述文本过长(%d\u003e=%d" "translation": "描述文本过长(%d>=%d"
}, },
{ {
"id": "comment contains non-printable char: %v", "id": "comment contains non-printable char: %v",
@@ -1142,13 +1142,13 @@
"translation": "名字长度超过%d" "translation": "名字长度超过%d"
}, },
{ {
"id": "input key too long \u003e %d", "id": "input key too long > %d",
"message": "input key too long \u003e %d", "message": "input key too long > %d",
"translation": "输入键名长度超过%d" "translation": "输入键名长度超过%d"
}, },
{ {
"id": "input value too long \u003e %d", "id": "input value too long > %d",
"message": "input value too long \u003e %d", "message": "input value too long > %d",
"translation": "输入键值长度超过%d" "translation": "输入键值长度超过%d"
}, },
{ {
@@ -1902,14 +1902,14 @@
"translation": "端口范围起始 %d 大于 %d" "translation": "端口范围起始 %d 大于 %d"
}, },
{ {
"id": "port range start %d \u003c= %d", "id": "port range start %d <= %d",
"message": "port range start %d \u003c= %d", "message": "port range start %d <= %d",
"translation": "端口范围起始 %d \u003c= %d" "translation": "端口范围起始 %d <= %d"
}, },
{ {
"id": "port range end %d \u003e %d", "id": "port range end %d > %d",
"message": "port range end %d \u003e %d", "message": "port range end %d > %d",
"translation": "端口范围结束 %d \u003e %d" "translation": "端口范围结束 %d > %d"
}, },
{ {
"id": "port number %d isn't within %d to %d", "id": "port number %d isn't within %d to %d",
@@ -1989,7 +1989,7 @@
{ {
"id": "GetGuestCount failed %s", "id": "GetGuestCount failed %s",
"message": "GetGuestCount failed %s", "message": "GetGuestCount failed %s",
"translation": "" "translation": "GetGuestCount失败: %s"
}, },
{ {
"id": "host has been occupied", "id": "host has been occupied",
@@ -2274,7 +2274,7 @@
{ {
"id": "BackupCount failed %s", "id": "BackupCount failed %s",
"message": "BackupCount failed %s", "message": "BackupCount failed %s",
"translation": "" "translation": "BackupCount失败: %s"
}, },
{ {
"id": "storage has been used", "id": "storage has been used",
@@ -2344,7 +2344,7 @@
{ {
"id": "GetIObject failed %s", "id": "GetIObject failed %s",
"message": "GetIObject failed %s", "message": "GetIObject failed %s",
"translation": "" "translation": "GetIObject失败: %s"
}, },
{ {
"id": "object count limit exceeds", "id": "object count limit exceeds",
@@ -2394,7 +2394,7 @@
{ {
"id": "GetIObject failed: %s", "id": "GetIObject failed: %s",
"message": "GetIObject failed: %s", "message": "GetIObject failed: %s",
"translation": "" "translation": "GetIObject失败: %s"
}, },
{ {
"id": "object size limit exceeds", "id": "object size limit exceeds",
@@ -2404,7 +2404,7 @@
{ {
"id": "bucket.GetQuotaKeys failed %s", "id": "bucket.GetQuotaKeys failed %s",
"message": "bucket.GetQuotaKeys failed %s", "message": "bucket.GetQuotaKeys failed %s",
"translation": "" "translation": "bucket.GetQuotaKeys失败: %s"
}, },
{ {
"id": "put object failed: %s", "id": "put object failed: %s",
@@ -2524,7 +2524,7 @@
{ {
"id": "iBucket.GetIObject failed: %s", "id": "iBucket.GetIObject failed: %s",
"message": "iBucket.GetIObject failed: %s", "message": "iBucket.GetIObject failed: %s",
"translation": "" "translation": "iBucket.GetIObject失败: %s"
}, },
{ {
"id": "The image has been cached on storages", "id": "The image has been cached on storages",
@@ -2609,7 +2609,7 @@
{ {
"id": "check uniqueness failed %s", "id": "check uniqueness failed %s",
"message": "check uniqueness failed %s", "message": "check uniqueness failed %s",
"translation": "" "translation": "检查唯一性失败: %s"
}, },
{ {
"id": "The account has been registered", "id": "The account has been registered",
@@ -3394,7 +3394,7 @@
{ {
"id": "disk.GetQuotaKeys failed %s", "id": "disk.GetQuotaKeys failed %s",
"message": "disk.GetQuotaKeys failed %s", "message": "disk.GetQuotaKeys failed %s",
"translation": "" "translation": "disk.GetQuotaKeys失败: %s"
}, },
{ {
"id": "Duplicate image name %s", "id": "Duplicate image name %s",
@@ -3444,7 +3444,7 @@
{ {
"id": "GetGuestDiskCount for disk %s failed %s", "id": "GetGuestDiskCount for disk %s failed %s",
"message": "GetGuestDiskCount for disk %s failed %s", "message": "GetGuestDiskCount for disk %s failed %s",
"translation": "" "translation": "获取磁盘 %s 的虚拟机磁盘数量失败: %s"
}, },
{ {
"id": "Diskinfo index %d: both imageID and size are absent", "id": "Diskinfo index %d: both imageID and size are absent",
@@ -3484,7 +3484,7 @@
{ {
"id": "GetSnapshotCount failed %s", "id": "GetSnapshotCount failed %s",
"message": "GetSnapshotCount failed %s", "message": "GetSnapshotCount failed %s",
"translation": "" "translation": "GetSnapshotCount失败: %s"
}, },
{ {
"id": "not allowed to purge virtual disk must not have snapshots", "id": "not allowed to purge virtual disk must not have snapshots",
@@ -3619,7 +3619,7 @@
{ {
"id": "unmarshal StandaloneResourceCreateInput failed %s", "id": "unmarshal StandaloneResourceCreateInput failed %s",
"message": "unmarshal StandaloneResourceCreateInput failed %s", "message": "unmarshal StandaloneResourceCreateInput failed %s",
"translation": "" "translation": "解析 StandaloneResourceCreateInput 失败: %s"
}, },
{ {
"id": "resource type %s is not supported", "id": "resource type %s is not supported",
@@ -3954,7 +3954,7 @@
{ {
"id": "GetVpcs failed %s", "id": "GetVpcs failed %s",
"message": "GetVpcs failed %s", "message": "GetVpcs failed %s",
"translation": "" "translation": "GetVpcs失败: %s"
}, },
{ {
"id": "global vpc is associated with %d VPCs", "id": "global vpc is associated with %d VPCs",
@@ -4324,7 +4324,7 @@
{ {
"id": "check isAttach2Disk failed %s", "id": "check isAttach2Disk failed %s",
"message": "check isAttach2Disk failed %s", "message": "check isAttach2Disk failed %s",
"translation": "" "translation": "检查是否挂载磁盘失败: %s"
}, },
{ {
"id": "Cannot keep detached disk", "id": "Cannot keep detached disk",
@@ -4342,8 +4342,8 @@
"translation": "找不到透传设备%s" "translation": "找不到透传设备%s"
}, },
{ {
"id": "guest attach gpu count must \u003e 0", "id": "guest attach gpu count must > 0",
"message": "guest attach gpu count must \u003e 0", "message": "guest attach gpu count must > 0",
"translation": "GPU数量必须大于1" "translation": "GPU数量必须大于1"
}, },
{ {
@@ -4464,7 +4464,7 @@
{ {
"id": "check mac uniqueness failed %s", "id": "check mac uniqueness failed %s",
"message": "check mac uniqueness failed %s", "message": "check mac uniqueness failed %s",
"translation": "" "translation": "检查 MAC 唯一性失败: %s"
}, },
{ {
"id": "mac addr %s has been occupied", "id": "mac addr %s has been occupied",
@@ -4654,7 +4654,7 @@
{ {
"id": "GuestDisksHasSnapshot failed %s", "id": "GuestDisksHasSnapshot failed %s",
"message": "GuestDisksHasSnapshot failed %s", "message": "GuestDisksHasSnapshot failed %s",
"translation": "" "translation": "GuestDisksHasSnapshot失败: %s"
}, },
{ {
"id": "Cannot create backup with snapshot", "id": "Cannot create backup with snapshot",
@@ -4852,13 +4852,13 @@
"translation": "虚机状态%s无法设定io限速" "translation": "虚机状态%s无法设定io限速"
}, },
{ {
"id": "disk %s bps must \u003e 0", "id": "disk %s bps must > 0",
"message": "disk %s bps must \u003e 0", "message": "disk %s bps must > 0",
"translation": "磁盘 %s bps必须大于0" "translation": "磁盘 %s bps必须大于0"
}, },
{ {
"id": "disk %s iops must \u003e 0", "id": "disk %s iops must > 0",
"message": "disk %s iops must \u003e 0", "message": "disk %s iops must > 0",
"translation": "磁盘 %s iops必须大于0" "translation": "磁盘 %s iops必须大于0"
}, },
{ {
@@ -4982,8 +4982,8 @@
"translation": "从快照恢复失败:%s" "translation": "从快照恢复失败:%s"
}, },
{ {
"id": "count must \u003e 0", "id": "count must > 0",
"message": "count must \u003e 0", "message": "count must > 0",
"translation": "count必须大于0" "translation": "count必须大于0"
}, },
{ {
@@ -5374,7 +5374,7 @@
{ {
"id": "check disk index uniqueness failed %s", "id": "check disk index uniqueness failed %s",
"message": "check disk index uniqueness failed %s", "message": "check disk index uniqueness failed %s",
"translation": "" "translation": "检查磁盘索引唯一性失败: %s"
}, },
{ {
"id": "DISK Index %d has been occupied", "id": "DISK Index %d has been occupied",
@@ -5641,6 +5641,11 @@
"message": "invalid category %s for policy definition %s(%s)", "message": "invalid category %s for policy definition %s(%s)",
"translation": "类别%s在策略定义%s(%s)中无效" "translation": "类别%s在策略定义%s(%s)中无效"
}, },
{
"id": "os_type is required",
"message": "os_type is required",
"translation": "os_type 为必填项"
},
{ {
"id": "unsupported os_type: %s, supported types: %v", "id": "unsupported os_type: %s, supported types: %v",
"message": "unsupported os_type: %s, supported types: %v", "message": "unsupported os_type: %s, supported types: %v",
@@ -5869,17 +5874,17 @@
{ {
"id": "getGuestCount failed %s", "id": "getGuestCount failed %s",
"message": "getGuestCount failed %s", "message": "getGuestCount failed %s",
"translation": "" "translation": "getGuestCount失败: %s"
}, },
{ {
"id": "GetBackupGuestCount failed %s", "id": "GetBackupGuestCount failed %s",
"message": "GetBackupGuestCount failed %s", "message": "GetBackupGuestCount failed %s",
"translation": "" "translation": "GetBackupGuestCount失败: %s"
}, },
{ {
"id": "GetDiskCount failed %s", "id": "GetDiskCount failed %s",
"message": "GetDiskCount failed %s", "message": "GetDiskCount failed %s",
"translation": "" "translation": "GetDiskCount失败: %s"
}, },
{ {
"id": "local host storage is not empty", "id": "local host storage is not empty",
@@ -5899,7 +5904,7 @@
{ {
"id": "check %s duplication failed %s", "id": "check %s duplication failed %s",
"message": "check %s duplication failed %s", "message": "check %s duplication failed %s",
"translation": "" "translation": "检查 %s 重复失败: %s"
}, },
{ {
"id": "duplicate %s %s", "id": "duplicate %s %s",
@@ -5914,7 +5919,7 @@
{ {
"id": "check access_mac duplication failed %s", "id": "check access_mac duplication failed %s",
"message": "check access_mac duplication failed %s", "message": "check access_mac duplication failed %s",
"translation": "" "translation": "检查 access_mac 重复失败: %s"
}, },
{ {
"id": "duplicate access_mac %s", "id": "duplicate access_mac %s",
@@ -6239,7 +6244,7 @@
{ {
"id": "isAlterNameUnique failed %s", "id": "isAlterNameUnique failed %s",
"message": "isAlterNameUnique failed %s", "message": "isAlterNameUnique failed %s",
"translation": "" "translation": "isAlterNameUnique失败: %s"
}, },
{ {
"id": "Convert error: %s", "id": "Convert error: %s",
@@ -6302,18 +6307,18 @@
"translation": "非裸金属服务器中无法执行同步配置操作" "translation": "非裸金属服务器中无法执行同步配置操作"
}, },
{ {
"id": "reserved cpu must \u003e= 0", "id": "reserved cpu must >= 0",
"message": "reserved cpu must \u003e= 0", "message": "reserved cpu must >= 0",
"translation": "预留CPU数必须为非负整数" "translation": "预留CPU数必须为非负整数"
}, },
{ {
"id": "reserved memory must \u003e= 0", "id": "reserved memory must >= 0",
"message": "reserved memory must \u003e= 0", "message": "reserved memory must >= 0",
"translation": "预留内存大小必须为非负整数" "translation": "预留内存大小必须为非负整数"
}, },
{ {
"id": "reserved storage must \u003e= 0", "id": "reserved storage must >= 0",
"message": "reserved storage must \u003e= 0", "message": "reserved storage must >= 0",
"translation": "预留存储必须为非负整数" "translation": "预留存储必须为非负整数"
}, },
{ {
@@ -6349,12 +6354,12 @@
{ {
"id": "GetGuestDiskCount failed %s", "id": "GetGuestDiskCount failed %s",
"message": "GetGuestDiskCount failed %s", "message": "GetGuestDiskCount failed %s",
"translation": "" "translation": "GetGuestDiskCount失败: %s"
}, },
{ {
"id": "GetGuestnicsCount failed %s", "id": "GetGuestnicsCount failed %s",
"message": "GetGuestnicsCount failed %s", "message": "GetGuestnicsCount failed %s",
"translation": "" "translation": "GetGuestnicsCount失败: %s"
}, },
{ {
"id": "cannot delete instance backup in current status", "id": "cannot delete instance backup in current status",
@@ -6557,9 +6562,9 @@
"translation": "%s时间错误%s" "translation": "%s时间错误%s"
}, },
{ {
"id": "%s: new time is in the future: %s \u003e %s", "id": "%s: new time is in the future: %s > %s",
"message": "%s: new time is in the future: %s \u003e %s", "message": "%s: new time is in the future: %s > %s",
"translation": "%s新指定时间在未来%s \u003e %s" "translation": "%s新指定时间在未来%s > %s"
}, },
{ {
"id": "use yum requires valid repo_base_url", "id": "use yum requires valid repo_base_url",
@@ -7094,7 +7099,7 @@
{ {
"id": "isAddressUsed failed %s", "id": "isAddressUsed failed %s",
"message": "isAddressUsed failed %s", "message": "isAddressUsed failed %s",
"translation": "" "translation": "isAddressUsed失败: %s"
}, },
{ {
"id": "Address %s has been used", "id": "Address %s has been used",
@@ -7114,7 +7119,7 @@
{ {
"id": "isAddress6Used failed %s", "id": "isAddress6Used failed %s",
"message": "isAddress6Used failed %s", "message": "isAddress6Used failed %s",
"translation": "" "translation": "isAddress6Used失败: %s"
}, },
{ {
"id": "v6 address %s has been used", "id": "v6 address %s has been used",
@@ -7129,7 +7134,7 @@
{ {
"id": "getFreeAddressCount failed %s", "id": "getFreeAddressCount failed %s",
"message": "getFreeAddressCount failed %s", "message": "getFreeAddressCount failed %s",
"translation": "" "translation": "getFreeAddressCount失败: %s"
}, },
{ {
"id": "network %s(%s) has no free addresses", "id": "network %s(%s) has no free addresses",
@@ -7489,7 +7494,7 @@
{ {
"id": "GenerateName failed %s", "id": "GenerateName failed %s",
"message": "GenerateName failed %s", "message": "GenerateName failed %s",
"translation": "" "translation": "GenerateName失败: %s"
}, },
{ {
"id": "Generate ifname hint failed %s", "id": "Generate ifname hint failed %s",
@@ -7754,7 +7759,7 @@
{ {
"id": "unmarshal JointResourceCreateInput failed %s", "id": "unmarshal JointResourceCreateInput failed %s",
"message": "unmarshal JointResourceCreateInput failed %s", "message": "unmarshal JointResourceCreateInput failed %s",
"translation": "" "translation": "解析 JointResourceCreateInput 失败: %s"
}, },
{ {
"id": "Invalid schedtag %s", "id": "Invalid schedtag %s",
@@ -7774,7 +7779,7 @@
{ {
"id": "GetObjectCount failed %s", "id": "GetObjectCount failed %s",
"message": "GetObjectCount failed %s", "message": "GetObjectCount failed %s",
"translation": "" "translation": "GetObjectCount失败: %s"
}, },
{ {
"id": "Tag is associated with %s", "id": "Tag is associated with %s",
@@ -7784,7 +7789,7 @@
{ {
"id": "getDynamicSchedtagCount failed %s", "id": "getDynamicSchedtagCount failed %s",
"message": "getDynamicSchedtagCount failed %s", "message": "getDynamicSchedtagCount failed %s",
"translation": "" "translation": "getDynamicSchedtagCount失败: %s"
}, },
{ {
"id": "tag has dynamic rules", "id": "tag has dynamic rules",
@@ -7794,7 +7799,7 @@
{ {
"id": "getSchedPoliciesCount failed %s", "id": "getSchedPoliciesCount failed %s",
"message": "getSchedPoliciesCount failed %s", "message": "getSchedPoliciesCount failed %s",
"translation": "" "translation": "getSchedPoliciesCount失败: %s"
}, },
{ {
"id": "tag is associated with scheduling policies", "id": "tag is associated with scheduling policies",
@@ -7906,6 +7911,11 @@
"message": "not allowed to delete public cloud instance_type: %s", "message": "not allowed to delete public cloud instance_type: %s",
"translation": "不允许删除公有云instance_type: %s" "translation": "不允许删除公有云instance_type: %s"
}, },
{
"id": "zone_ids must be in the same cloudregion",
"message": "zone_ids must be in the same cloudregion",
"translation": "zone_ids 必须属于同一区域"
},
{ {
"id": "duplicate instanceType %s", "id": "duplicate instanceType %s",
"message": "duplicate instanceType %s", "message": "duplicate instanceType %s",
@@ -8022,8 +8032,8 @@
"translation": "Snapshot有 %d 个活跃任务,无法同步状态" "translation": "Snapshot有 %d 个活跃任务,无法同步状态"
}, },
{ {
"id": "Cannot delete disk %s snapshots, disk still exists", "id": "Cannot Delete disk %s snapshots, disk exist",
"message": "Cannot delete disk %s snapshots, disk still exists", "message": "Cannot Delete disk %s snapshots, disk exist",
"translation": "无法删除磁盘 %s 的快照,磁盘仍然存在" "translation": "无法删除磁盘 %s 的快照,磁盘仍然存在"
}, },
{ {
@@ -8044,7 +8054,7 @@
{ {
"id": "getReferenceCount failed %s", "id": "getReferenceCount failed %s",
"message": "getReferenceCount failed %s", "message": "getReferenceCount failed %s",
"translation": "" "translation": "getReferenceCount失败: %s"
}, },
{ {
"id": "Image is in use", "id": "Image is in use",
@@ -8472,8 +8482,8 @@
"translation": "主实例内在大于等于64GB最多可创建10个只读实例" "translation": "主实例内在大于等于64GB最多可创建10个只读实例"
}, },
{ {
"id": "Master dbinstance memory \u003c64GB, up to 5 read-only instances are allowed to be created", "id": "Master dbinstance memory <64GB, up to 5 read-only instances are allowed to be created",
"message": "Master dbinstance memory \u003c64GB, up to 5 read-only instances are allowed to be created", "message": "Master dbinstance memory <64GB, up to 5 read-only instances are allowed to be created",
"translation": "主实例内在小于64GB最多可创建5个只读实例" "translation": "主实例内在小于64GB最多可创建5个只读实例"
}, },
{ {
@@ -8952,9 +8962,9 @@
"translation": "不允许访问admin key" "translation": "不允许访问admin key"
}, },
{ {
"id": "Snapshot reference(by disk) count \u003e 0, cannot delete", "id": "Snapshot reference(by disk) count > 0, cannot delete",
"message": "Snapshot reference(by disk) count \u003e 0, cannot delete", "message": "Snapshot reference(by disk) count > 0, cannot delete",
"translation": "快照引用(按磁盘)数量\u003e0无法删除" "translation": "快照引用(按磁盘)数量>0无法删除"
}, },
{ {
"id": "Disk %s is not attached to a guest", "id": "Disk %s is not attached to a guest",
@@ -9871,11 +9881,146 @@
"message": "not allow to get usage", "message": "not allow to get usage",
"translation": "不允许获取使用量" "translation": "不允许获取使用量"
}, },
{
"id": "invalid url %q",
"message": "invalid url %q",
"translation": "无效的 URL: %q"
},
{
"id": "only http(s) urls are allowed",
"message": "only http(s) urls are allowed",
"translation": "仅允许 http(s) URL"
},
{
"id": "upstream host %s is not in the proxy allow list",
"message": "upstream host %s is not in the proxy allow list",
"translation": "上游主机 %s 不在代理白名单中"
},
{
"id": "GPU devices are required for %s: set GPU in the request or configure GPU on the LLM SKU",
"message": "GPU devices are required for %s: set GPU in the request or configure GPU on the LLM SKU",
"translation": "%s 需要配置 GPU 设备:请在请求中设置 GPU或在推理模板中配置 GPU"
},
{
"id": "mounted models are required for %s: set mounted_models in the request or configure models on the LLM SKU",
"message": "mounted models are required for %s: set mounted_models in the request or configure models on the LLM SKU",
"translation": "%s 需要配置挂载模型:请在请求中设置 mounted_models或在推理模板中配置挂载模型"
},
{
"id": "LLM is being used by %d MCPAgents",
"message": "LLM is being used by %d MCPAgents",
"translation": "LLM 正被 %d 个 MCP Agent 使用"
},
{
"id": "invalid provider_type %q",
"message": "invalid provider_type %q",
"translation": "无效的 provider_type: %q"
},
{
"id": "provider_type %q is not supported",
"message": "provider_type %q is not supported",
"translation": "不支持的 provider_type: %q"
},
{
"id": "provider_type %q does not support listing models",
"message": "provider_type %q does not support listing models",
"translation": "provider_type %q 不支持列出模型"
},
{
"id": "list models from %q via %q: %v",
"message": "list models from %q via %q: %v",
"translation": "通过 %q 从 %q 列出模型失败: %v"
},
{
"id": "Not found host by public_ip %s",
"message": "Not found host by public_ip %s",
"translation": "未找到公网 IP 为 %s 的主机"
},
{
"id": "app container driver %s not found",
"message": "app container driver %s not found",
"translation": "找不到应用容器驱动 %s"
},
{
"id": "auto_gpu_memory_utilization requires mounted models: configure mounted_models on the LLM SKU",
"message": "auto_gpu_memory_utilization requires mounted models: configure mounted_models on the LLM SKU",
"translation": "开启 auto_gpu_memory_utilization 需要先在推理模板中配置挂载模型mounted_models"
},
{
"id": "auto_gpu_memory_utilization requires GPU devices: configure GPU on the LLM SKU",
"message": "auto_gpu_memory_utilization requires GPU devices: configure GPU on the LLM SKU",
"translation": "开启 auto_gpu_memory_utilization 需要先在推理模板中配置 GPU 设备"
},
{
"id": "LLM is not running",
"message": "LLM is not running",
"translation": "LLM 未运行"
},
{
"id": "ModelId %s not found",
"message": "ModelId %s not found",
"translation": "找不到模型 ID %s"
},
{
"id": "url %s not found",
"message": "url %s not found",
"translation": "找不到 URL %s"
},
{
"id": "no available models found in LLM instance %s",
"message": "no available models found in LLM instance %s",
"translation": "LLM 实例 %s 中无可用模型"
},
{
"id": "Parse query string %q: %v",
"message": "Parse query string %q: %v",
"translation": "解析查询字符串 %q 失败: %v"
},
{
"id": "model set %s not found",
"message": "model set %s not found",
"translation": "找不到模型集 %s"
},
{
"id": "missing model set id",
"message": "missing model set id",
"translation": "缺少模型集 ID"
},
{
"id": "model spec %s not found",
"message": "model spec %s not found",
"translation": "找不到模型规格 %s"
},
{ {
"id": "Unauthorized", "id": "Unauthorized",
"message": "Unauthorized", "message": "Unauthorized",
"translation": "未授权" "translation": "未授权"
}, },
{
"id": "system admin required",
"message": "system admin required",
"translation": "需要系统管理员权限"
},
{
"id": "images catalog item %s not found",
"message": "images catalog item %s not found",
"translation": "找不到镜像目录项 %s"
},
{
"id": "invalid body: %v",
"message": "invalid body: %v",
"translation": "无效的请求体: %v"
},
{
"id": "invalid input: %v",
"message": "invalid input: %v",
"translation": "无效的输入: %v"
},
{
"id": "no default MCP agent set (set one agent with default_agent=true)",
"message": "no default MCP agent set (set one agent with default_agent=true)",
"translation": "未设置默认 MCP Agent请将某个 Agent 的 default_agent 设为 true"
},
{ {
"id": "InvalidToken", "id": "InvalidToken",
"message": "InvalidToken", "message": "InvalidToken",
@@ -10077,9 +10222,9 @@
"translation": "不支持的execution_error_state %s" "translation": "不支持的execution_error_state %s"
}, },
{ {
"id": "metric %s is invalid format, usage \u003cmeasurement\u003e.\u003cfield\u003e", "id": "metric %s is invalid format, usage <measurement>.<field>",
"message": "metric %s is invalid format, usage \u003cmeasurement\u003e.\u003cfield\u003e", "message": "metric %s is invalid format, usage <measurement>.<field>",
"translation": "metric %s 格式无效,用法 \u003cmeasurement\u003e.\u003cfield\u003e" "translation": "metric %s 格式无效,用法 <measurement>.<field>"
}, },
{ {
"id": "Cannot change state on pause alert", "id": "Cannot change state on pause alert",
@@ -10512,4 +10657,4 @@
"translation": "权限定义正被使用" "translation": "权限定义正被使用"
} }
] ]
} }

View File

@@ -2445,7 +2445,7 @@ func (manager *SGuestManager) ValidateCreateData(ctx context.Context, userCred m
func validateKickstartConfig(config *api.KickstartConfig) error { func validateKickstartConfig(config *api.KickstartConfig) error {
if config.OSType == "" { if config.OSType == "" {
return httperrors.NewMissingParameterError("os_type") return httperrors.NewInputParameterError("os_type is required")
} }
if !utils.IsInStringArray(config.OSType, api.KICKSTART_VALID_OS_TYPES) { if !utils.IsInStringArray(config.OSType, api.KICKSTART_VALID_OS_TYPES) {

View File

@@ -111,11 +111,41 @@ func ValidateRequireDevices(
effectiveDevices = sku.Devices effectiveDevices = sku.Devices
} }
if devicesIsEmpty(effectiveDevices) { if devicesIsEmpty(effectiveDevices) {
return errors.Wrapf(httperrors.ErrInputParameter, "devices is required for %s: specify in request or set on sku", llmType) return httperrors.NewInputParameterError("GPU devices are required for %s: set GPU in the request or configure GPU on the LLM SKU", llmType)
} }
return nil return nil
} }
var llmContainerTypesRequireGPUDevices = map[api.LLMContainerType]struct{}{
api.LLM_CONTAINER_VLLM: {},
api.LLM_CONTAINER_SGLANG: {},
api.LLM_CONTAINER_OLLAMA: {},
api.LLM_CONTAINER_COMFYUI: {},
}
// ValidateDeploymentDevices ensures the deployment SKU (existing or sku_spec) has GPU devices when required by llm_type.
func ValidateDeploymentDevices(llmType string, sku *SLLMSku) error {
if sku == nil || llmType == "" {
return nil
}
if _, ok := llmContainerTypesRequireGPUDevices[api.LLMContainerType(llmType)]; !ok {
return nil
}
return ValidateRequireDevices(llmType, nil, nil, sku)
}
func skuFromLLMSkuCreateInput(input *api.LLMSkuCreateInput) *SLLMSku {
if input == nil {
return nil
}
return &SLLMSku{
LLMType: input.LLMType,
SLLMSkuBase: SLLMSkuBase{
Devices: input.Devices,
},
}
}
// ValidateRequireMountedModels errors if neither input nor existing llm nor sku supplies mounted_models. For create, pass nil/empty for llmCurMountedModels. // ValidateRequireMountedModels errors if neither input nor existing llm nor sku supplies mounted_models. For create, pass nil/empty for llmCurMountedModels.
func ValidateRequireMountedModels( func ValidateRequireMountedModels(
llmType string, llmType string,
@@ -131,7 +161,7 @@ func ValidateRequireMountedModels(
effectiveModels = sku.GetMountedModels() effectiveModels = sku.GetMountedModels()
} }
if len(effectiveModels) == 0 { if len(effectiveModels) == 0 {
return errors.Wrapf(httperrors.ErrInputParameter, "mounted_models is required for %s: specify in request or set on sku", llmType) return httperrors.NewInputParameterError("mounted models are required for %s: set mounted_models in the request or configure models on the LLM SKU", llmType)
} }
return nil return nil
} }
@@ -190,7 +220,7 @@ func (man *SLLMManager) ValidateCreateData(ctx context.Context, userCred mcclien
drv := lSku.GetLLMContainerDriver() drv := lSku.GetLLMContainerDriver()
input, err = drv.ValidateLLMCreateData(ctx, userCred, lSku, input) input, err = drv.ValidateLLMCreateData(ctx, userCred, lSku, input)
if err != nil { if err != nil {
return input, errors.Wrap(err, "validate LLM create data") return input, err
} }
return input, nil return input, nil
@@ -569,7 +599,7 @@ func (llm *SLLM) ValidateUpdateData(ctx context.Context, userCred mcclient.Token
drv := sku.GetLLMContainerDriver() drv := sku.GetLLMContainerDriver()
out, err := drv.ValidateLLMUpdateData(ctx, userCred, llm, sku, &input) out, err := drv.ValidateLLMUpdateData(ctx, userCred, llm, sku, &input)
if err != nil { if err != nil {
return input, errors.Wrap(err, "validate LLM update data") return input, err
} }
if out != nil { if out != nil {
input = *out input = *out

View File

@@ -145,6 +145,9 @@ func (man *SLLMDeploymentManager) ValidateCreateData(
if err := validateDeploymentGpuMemoryUtilization(input.GpuMemoryUtilization, input.AutoGpuMemoryUtilization, lSku.LLMType); err != nil { if err := validateDeploymentGpuMemoryUtilization(input.GpuMemoryUtilization, input.AutoGpuMemoryUtilization, lSku.LLMType); err != nil {
return input, err return input, err
} }
if err := ValidateDeploymentDevices(lSku.LLMType, lSku); err != nil {
return input, err
}
input.LLMSkuId = lSku.GetId() input.LLMSkuId = lSku.GetId()
// ModelSpec is meaningless here // ModelSpec is meaningless here
input.ModelSpec = nil input.ModelSpec = nil
@@ -173,6 +176,9 @@ func (man *SLLMDeploymentManager) ValidateCreateData(
if err := validateDeploymentGpuMemoryUtilization(input.GpuMemoryUtilization, input.AutoGpuMemoryUtilization, input.SkuSpec.LLMType); err != nil { if err := validateDeploymentGpuMemoryUtilization(input.GpuMemoryUtilization, input.AutoGpuMemoryUtilization, input.SkuSpec.LLMType); err != nil {
return input, err return input, err
} }
if err := ValidateDeploymentDevices(input.SkuSpec.LLMType, skuFromLLMSkuCreateInput(input.SkuSpec)); err != nil {
return input, err
}
} }
// Validate placement strategy // Validate placement strategy

View File

@@ -0,0 +1,44 @@
package models
import (
"testing"
api "yunion.io/x/onecloud/pkg/apis/llm"
)
func TestValidateDeploymentDevices(t *testing.T) {
devices := api.Devices{{Model: "NVIDIA A100"}}
skuWithGPU := &SLLMSku{
LLMType: string(api.LLM_CONTAINER_VLLM),
SLLMSkuBase: SLLMSkuBase{
Devices: &devices,
},
}
skuWithoutGPU := &SLLMSku{LLMType: string(api.LLM_CONTAINER_VLLM)}
if err := ValidateDeploymentDevices(string(api.LLM_CONTAINER_VLLM), skuWithGPU); err != nil {
t.Fatalf("expected no error with GPU devices, got %v", err)
}
if err := ValidateDeploymentDevices(string(api.LLM_CONTAINER_VLLM), skuWithoutGPU); err == nil {
t.Fatal("expected error when vllm SKU has no GPU devices")
}
if err := ValidateDeploymentDevices(string(api.LLM_CONTAINER_DIFY), skuWithoutGPU); err != nil {
t.Fatalf("dify deployment should not require GPU devices, got %v", err)
}
}
func TestSkuFromLLMSkuCreateInput(t *testing.T) {
devices := api.Devices{{Model: "NVIDIA A100"}}
sku := skuFromLLMSkuCreateInput(&api.LLMSkuCreateInput{
LLMType: string(api.LLM_CONTAINER_SGLANG),
LLMSKuBaseCreateInput: api.LLMSKuBaseCreateInput{
Devices: &devices,
},
})
if sku == nil || sku.LLMType != string(api.LLM_CONTAINER_SGLANG) {
t.Fatalf("unexpected sku: %#v", sku)
}
if sku.Devices == nil || len(*sku.Devices) != 1 {
t.Fatalf("expected devices on sku, got %#v", sku.Devices)
}
}

View File

@@ -171,7 +171,7 @@ func BuildDeploymentResolvedGpuMemoryLLMSpec(ctx context.Context, userCred mccli
func maxMountedModelSizeMB(sku *SLLMSku) (int64, error) { func maxMountedModelSizeMB(sku *SLLMSku) (int64, error) {
modelIds := sku.GetMountedModels() modelIds := sku.GetMountedModels()
if len(modelIds) == 0 { if len(modelIds) == 0 {
return 0, errors.Wrap(httperrors.ErrInputParameter, "auto_gpu_memory_utilization requires mounted_models") return 0, httperrors.NewInputParameterError("auto_gpu_memory_utilization requires mounted models: configure mounted_models on the LLM SKU")
} }
var maxSize int64 var maxSize int64
for _, modelId := range modelIds { for _, modelId := range modelIds {
@@ -192,7 +192,7 @@ func maxMountedModelSizeMB(sku *SLLMSku) (int64, error) {
func minGpuMemoryMB(ctx context.Context, userCred mcclient.TokenCredential, devices *api.Devices) (int64, error) { func minGpuMemoryMB(ctx context.Context, userCred mcclient.TokenCredential, devices *api.Devices) (int64, error) {
if devices == nil || len(*devices) == 0 { if devices == nil || len(*devices) == 0 {
return 0, errors.Wrap(httperrors.ErrInputParameter, "auto_gpu_memory_utilization requires devices") return 0, httperrors.NewInputParameterError("auto_gpu_memory_utilization requires GPU devices: configure GPU on the LLM SKU")
} }
var minMemory int64 var minMemory int64
for i := range *devices { for i := range *devices {