package chaterror import ( "errors" "net/http" "strconv" "strings" "time" "charm.land/fantasy" ) type providerErrorDetails struct { statusCode int retryAfter time.Duration } func extractProviderErrorDetails(err error) providerErrorDetails { var providerErr *fantasy.ProviderError if !errors.As(err, &providerErr) { return providerErrorDetails{} } return providerErrorDetails{ statusCode: providerErr.StatusCode, retryAfter: retryAfterFromHeaders(providerErr.ResponseHeaders), } } func retryAfterFromHeaders(headers map[string]string) time.Duration { if len(headers) == 0 { return 0 } // Prefer retry-after-ms (OpenAI convention, milliseconds) // over the standard retry-after (seconds or HTTP-date). for key, value := range headers { if strings.EqualFold(key, "retry-after-ms") { ms, err := strconv.ParseFloat(strings.TrimSpace(value), 64) if err == nil && ms > 0 { return time.Duration(ms * float64(time.Millisecond)) } } } for key, value := range headers { if strings.EqualFold(key, "retry-after") { v := strings.TrimSpace(value) if seconds, err := strconv.ParseFloat(v, 64); err == nil { if seconds > 0 { return time.Duration(seconds * float64(time.Second)) } return 0 } if retryAt, err := http.ParseTime(v); err == nil { if d := time.Until(retryAt); d > 0 { return d } } return 0 } } return 0 }