refactor(runner): 收敛上游失败决策与轮转策略

将同平台重试、跨平台动作、健康副作用和冷却排队统一到单一失败决策,避免旧降级策略与 failover 重复执行。\n\n增加事务级单源保护、候选实时刷新、冷却错误契约、管理接口严格校验及兼容策略只读展示。\n\n验证:真实 Gateway HTTP/PostgreSQL 接受测试 10 项通过;go test ./...、pnpm openapi、pnpm lint、pnpm test、pnpm build 均通过。
This commit is contained in:
2026-07-27 23:50:17 +08:00
parent 046c16fc69
commit 31565af07a
19 changed files with 1988 additions and 307 deletions
+14 -1
View File
@@ -980,6 +980,7 @@ func (s *Server) estimatePricing(w http.ResponseWriter, r *http.Request) {
return
}
if errors.Is(err, store.ErrNoModelCandidate) {
applyRunErrorHeaders(w, err)
writeErrorWithDetails(w, statusFromRunError(err), runErrorMessage(err), runErrorDetails(err), store.ModelCandidateErrorCode(err))
return
}
@@ -1421,6 +1422,7 @@ func writeProtocolCompatibleTaskResponse(runCtx context.Context, w http.Response
if !requestStillConnected(r) {
return
}
applyRunErrorHeaders(w, runErr)
errorPayload := map[string]any{"message": runErrorMessage(runErr), "type": "server_error", "param": nil, "code": runErrorCode(runErr)}
if status := statusFromRunError(runErr); status >= 400 && status < 500 {
errorPayload["type"] = "invalid_request_error"
@@ -1458,6 +1460,7 @@ func writeProtocolCompatibleTaskResponse(runCtx context.Context, w http.Response
if !requestStillConnected(r) {
return
}
applyRunErrorHeaders(w, runErr)
if wire := clients.ErrorWireResponse(runErr); wireResponseMatches(wire, targetProtocol) {
writeWireResponse(w, wire)
return
@@ -1694,11 +1697,21 @@ func runErrorDetails(err error) map[string]any {
return map[string]any{"rateLimit": detail}
}
if detail := store.ModelCandidateErrorDetails(err); len(detail) > 0 {
return map[string]any{"modelCandidate": detail}
return detail
}
return nil
}
func applyRunErrorHeaders(w http.ResponseWriter, err error) {
if retryAfter := store.ModelCandidateRetryAfter(err); retryAfter > 0 {
seconds := int((retryAfter + time.Second - 1) / time.Second)
if seconds < 1 {
seconds = 1
}
w.Header().Set("Retry-After", strconv.Itoa(seconds))
}
}
func rateLimitErrorSummary(err error) string {
var limitErr *store.RateLimitExceededError
if !errors.As(err, &limitErr) {