package executor import ( "bytes" "fmt" "net/http" "strings" cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth" cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor") // claudeFastRequestError marks a Fast request failure as request-scoped. Fast// errors must stop at the caller: they do not justify retrying another// credential or changing the selected credential's availability.type claudeFastRequestError struct { cause error status int} func (e *claudeFastRequestError) Error() string { if e == nil || e.cause == nil { return "" } return e.cause.Error()} func (e *claudeFastRequestError) Unwrap() error { if e == nil { return nil } return e.cause} func (e *claudeFastRequestError) StatusCode() int { if e == nil || (e.status >= http.StatusOK && e.status < http.StatusMultipleChoices) { return 0 } return e.status} func (e *claudeFastRequestError) IsRequestScoped() bool { return e != nil} // claudeFastDirectResponseError carries an upstream HTTP error response through// the auth manager and protocol handlers without retrying or rebuilding its// status and JSON body.type claudeFastDirectResponseError struct { response *cliproxyexecutor.RequestTerminatedError} func (e *claudeFastDirectResponseError) Error() string { if e == nil || e.response == nil { return "" } return fmt.Sprintf("claude Fast upstream request failed with status %d", e.response.HTTPStatus)} func (e *claudeFastDirectResponseError) Unwrap() error { if e == nil { return nil } return e.response} func (e *claudeFastDirectResponseError) IsRequestScoped() bool { return e != nil} func wrapClaudeFastRequestError(fastRequest bool, status int, err error) error { if err == nil || !fastRequest { return err } return &claudeFastRequestError{cause: err, status: status}} func newClaudeFastDirectResponseError(resp *http.Response, body []byte) error { if resp == nil { return nil } headers := resp.Header.Clone() // body has already been decoded. Do not forward stale representation or // length headers that describe the compressed upstream bytes. headers.Del("Content-Encoding") headers.Del("Content-Length") return &claudeFastDirectResponseError{response: &cliproxyexecutor.RequestTerminatedError{ HTTPStatus: resp.StatusCode, Header: headers, Body: bytes.Clone(body), }}} func claudeRequestIsFast(req *http.Request, body []byte) bool { if req == nil { return false } betas := strings.Join(req.Header.Values("Anthropic-Beta"), ",") return claudeRequestUsesFastMode(body, claudeRequestedBetas(betas, nil))} func claudeAuthLogIdentity(auth *cliproxyauth.Auth) (id, label, authType, authValue string) { if auth == nil { return "", "", "", "" } authType, authValue = auth.AccountInfo() return auth.ID, auth.Label, authType, authValue}
package executor
import (
"bytes"
"fmt"
"net/http"
"strings"
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
)
// claudeFastRequestError marks a Fast request failure as request-scoped. Fast
// errors must stop at the caller: they do not justify retrying another
// credential or changing the selected credential's availability.
type claudeFastRequestError struct {
cause error
status int
}
func (e *claudeFastRequestError) Error() string {
if e == nil || e.cause == nil {
return ""
}
return e.cause.Error()
}
func (e *claudeFastRequestError) Unwrap() error {
if e == nil {
return nil
}
return e.cause
}
func (e *claudeFastRequestError) StatusCode() int {
if e == nil || (e.status >= http.StatusOK && e.status < http.StatusMultipleChoices) {
return 0
}
return e.status
}
func (e *claudeFastRequestError) IsRequestScoped() bool {
return e != nil
}
// claudeFastDirectResponseError carries an upstream HTTP error response through
// the auth manager and protocol handlers without retrying or rebuilding its
// status and JSON body.
type claudeFastDirectResponseError struct {
response *cliproxyexecutor.RequestTerminatedError
}
func (e *claudeFastDirectResponseError) Error() string {
if e == nil || e.response == nil {
return ""
}
return fmt.Sprintf("claude Fast upstream request failed with status %d", e.response.HTTPStatus)
}
func (e *claudeFastDirectResponseError) Unwrap() error {
if e == nil {
return nil
}
return e.response
}
func (e *claudeFastDirectResponseError) IsRequestScoped() bool {
return e != nil
}
func wrapClaudeFastRequestError(fastRequest bool, status int, err error) error {
if err == nil || !fastRequest {
return err
}
return &claudeFastRequestError{cause: err, status: status}
}
func newClaudeFastDirectResponseError(resp *http.Response, body []byte) error {
if resp == nil {
return nil
}
headers := resp.Header.Clone()
// body has already been decoded. Do not forward stale representation or
// length headers that describe the compressed upstream bytes.
headers.Del("Content-Encoding")
headers.Del("Content-Length")
return &claudeFastDirectResponseError{response: &cliproxyexecutor.RequestTerminatedError{
HTTPStatus: resp.StatusCode,
Header: headers,
Body: bytes.Clone(body),
}}
}
func claudeRequestIsFast(req *http.Request, body []byte) bool {
if req == nil {
return false
}
betas := strings.Join(req.Header.Values("Anthropic-Beta"), ",")
return claudeRequestUsesFastMode(body, claudeRequestedBetas(betas, nil))
}
func claudeAuthLogIdentity(auth *cliproxyauth.Auth) (id, label, authType, authValue string) {
if auth == nil {
return "", "", "", ""
}
authType, authValue = auth.AccountInfo()
return auth.ID, auth.Label, authType, authValue
}