Files
wangdefa 7706f59549
CI / test (push) Successful in 32s
Release / release (push) Successful in 52s
发布 0.2.0:模型池自愈、探测修正、任务异步触发与删除加固
2026-07-10 20:25:37 +08:00

110 lines
4.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package oci
import (
"errors"
"regexp"
"strings"
"github.com/oracle/oci-go-sdk/v65/common"
)
// ociErrorHints 把常见 OCI 错误码翻译成给用户看的中文提示。
var ociErrorHints = map[string]string{
"IncorrectState": "资源当前状态不允许该操作,通常是仍被其他资源占用或正在变更中",
"NotAuthorizedOrNotFound": "无权限或资源不存在(可能已被删除)",
"LimitExceeded": "已达到服务配额上限",
"QuotaExceeded": "compartment 配额不足",
"OutOfHostCapacity": "该可用域容量不足,可稍后重试或改用抢机任务",
"TooManyRequests": "请求过于频繁,请稍后再试",
"InvalidParameter": "请求参数无效",
"InternalError": "OCI 服务内部错误,请稍后重试",
"Conflict": "资源正在变更中,请稍后重试",
}
// ocidRe 匹配消息中的完整 OCIDunique 段 20 位以上才压缩,避免误伤短标识)。
var ocidRe = regexp.MustCompile(`ocid1\.([a-z0-9]+)\.[a-z0-9]*\.[a-z0-9-]*\.{1,2}[a-z0-9]{20,}`)
// shortenOcids 把消息里的长 OCID 压缩为「ocid1.<类型>…<尾6位>」,
// 保留资源类型与可比对的尾部,避免整条错误被 OCID 撑爆。
func shortenOcids(s string) string {
return ocidRe.ReplaceAllStringFunc(s, func(m string) string {
i := strings.Index(m, ".")
typ := m[i+1:]
if j := strings.Index(typ, "."); j > 0 {
typ = typ[:j]
}
return "ocid1." + typ + "…" + m[len(m)-6:]
})
}
// CompactError 把含 OCI ServiceError 的错误链提炼成短消息:
// 操作前缀 + 服务端消息,剥掉 SDK 附加的错误码、排障链接、
// 时间戳、SDK 版本等长文(错误码另经 API 的 ociCode 字段透出);
// 消息中的长 OCID 压缩为类型加尾位的短形式。非 OCI 错误原样返回。
func CompactError(err error) string {
var svcErr common.ServiceError
if !errors.As(err, &svcErr) {
return shortenOcids(err.Error())
}
prefix := err.Error()
if i := strings.Index(prefix, ": Error returned by"); i > 0 {
prefix = prefix[:i] + ": "
} else {
prefix = ""
}
return shortenOcids(prefix + svcErr.GetMessage())
}
// ErrorHint 返回常见 OCI 错误的中文提示;未识别返回空串。
// 容量不足常以 code=InternalError 返回,按消息内容归到容量提示。
func ErrorHint(err error) string {
var svcErr common.ServiceError
if !errors.As(err, &svcErr) {
return ""
}
if strings.Contains(svcErr.GetMessage(), "Out of host capacity") {
return ociErrorHints["OutOfHostCapacity"]
}
return ociErrorHints[svcErr.GetCode()]
}
// ServiceStatus 返回错误链中 OCI ServiceError 的 HTTP 状态码;非服务端错误 ok=false。
func ServiceStatus(err error) (int, bool) {
var svcErr common.ServiceError
if errors.As(err, &svcErr) {
return svcErr.GetHTTPStatusCode(), true
}
return 0, false
}
// IsOnDemandUnsupported 识别「模型在该区域仅为微调基座、不支持按需调用」的 400:
// OCI 消息形如 "Not allowed to call finetune base model …, use Endpoint: false"。
// ListModels 无字段可事先区分,只能在调用报错时识别并换渠道。
func IsOnDemandUnsupported(err error) bool {
var svcErr common.ServiceError
if !errors.As(err, &svcErr) {
return false
}
return svcErr.GetHTTPStatusCode() == 400 &&
strings.Contains(strings.ToLower(svcErr.GetMessage()), "finetune base model")
}
// IsEntityNotFound 识别「实体不存在」404(消息 "Entity with key … not found"):
// GenAI 对区域内无按需供给的模型 OCID 返回此类 404,属模型级错误;
// 鉴权失败的 404 是 NotAuthorizedOrNotFound(消息为 Authorization failed…),不在此列。
func IsEntityNotFound(err error) bool {
var svcErr common.ServiceError
if !errors.As(err, &svcErr) {
return false
}
msg := strings.ToLower(svcErr.GetMessage())
return svcErr.GetHTTPStatusCode() == 404 &&
strings.Contains(msg, "entity with key") && strings.Contains(msg, "not found")
}
// IsModelUnavailable 归并「模型×区域不可按需调用」两类报错:微调基座 400 与实体不存在 404;
// 命中即应把该 (渠道, 模型) 从池中剔除并换候选/换渠道,而非定论租户配额问题。
func IsModelUnavailable(err error) bool {
return IsOnDemandUnsupported(err) || IsEntityNotFound(err)
}