feat: Introduce JSON decoding utility functions and update error handling in Claude and OpenAI response structures

This commit is contained in:
1808837298@qq.com
2025-03-16 18:34:39 +08:00
parent a4a40c495d
commit b3b1c803fc
6 changed files with 39 additions and 14 deletions

14
common/json.go Normal file
View File

@@ -0,0 +1,14 @@
package common
import (
"bytes"
"encoding/json"
)
func DecodeJson(data []byte, v any) error {
return json.NewDecoder(bytes.NewReader(data)).Decode(v)
}
func DecodeJsonStr(data string, v any) error {
return DecodeJson(StringToByteSlice(data), v)
}

View File

@@ -165,8 +165,8 @@ func (c *ClaudeRequest) ParseSystem() []ClaudeMediaMessage {
} }
type ClaudeError struct { type ClaudeError struct {
Type string `json:"type"` Type string `json:"type,omitempty"`
Message string `json:"message"` Message string `json:"message,omitempty"`
} }
type ClaudeErrorWithStatusCode struct { type ClaudeErrorWithStatusCode struct {

View File

@@ -1,9 +1,8 @@
package dto package dto
type SimpleResponse struct { type SimpleResponse struct {
Usage `json:"usage"` Usage `json:"usage"`
Error OpenAIError `json:"error"` Error *OpenAIError `json:"error"`
Choices []OpenAITextResponseChoice `json:"choices"`
} }
type TextResponse struct { type TextResponse struct {
@@ -27,6 +26,7 @@ type OpenAITextResponse struct {
Object string `json:"object"` Object string `json:"object"`
Created int64 `json:"created"` Created int64 `json:"created"`
Choices []OpenAITextResponseChoice `json:"choices"` Choices []OpenAITextResponseChoice `json:"choices"`
Error *OpenAIError `json:"error"`
Usage `json:"usage"` Usage `json:"usage"`
} }

View File

@@ -1,7 +1,6 @@
package claude package claude
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@@ -481,7 +480,7 @@ func FormatClaudeResponseInfo(requestMode int, claudeResponse *dto.ClaudeRespons
func HandleResponseData(c *gin.Context, info *relaycommon.RelayInfo, claudeInfo *ClaudeResponseInfo, data string, requestMode int) error { func HandleResponseData(c *gin.Context, info *relaycommon.RelayInfo, claudeInfo *ClaudeResponseInfo, data string, requestMode int) error {
var claudeResponse dto.ClaudeResponse var claudeResponse dto.ClaudeResponse
err := json.NewDecoder(bytes.NewReader(common.StringToByteSlice(data))).Decode(&claudeResponse) err := common.DecodeJsonStr(data, &claudeResponse)
if err != nil { if err != nil {
common.SysError("error unmarshalling stream response: " + err.Error()) common.SysError("error unmarshalling stream response: " + err.Error())
return fmt.Errorf("error unmarshalling stream aws response: %w", err) return fmt.Errorf("error unmarshalling stream aws response: %w", err)

View File

@@ -33,7 +33,7 @@ func sendStreamData(c *gin.Context, info *relaycommon.RelayInfo, data string, fo
} }
var lastStreamResponse dto.ChatCompletionsStreamResponse var lastStreamResponse dto.ChatCompletionsStreamResponse
if err := json.Unmarshal(common.StringToByteSlice(data), &lastStreamResponse); err != nil { if err := common.DecodeJsonStr(data, &lastStreamResponse); err != nil {
return err return err
} }
@@ -151,7 +151,7 @@ func OaiStreamHandler(c *gin.Context, resp *http.Response, info *relaycommon.Rel
shouldSendLastResp := true shouldSendLastResp := true
var lastStreamResponse dto.ChatCompletionsStreamResponse var lastStreamResponse dto.ChatCompletionsStreamResponse
err := json.Unmarshal(common.StringToByteSlice(lastStreamData), &lastStreamResponse) err := common.DecodeJsonStr(lastStreamData, &lastStreamResponse)
if err == nil { if err == nil {
responseId = lastStreamResponse.Id responseId = lastStreamResponse.Id
createAt = lastStreamResponse.Created createAt = lastStreamResponse.Created
@@ -196,7 +196,7 @@ func OaiStreamHandler(c *gin.Context, resp *http.Response, info *relaycommon.Rel
} }
func OpenaiHandler(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (*dto.OpenAIErrorWithStatusCode, *dto.Usage) { func OpenaiHandler(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (*dto.OpenAIErrorWithStatusCode, *dto.Usage) {
var simpleResponse dto.SimpleResponse var simpleResponse dto.OpenAITextResponse
responseBody, err := io.ReadAll(resp.Body) responseBody, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return service.OpenAIErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError), nil return service.OpenAIErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError), nil
@@ -205,16 +205,29 @@ func OpenaiHandler(c *gin.Context, resp *http.Response, info *relaycommon.RelayI
if err != nil { if err != nil {
return service.OpenAIErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil return service.OpenAIErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
} }
err = json.Unmarshal(responseBody, &simpleResponse) err = common.DecodeJson(responseBody, &simpleResponse)
if err != nil { if err != nil {
return service.OpenAIErrorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError), nil return service.OpenAIErrorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError), nil
} }
if simpleResponse.Error.Type != "" { if simpleResponse.Error != nil && simpleResponse.Error.Type != "" {
return &dto.OpenAIErrorWithStatusCode{ return &dto.OpenAIErrorWithStatusCode{
Error: simpleResponse.Error, Error: *simpleResponse.Error,
StatusCode: resp.StatusCode, StatusCode: resp.StatusCode,
}, nil }, nil
} }
switch info.RelayFormat {
case relaycommon.RelayFormatOpenAI:
break
case relaycommon.RelayFormatClaude:
claudeResp := service.ResponseOpenAI2Claude(&simpleResponse, info)
claudeRespStr, err := json.Marshal(claudeResp)
if err != nil {
return service.OpenAIErrorWrapper(err, "marshal_response_body_failed", http.StatusInternalServerError), nil
}
responseBody = claudeRespStr
}
// Reset response body // Reset response body
resp.Body = io.NopCloser(bytes.NewBuffer(responseBody)) resp.Body = io.NopCloser(bytes.NewBuffer(responseBody))
// We shouldn't set the header before we parse the response body, because the parse part may fail. // We shouldn't set the header before we parse the response body, because the parse part may fail.

View File

@@ -60,7 +60,6 @@ func ClaudeErrorWrapper(err error, code string, statusCode int) *dto.ClaudeError
claudeError := dto.ClaudeError{ claudeError := dto.ClaudeError{
Message: text, Message: text,
Type: "new_api_error", Type: "new_api_error",
//Code: code,
} }
return &dto.ClaudeErrorWithStatusCode{ return &dto.ClaudeErrorWithStatusCode{
Error: claudeError, Error: claudeError,