54 lines
1.7 KiB
Go
54 lines
1.7 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"gitea.redpowerfuture.com/red-future/common/oss"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
)
|
|
|
|
// modelCallMaxRetries 上游调用最大重试次数
|
|
const modelCallMaxRetries = 10
|
|
|
|
// retryWait 指数退避等待(第 attempt 次重试,等待 1<<attempt 秒)。
|
|
// 返回 nil 表示可继续重试;ctx 已取消返回 ctx.Err(),调用方应停止。
|
|
func retryWait(ctx context.Context, attempt int) error {
|
|
wait := time.Duration(1<<attempt) * time.Second
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-time.After(wait):
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// isRetryableErrorCode 判定上游返回的错误码是否可重试:限流(429/limit_requests/limit_tokens/rate_limit_exceeded)与 5xx(500-503)。
|
|
// httpclient.ModelHttpNormalRequest 不返回 HTTP status,只能按响应体 error.code 字符串判定。
|
|
func isRetryableErrorCode(code string) bool {
|
|
switch code {
|
|
case "429", "500", "501", "502", "503", "InvalidParameter", "limit_requests", "limit_tokens", "rate_limit_exceeded":
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// firstText 取任意值首位文本:数组取首个元素,其余原样转字符串
|
|
func firstText(v any) string {
|
|
if arr, ok := v.([]any); ok && len(arr) > 0 {
|
|
return gconv.String(arr[0])
|
|
}
|
|
return gconv.String(v)
|
|
}
|
|
|
|
// extractChunkText 从流式分片字段取值并转存 OSS,返回首位文本(数组取首个元素,去掉首尾空白)。
|
|
// 空值 / 仅空白 / 数组全空 返回空串。
|
|
func extractChunkText(ctx context.Context, v any) string {
|
|
if v == nil || g.IsEmpty(v) {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(firstText(oss.TempURLToOSS(ctx, v)))
|
|
}
|