Retain successful response read failures

This commit is contained in:
2026-11-05 13:46:56 +00:00
parent cd373ee46b
commit b8dc8a49a9
4 changed files with 122 additions and 5 deletions

View File

@@ -370,40 +370,69 @@ func tokenCount(value *int) int {
return *value
}
type responseErrorReader struct {
reader io.Reader
err error
}
func (r *responseErrorReader) Read(p []byte) (int, error) {
n, err := r.reader.Read(p)
if err != nil && !errors.Is(err, io.EOF) {
r.err = err
}
return n, err
}
func decodeOpenAIChatResponse(body io.Reader) (openAIChatResponse, error) {
observed := &responseErrorReader{reader: body}
limited := &io.LimitedReader{
R: body,
R: observed,
N: maxOpenAIChatResponseBytes + 1,
}
decoder := json.NewDecoder(limited)
var response openAIChatResponse
if err := decoder.Decode(&response); err != nil {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return openAIChatResponse{}, &requestFailedError{cause: err}
if errors.Is(observed.err, context.Canceled) || errors.Is(observed.err, context.DeadlineExceeded) {
return openAIChatResponse{}, &requestFailedError{cause: observed.err}
}
if limited.N == 0 {
return openAIChatResponse{}, openAIChatResponseTooLargeError()
}
if observed.err != nil {
return openAIChatResponse{}, &requestFailedError{cause: observed.err}
}
return openAIChatResponse{}, fmt.Errorf("%w: failed to decode response", ErrMalformedResponse)
}
if errors.Is(observed.err, context.Canceled) || errors.Is(observed.err, context.DeadlineExceeded) {
return openAIChatResponse{}, &requestFailedError{cause: observed.err}
}
if limited.N == 0 {
return openAIChatResponse{}, openAIChatResponseTooLargeError()
}
var trailing any
if err := decoder.Decode(&trailing); !errors.Is(err, io.EOF) {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return openAIChatResponse{}, &requestFailedError{cause: err}
if errors.Is(observed.err, context.Canceled) || errors.Is(observed.err, context.DeadlineExceeded) {
return openAIChatResponse{}, &requestFailedError{cause: observed.err}
}
if limited.N == 0 {
return openAIChatResponse{}, openAIChatResponseTooLargeError()
}
if observed.err != nil {
return openAIChatResponse{}, &requestFailedError{cause: observed.err}
}
return openAIChatResponse{}, fmt.Errorf("%w: response contains trailing data", ErrMalformedResponse)
}
if errors.Is(observed.err, context.Canceled) || errors.Is(observed.err, context.DeadlineExceeded) {
return openAIChatResponse{}, &requestFailedError{cause: observed.err}
}
if limited.N == 0 {
return openAIChatResponse{}, openAIChatResponseTooLargeError()
}
if observed.err != nil {
return openAIChatResponse{}, &requestFailedError{cause: observed.err}
}
return response, nil
}