发布 0.2.0:模型池自愈、探测修正、任务异步触发与删除加固
This commit is contained in:
@@ -43,10 +43,13 @@ func (s *AiGatewayService) Chat(ctx context.Context, ir aiwire.ChatRequest, grou
|
||||
s.markSuccess(ctx, cand.ch.ID)
|
||||
return resp, meta, nil
|
||||
}
|
||||
if !retryable(err) {
|
||||
retry, penalize := s.noteCallErr(ctx, cand, err)
|
||||
if !retry {
|
||||
return nil, meta, err
|
||||
}
|
||||
s.markFailure(ctx, cand.ch.ID)
|
||||
if penalize {
|
||||
s.markFailure(ctx, cand.ch.ID)
|
||||
}
|
||||
excluded[cand.ch.ID] = true
|
||||
meta.Retries++
|
||||
lastErr = err
|
||||
@@ -83,10 +86,13 @@ func (s *AiGatewayService) OpenStream(ctx context.Context, ir aiwire.ChatRequest
|
||||
s.markSuccess(ctx, cand.ch.ID)
|
||||
return stream, meta, nil
|
||||
}
|
||||
if !retryable(err) {
|
||||
retry, penalize := s.noteCallErr(ctx, cand, err)
|
||||
if !retry {
|
||||
return nil, meta, err
|
||||
}
|
||||
s.markFailure(ctx, cand.ch.ID)
|
||||
if penalize {
|
||||
s.markFailure(ctx, cand.ch.ID)
|
||||
}
|
||||
excluded[cand.ch.ID] = true
|
||||
meta.Retries++
|
||||
lastErr = err
|
||||
@@ -102,12 +108,18 @@ func firstErr(lastErr, pickErr error) error {
|
||||
return pickErr
|
||||
}
|
||||
|
||||
// retryable 判定是否换渠道重试:429 / 5xx / 网络错误可重试,其余 4xx 直接透传。
|
||||
func retryable(err error) bool {
|
||||
if status, ok := oci.ServiceStatus(err); ok {
|
||||
return status == 429 || status >= 500
|
||||
// noteCallErr 汇总一次调用失败:模型级不可用(微调基座 400 / 实体不存在 404)先标记
|
||||
// 剔除该 (渠道, 模型),换渠道重试且不计熔断——这是模型×区域供给问题而非渠道健康问题;
|
||||
// 429 / 5xx / 网络错误换渠道并计熔断;其余 4xx 直接透传。
|
||||
func (s *AiGatewayService) noteCallErr(ctx context.Context, cand *aiCandidate, err error) (retry, penalize bool) {
|
||||
if oci.IsModelUnavailable(err) {
|
||||
s.markModelUnusable(ctx, cand.ch.ID, cand.modelOcid, oci.CompactError(err))
|
||||
return true, false
|
||||
}
|
||||
return true
|
||||
if status, ok := oci.ServiceStatus(err); ok {
|
||||
return status == 429 || status >= 500, true
|
||||
}
|
||||
return true, true
|
||||
}
|
||||
|
||||
// pick 选出支持该模型的最优渠道:能力匹配 → 启用 → 分组匹配 → 未熔断 → 最小优先级组 → 加权随机。
|
||||
@@ -142,10 +154,11 @@ func (s *AiGatewayService) pick(ctx context.Context, modelName, group, capabilit
|
||||
return &aiCandidate{ch: chosen, modelOcid: ocids[chosen.ID]}, nil
|
||||
}
|
||||
|
||||
// modelChannels 查出提供该模型的渠道 ID 及各自的模型 OCID;
|
||||
// capability=CHAT 时兼容存量空串(加列前只同步对话模型)。
|
||||
// modelChannels 查出提供该模型的渠道 ID 及各自的模型 OCID(剔除不可按需调用标记);
|
||||
// capability=CHAT 时兼容存量空串(加列前只同步对话模型);unusable 需兼容 NULL
|
||||
// (AutoMigrate 加列后、首次重同步前的存量行)。
|
||||
func (s *AiGatewayService) modelChannels(ctx context.Context, modelName, capability string) (map[uint]string, []uint, error) {
|
||||
q := s.db.WithContext(ctx).Where("name = ?", modelName)
|
||||
q := s.db.WithContext(ctx).Where("name = ? AND (unusable = ? OR unusable IS NULL)", modelName, false)
|
||||
if capability == "CHAT" {
|
||||
q = q.Where("capability IN ?", []string{"CHAT", ""})
|
||||
} else {
|
||||
@@ -221,10 +234,13 @@ func (s *AiGatewayService) Embeddings(ctx context.Context, req aiwire.Embeddings
|
||||
s.markSuccess(ctx, cand.ch.ID)
|
||||
return resp, meta, nil
|
||||
}
|
||||
if !retryable(err) {
|
||||
retry, penalize := s.noteCallErr(ctx, cand, err)
|
||||
if !retry {
|
||||
return nil, meta, err
|
||||
}
|
||||
s.markFailure(ctx, cand.ch.ID)
|
||||
if penalize {
|
||||
s.markFailure(ctx, cand.ch.ID)
|
||||
}
|
||||
excluded[cand.ch.ID] = true
|
||||
meta.Retries++
|
||||
lastErr = err
|
||||
|
||||
Reference in New Issue
Block a user