Skip to content

Commit b0b8035

Browse files
committed
fix: siliconflow error handloer
1 parent 316b24f commit b0b8035

2 files changed

Lines changed: 217 additions & 12 deletions

File tree

Lines changed: 123 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,155 @@
11
package siliconflow
22

33
import (
4+
"bytes"
5+
"encoding/json"
6+
"io"
47
"net/http"
58
"strconv"
69
"strings"
710

11+
"github.com/bytedance/sonic"
812
"github.com/labring/aiproxy/core/common"
913
"github.com/labring/aiproxy/core/relay/adaptor"
1014
relaymodel "github.com/labring/aiproxy/core/relay/model"
1115
)
1216

1317
type errorResponse struct {
14-
Message string `json:"message"`
15-
Code int `json:"code"`
18+
Message string `json:"message"`
19+
Code any `json:"code"`
20+
Error json.RawMessage `json:"error"`
1621
}
1722

1823
func ErrorHandler(resp *http.Response) adaptor.Error {
1924
defer resp.Body.Close()
2025

21-
var er errorResponse
22-
23-
err := common.UnmarshalResponse(resp, &er)
26+
responseBody, err := common.GetResponseBody(resp)
2427
if err != nil {
2528
return relaymodel.WrapperOpenAIErrorWithMessage(
2629
err.Error(),
27-
"unmarshal_response_body_failed",
30+
"read_response_body_failed",
2831
http.StatusInternalServerError,
2932
)
3033
}
3134

32-
statusCode := resp.StatusCode
33-
34-
if strings.Contains(er.Message, "System is really busy") {
35-
statusCode = http.StatusTooManyRequests
35+
message, code, parseErr := parseErrorResponse(responseBody)
36+
if parseErr != nil {
37+
return relaymodel.WrapperOpenAIErrorWithMessage(
38+
parseErr.Error(),
39+
"unmarshal_response_body_failed",
40+
http.StatusInternalServerError,
41+
)
3642
}
3743

44+
statusCode, code := normalizeError(resp.StatusCode, message, code)
45+
3846
return relaymodel.WrapperOpenAIErrorWithMessage(
39-
er.Message,
40-
strconv.Itoa(er.Code),
47+
message,
48+
code,
4149
statusCode,
4250
relaymodel.ErrorTypeUpstream,
4351
)
4452
}
53+
54+
func normalizeError(statusCode int, message string, code any) (int, any) {
55+
if strings.Contains(message, "System is really busy") {
56+
statusCode = http.StatusTooManyRequests
57+
}
58+
59+
if isUnauthorizedMessage(message) {
60+
statusCode = http.StatusUnauthorized
61+
}
62+
63+
if code == nil {
64+
code = relaymodel.ErrorCodeBadResponse
65+
}
66+
67+
return statusCode, code
68+
}
69+
70+
func isUnauthorizedMessage(message string) bool {
71+
lowerMessage := strings.ToLower(message)
72+
73+
return strings.Contains(lowerMessage, "api key is invalid") ||
74+
strings.Contains(lowerMessage, "invalid api key") ||
75+
strings.Contains(lowerMessage, "api key provided is invalid") ||
76+
strings.Contains(lowerMessage, "unauthorized")
77+
}
78+
79+
func parseErrorResponse(body []byte) (string, any, error) {
80+
body = bytes.TrimSpace(body)
81+
if len(body) == 0 {
82+
return "", nil, io.EOF
83+
}
84+
85+
var er errorResponse
86+
if err := sonic.Unmarshal(body, &er); err == nil {
87+
if nestedMessage, nestedCode := parseNestedError(er.Error); nestedMessage != "" {
88+
if er.Message == "" {
89+
er.Message = nestedMessage
90+
}
91+
92+
if er.Code == nil {
93+
er.Code = nestedCode
94+
}
95+
}
96+
97+
if er.Message != "" {
98+
return er.Message, normalizeErrorCode(er.Code), nil
99+
}
100+
}
101+
102+
var stringMessage string
103+
if err := sonic.Unmarshal(body, &stringMessage); err == nil && stringMessage != "" {
104+
return stringMessage, nil, nil
105+
}
106+
107+
return string(body), nil, nil
108+
}
109+
110+
func parseNestedError(raw json.RawMessage) (string, any) {
111+
if len(raw) == 0 {
112+
return "", nil
113+
}
114+
115+
var message string
116+
if err := sonic.Unmarshal([]byte(raw), &message); err == nil {
117+
return message, nil
118+
}
119+
120+
var nested struct {
121+
Message string `json:"message"`
122+
Code any `json:"code"`
123+
Error string `json:"error"`
124+
}
125+
126+
if err := sonic.Unmarshal([]byte(raw), &nested); err == nil {
127+
if nested.Message != "" {
128+
return nested.Message, nested.Code
129+
}
130+
131+
if nested.Error != "" {
132+
return nested.Error, nested.Code
133+
}
134+
}
135+
136+
return "", nil
137+
}
138+
139+
func normalizeErrorCode(code any) any {
140+
switch value := code.(type) {
141+
case nil:
142+
return nil
143+
case float64:
144+
if value == float64(int64(value)) {
145+
return strconv.FormatInt(int64(value), 10)
146+
}
147+
return value
148+
case int:
149+
return strconv.Itoa(value)
150+
case int64:
151+
return strconv.FormatInt(value, 10)
152+
default:
153+
return value
154+
}
155+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//nolint:testpackage
2+
package siliconflow
3+
4+
import (
5+
"encoding/json"
6+
"io"
7+
"net/http"
8+
"strings"
9+
"testing"
10+
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestErrorHandlerParsesObjectError(t *testing.T) {
15+
resp := &http.Response{
16+
StatusCode: http.StatusTooManyRequests,
17+
Body: io.NopCloser(strings.NewReader(
18+
`{"message":"System is really busy, please try again later","code":20015}`,
19+
)),
20+
}
21+
22+
err := ErrorHandler(resp)
23+
require.Equal(t, http.StatusTooManyRequests, err.StatusCode())
24+
25+
data, marshalErr := err.MarshalJSON()
26+
require.NoError(t, marshalErr)
27+
28+
var body struct {
29+
Error struct {
30+
Code string `json:"code"`
31+
Message string `json:"message"`
32+
Type string `json:"type"`
33+
} `json:"error"`
34+
}
35+
36+
require.NoError(t, json.Unmarshal(data, &body))
37+
require.Equal(t, "20015", body.Error.Code)
38+
require.Equal(t, "System is really busy, please try again later", body.Error.Message)
39+
require.Equal(t, "upstream_error", body.Error.Type)
40+
}
41+
42+
func TestErrorHandlerParsesJSONStringError(t *testing.T) {
43+
resp := &http.Response{
44+
StatusCode: http.StatusBadRequest,
45+
Body: io.NopCloser(strings.NewReader(`"Api key is invalid"`)),
46+
}
47+
48+
err := ErrorHandler(resp)
49+
require.Equal(t, http.StatusUnauthorized, err.StatusCode())
50+
51+
data, marshalErr := err.MarshalJSON()
52+
require.NoError(t, marshalErr)
53+
54+
var body struct {
55+
Error struct {
56+
Code string `json:"code"`
57+
Message string `json:"message"`
58+
Type string `json:"type"`
59+
} `json:"error"`
60+
}
61+
62+
require.NoError(t, json.Unmarshal(data, &body))
63+
require.Equal(t, "bad_response", body.Error.Code)
64+
require.Equal(t, "Api key is invalid", body.Error.Message)
65+
require.Equal(t, "upstream_error", body.Error.Type)
66+
}
67+
68+
func TestErrorHandlerParsesNestedErrorObject(t *testing.T) {
69+
resp := &http.Response{
70+
StatusCode: http.StatusBadRequest,
71+
Body: io.NopCloser(strings.NewReader(
72+
`{"error":{"message":"Api key is invalid","code":"invalid_api_key"}}`,
73+
)),
74+
}
75+
76+
err := ErrorHandler(resp)
77+
require.Equal(t, http.StatusUnauthorized, err.StatusCode())
78+
79+
data, marshalErr := err.MarshalJSON()
80+
require.NoError(t, marshalErr)
81+
82+
var body struct {
83+
Error struct {
84+
Code string `json:"code"`
85+
Message string `json:"message"`
86+
Type string `json:"type"`
87+
} `json:"error"`
88+
}
89+
90+
require.NoError(t, json.Unmarshal(data, &body))
91+
require.Equal(t, "invalid_api_key", body.Error.Code)
92+
require.Equal(t, "Api key is invalid", body.Error.Message)
93+
require.Equal(t, "upstream_error", body.Error.Type)
94+
}

0 commit comments

Comments
 (0)