60 lines
1.9 KiB
Go
60 lines
1.9 KiB
Go
package service
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
// OpenAIUpstreamHTTP2StreamErrorCode is returned to OpenAI-compatible clients
|
|
// when an upstream HTTP/2 response stream is reset after the request started.
|
|
OpenAIUpstreamHTTP2StreamErrorCode = "upstream_http2_stream_error"
|
|
OpenAIUpstreamStreamReadErrorCode = "upstream_stream_read_error"
|
|
)
|
|
|
|
type openAIUpstreamStreamReadError struct {
|
|
cause error
|
|
clientCode string
|
|
clientMessage string
|
|
}
|
|
|
|
func (e *openAIUpstreamStreamReadError) Error() string {
|
|
return fmt.Sprintf("stream usage incomplete: %v", e.cause)
|
|
}
|
|
|
|
func (e *openAIUpstreamStreamReadError) Unwrap() error { return e.cause }
|
|
|
|
func newOpenAIUpstreamStreamReadError(err error) error {
|
|
code, message := classifyOpenAIUpstreamStreamReadError(err)
|
|
return &openAIUpstreamStreamReadError{
|
|
cause: err,
|
|
clientCode: code,
|
|
clientMessage: message,
|
|
}
|
|
}
|
|
|
|
// OpenAIUpstreamStreamReadErrorDetails returns the stable, sanitized client
|
|
// classification attached to an upstream stream read failure.
|
|
func OpenAIUpstreamStreamReadErrorDetails(err error) (code, message string, ok bool) {
|
|
var streamErr *openAIUpstreamStreamReadError
|
|
if !errors.As(err, &streamErr) || streamErr == nil {
|
|
return "", "", false
|
|
}
|
|
return streamErr.clientCode, streamErr.clientMessage, true
|
|
}
|
|
|
|
func classifyOpenAIUpstreamStreamReadError(err error) (code, message string) {
|
|
if err != nil {
|
|
lower := strings.ToLower(err.Error())
|
|
// net/http's HTTP/2 stream error is unexported. Its stable text contains
|
|
// "stream error: stream ID ..."; match only the transport signature and
|
|
// never pass the original text to the client.
|
|
if strings.Contains(lower, "stream error: stream id ") ||
|
|
(strings.Contains(lower, "http2:") && strings.Contains(lower, "stream")) {
|
|
return OpenAIUpstreamHTTP2StreamErrorCode, "Upstream HTTP/2 stream failed"
|
|
}
|
|
}
|
|
return OpenAIUpstreamStreamReadErrorCode, "Upstream response stream was interrupted"
|
|
}
|