error.go

 1package openai
 2
 3import (
 4	"cmp"
 5	"errors"
 6	"io"
 7	"net/http"
 8	"strings"
 9
10	"charm.land/fantasy"
11	"github.com/openai/openai-go/v2"
12)
13
14func toProviderErr(err error) error {
15	var apiErr *openai.Error
16	if errors.As(err, &apiErr) {
17		return &fantasy.ProviderError{
18			Title:           cmp.Or(fantasy.ErrorTitleForStatusCode(apiErr.StatusCode), "provider request failed"),
19			Message:         toProviderErrMessage(apiErr),
20			Cause:           apiErr,
21			URL:             apiErr.Request.URL.String(),
22			StatusCode:      apiErr.StatusCode,
23			RequestBody:     apiErr.DumpRequest(true),
24			ResponseHeaders: toHeaderMap(apiErr.Response.Header),
25			ResponseBody:    apiErr.DumpResponse(true),
26		}
27	}
28	return err
29}
30
31func toProviderErrMessage(apiErr *openai.Error) string {
32	if apiErr.Message != "" {
33		return apiErr.Message
34	}
35
36	// For some OpenAI-compatible providers, the SDK is not always able to parse
37	// the error message correctly.
38	// Fallback to returning the raw response body in such cases.
39	data, _ := io.ReadAll(apiErr.Response.Body)
40	return string(data)
41}
42
43func toHeaderMap(in http.Header) (out map[string]string) {
44	out = make(map[string]string, len(in))
45	for k, v := range in {
46		if l := len(v); l > 0 {
47			out[k] = v[l-1]
48			in[strings.ToLower(k)] = v
49		}
50	}
51	return out
52}