error.go

 1package anthropic
 2
 3import (
 4	"cmp"
 5	"errors"
 6	"net/http"
 7	"regexp"
 8	"strconv"
 9	"strings"
10
11	"charm.land/fantasy"
12	"github.com/charmbracelet/anthropic-sdk-go"
13)
14
15var anthropicContextPattern = regexp.MustCompile(`prompt is too long:\s*(\d+)\s*tokens?\s*>\s*(\d+)\s*maximum`)
16
17func toProviderErr(err error) error {
18	var apiErr *anthropic.Error
19	if errors.As(err, &apiErr) {
20		providerErr := &fantasy.ProviderError{
21			Title:           cmp.Or(fantasy.ErrorTitleForStatusCode(apiErr.StatusCode), "provider request failed"),
22			Message:         apiErr.Error(),
23			Cause:           apiErr,
24			URL:             apiErr.Request.URL.String(),
25			StatusCode:      apiErr.StatusCode,
26			RequestBody:     apiErr.DumpRequest(true),
27			ResponseHeaders: toHeaderMap(apiErr.Response.Header),
28			ResponseBody:    apiErr.DumpResponse(true),
29		}
30
31		parseContextTooLargeError(apiErr.Error(), providerErr)
32
33		return providerErr
34	}
35	return err
36}
37
38func parseContextTooLargeError(message string, providerErr *fantasy.ProviderError) {
39	matches := anthropicContextPattern.FindStringSubmatch(message)
40	if matches == nil {
41		return
42	}
43
44	providerErr.ContextTooLargeErr = true
45	providerErr.ContextUsedTokens, _ = strconv.Atoi(matches[1])
46	providerErr.ContextMaxTokens, _ = strconv.Atoi(matches[2])
47}
48
49func toHeaderMap(in http.Header) (out map[string]string) {
50	out = make(map[string]string, len(in))
51	for k, v := range in {
52		if l := len(v); l > 0 {
53			out[k] = v[l-1]
54			in[strings.ToLower(k)] = v
55		}
56	}
57	return out
58}