-
Notifications
You must be signed in to change notification settings - Fork 66
Expand file tree
/
Copy patherrors.go
More file actions
142 lines (123 loc) · 3.56 KB
/
errors.go
File metadata and controls
142 lines (123 loc) · 3.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package fantasy
import (
"errors"
"fmt"
"io"
"net/http"
"strconv"
"strings"
"github.com/charmbracelet/x/exp/slice"
)
// Error is a custom error type for the fantasy package.
type Error struct {
Message string
Title string
Cause error
}
func (err *Error) Error() string {
if err.Title == "" {
return err.Message
}
return fmt.Sprintf("%s: %s", err.Title, err.Message)
}
func (err Error) Unwrap() error {
return err.Cause
}
// ProviderError represents an error returned by an external provider.
type ProviderError struct {
Message string
Title string
Cause error
URL string
StatusCode int
RequestBody []byte
ResponseHeaders map[string]string
ResponseBody []byte
ContextUsedTokens int
ContextMaxTokens int
ContextTooLargeErr bool
}
func (m *ProviderError) Error() string {
if m.Title == "" {
return m.Message
}
return fmt.Sprintf("%s: %s", m.Title, m.Message)
}
// IsRetryable reports whether the error should be retried.
// It returns true if the underlying cause is io.ErrUnexpectedEOF, if the
// "x-should-retry" response header evaluates to true, or if the HTTP status
// code indicates a retryable condition (408, 409, 429, or any 5xx).
func (m *ProviderError) IsRetryable() bool {
// We're mostly mimicking OpenAI's Go SDK here:
// https://github.com/openai/openai-go/blob/b9d280a37149430982e9dfeed16c41d27d45cfc5/internal/requestconfig/requestconfig.go#L244
if errors.Is(m.Cause, io.ErrUnexpectedEOF) {
return true
}
if m.shouldRetryHeader() {
return true
}
return m.StatusCode == http.StatusRequestTimeout ||
m.StatusCode == http.StatusConflict ||
m.StatusCode == http.StatusTooManyRequests ||
m.StatusCode >= http.StatusInternalServerError
}
func (m *ProviderError) shouldRetryHeader() bool {
if m.ResponseHeaders == nil {
return false
}
for k, v := range m.ResponseHeaders {
if strings.EqualFold(k, "x-should-retry") {
b, _ := strconv.ParseBool(v)
return b
}
}
return false
}
// IsContextTooLarge checks if the error is due to the context exceeding the model's limit.
func (m *ProviderError) IsContextTooLarge() bool {
return m.ContextTooLargeErr || m.ContextMaxTokens > 0 || m.ContextUsedTokens > 0
}
// RetryError represents an error that occurred during retry operations.
type RetryError struct {
Errors []error
}
func (e *RetryError) Error() string {
if err, ok := slice.Last(e.Errors); ok {
return fmt.Sprintf("retry error: %v", err)
}
return "retry error: no underlying errors"
}
func (e RetryError) Unwrap() error {
if err, ok := slice.Last(e.Errors); ok {
return err
}
return nil
}
// ErrorTitleForStatusCode returns a human-readable title for a given HTTP status code.
func ErrorTitleForStatusCode(statusCode int) string {
return strings.ToLower(http.StatusText(statusCode))
}
// NoObjectGeneratedError is returned when object generation fails
// due to parsing errors, validation errors, or model failures.
type NoObjectGeneratedError struct {
RawText string
ParseError error
ValidationError error
Usage Usage
FinishReason FinishReason
}
// Error implements the error interface.
func (e *NoObjectGeneratedError) Error() string {
if e.ValidationError != nil {
return fmt.Sprintf("object validation failed: %v", e.ValidationError)
}
if e.ParseError != nil {
return fmt.Sprintf("failed to parse object: %v", e.ParseError)
}
return "failed to generate object"
}
// IsNoObjectGeneratedError checks if an error is of type NoObjectGeneratedError.
func IsNoObjectGeneratedError(err error) bool {
var target *NoObjectGeneratedError
return errors.As(err, &target)
}