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}