fix: Fix Openrouter test errors and optimize error messages (#2433)

* fix: Refine openrouter error

* fix: Refine openrouter error

* fix: openrouter test max_output_token

* fix: optimize messages

* fix: maxToken unified to 16

* fix: codex系列模型使用 responses接口

* fix: codex系列模型使用 responses接口

* fix: 状态码非200打印错误信息

* fix: 日志里没有报错的响应体
This commit is contained in:
Seefs
2025-12-26 13:58:44 +08:00
committed by GitHub
parent 654bb10b45
commit 58db72d459
5 changed files with 57 additions and 10 deletions

1
.gitignore vendored
View File

@@ -23,3 +23,4 @@ web/bun.lock
electron/node_modules electron/node_modules
electron/dist electron/dist
data/ data/
.gomodcache/

View File

@@ -97,6 +97,11 @@ func testChannel(channel *model.Channel, testModel string, endpointType string)
if channel.Type == constant.ChannelTypeVolcEngine && strings.Contains(testModel, "seedream") { if channel.Type == constant.ChannelTypeVolcEngine && strings.Contains(testModel, "seedream") {
requestPath = "/v1/images/generations" requestPath = "/v1/images/generations"
} }
// responses-only models
if strings.Contains(strings.ToLower(testModel), "codex") {
requestPath = "/v1/responses"
}
} }
c.Request = &http.Request{ c.Request = &http.Request{
@@ -176,7 +181,7 @@ func testChannel(channel *model.Channel, testModel string, endpointType string)
} }
} }
request := buildTestRequest(testModel, endpointType) request := buildTestRequest(testModel, endpointType, channel)
info, err := relaycommon.GenRelayInfo(c, relayFormat, request, nil) info, err := relaycommon.GenRelayInfo(c, relayFormat, request, nil)
@@ -319,6 +324,16 @@ func testChannel(channel *model.Channel, testModel string, endpointType string)
httpResp = resp.(*http.Response) httpResp = resp.(*http.Response)
if httpResp.StatusCode != http.StatusOK { if httpResp.StatusCode != http.StatusOK {
err := service.RelayErrorHandler(c.Request.Context(), httpResp, true) err := service.RelayErrorHandler(c.Request.Context(), httpResp, true)
common.SysError(fmt.Sprintf(
"channel test bad response: channel_id=%d name=%s type=%d model=%s endpoint_type=%s status=%d err=%v",
channel.Id,
channel.Name,
channel.Type,
testModel,
endpointType,
httpResp.StatusCode,
err,
))
return testResult{ return testResult{
context: c, context: c,
localErr: err, localErr: err,
@@ -389,7 +404,7 @@ func testChannel(channel *model.Channel, testModel string, endpointType string)
} }
} }
func buildTestRequest(model string, endpointType string) dto.Request { func buildTestRequest(model string, endpointType string, channel *model.Channel) dto.Request {
// 根据端点类型构建不同的测试请求 // 根据端点类型构建不同的测试请求
if endpointType != "" { if endpointType != "" {
switch constant.EndpointType(endpointType) { switch constant.EndpointType(endpointType) {
@@ -423,7 +438,7 @@ func buildTestRequest(model string, endpointType string) dto.Request {
} }
case constant.EndpointTypeAnthropic, constant.EndpointTypeGemini, constant.EndpointTypeOpenAI: case constant.EndpointTypeAnthropic, constant.EndpointTypeGemini, constant.EndpointTypeOpenAI:
// 返回 GeneralOpenAIRequest // 返回 GeneralOpenAIRequest
maxTokens := uint(10) maxTokens := uint(16)
if constant.EndpointType(endpointType) == constant.EndpointTypeGemini { if constant.EndpointType(endpointType) == constant.EndpointTypeGemini {
maxTokens = 3000 maxTokens = 3000
} }
@@ -453,6 +468,14 @@ func buildTestRequest(model string, endpointType string) dto.Request {
} }
} }
// Responses-only models (e.g. codex series)
if strings.Contains(strings.ToLower(model), "codex") {
return &dto.OpenAIResponsesRequest{
Model: model,
Input: json.RawMessage("\"hi\""),
}
}
// Chat/Completion 请求 - 返回 GeneralOpenAIRequest // Chat/Completion 请求 - 返回 GeneralOpenAIRequest
testRequest := &dto.GeneralOpenAIRequest{ testRequest := &dto.GeneralOpenAIRequest{
Model: model, Model: model,
@@ -466,7 +489,7 @@ func buildTestRequest(model string, endpointType string) dto.Request {
} }
if strings.HasPrefix(model, "o") { if strings.HasPrefix(model, "o") {
testRequest.MaxCompletionTokens = 10 testRequest.MaxCompletionTokens = 16
} else if strings.Contains(model, "thinking") { } else if strings.Contains(model, "thinking") {
if !strings.Contains(model, "claude") { if !strings.Contains(model, "claude") {
testRequest.MaxTokens = 50 testRequest.MaxTokens = 50
@@ -474,7 +497,7 @@ func buildTestRequest(model string, endpointType string) dto.Request {
} else if strings.Contains(model, "gemini") { } else if strings.Contains(model, "gemini") {
testRequest.MaxTokens = 3000 testRequest.MaxTokens = 3000
} else { } else {
testRequest.MaxTokens = 10 testRequest.MaxTokens = 16
} }
return testRequest return testRequest

View File

@@ -26,6 +26,7 @@ type GeneralErrorResponse struct {
Msg string `json:"msg"` Msg string `json:"msg"`
Err string `json:"err"` Err string `json:"err"`
ErrorMsg string `json:"error_msg"` ErrorMsg string `json:"error_msg"`
Metadata json.RawMessage `json:"metadata,omitempty"`
Header struct { Header struct {
Message string `json:"message"` Message string `json:"message"`
} `json:"header"` } `json:"header"`

View File

@@ -90,11 +90,17 @@ func RelayErrorHandler(ctx context.Context, resp *http.Response, showBodyWhenFai
} }
CloseResponseBodyGracefully(resp) CloseResponseBodyGracefully(resp)
var errResponse dto.GeneralErrorResponse var errResponse dto.GeneralErrorResponse
buildErrWithBody := func(message string) error {
if message == "" {
return fmt.Errorf("bad response status code %d, body: %s", resp.StatusCode, string(responseBody))
}
return fmt.Errorf("bad response status code %d, message: %s, body: %s", resp.StatusCode, message, string(responseBody))
}
err = common.Unmarshal(responseBody, &errResponse) err = common.Unmarshal(responseBody, &errResponse)
if err != nil { if err != nil {
if showBodyWhenFail { if showBodyWhenFail {
newApiErr.Err = fmt.Errorf("bad response status code %d, body: %s", resp.StatusCode, string(responseBody)) newApiErr.Err = buildErrWithBody("")
} else { } else {
logger.LogError(ctx, fmt.Sprintf("bad response status code %d, body: %s", resp.StatusCode, string(responseBody))) logger.LogError(ctx, fmt.Sprintf("bad response status code %d, body: %s", resp.StatusCode, string(responseBody)))
newApiErr.Err = fmt.Errorf("bad response status code %d", resp.StatusCode) newApiErr.Err = fmt.Errorf("bad response status code %d", resp.StatusCode)
@@ -107,10 +113,16 @@ func RelayErrorHandler(ctx context.Context, resp *http.Response, showBodyWhenFai
oaiError := errResponse.TryToOpenAIError() oaiError := errResponse.TryToOpenAIError()
if oaiError != nil { if oaiError != nil {
newApiErr = types.WithOpenAIError(*oaiError, resp.StatusCode) newApiErr = types.WithOpenAIError(*oaiError, resp.StatusCode)
if showBodyWhenFail {
newApiErr.Err = buildErrWithBody(newApiErr.Error())
}
return return
} }
} }
newApiErr = types.NewOpenAIError(errors.New(errResponse.ToMessage()), types.ErrorCodeBadResponseStatusCode, resp.StatusCode) newApiErr = types.NewOpenAIError(errors.New(errResponse.ToMessage()), types.ErrorCodeBadResponseStatusCode, resp.StatusCode)
if showBodyWhenFail {
newApiErr.Err = buildErrWithBody(newApiErr.Error())
}
return return
} }

View File

@@ -1,6 +1,7 @@
package types package types
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
@@ -10,10 +11,11 @@ import (
) )
type OpenAIError struct { type OpenAIError struct {
Message string `json:"message"` Message string `json:"message"`
Type string `json:"type"` Type string `json:"type"`
Param string `json:"param"` Param string `json:"param"`
Code any `json:"code"` Code any `json:"code"`
Metadata json.RawMessage `json:"metadata,omitempty"`
} }
type ClaudeError struct { type ClaudeError struct {
@@ -92,6 +94,7 @@ type NewAPIError struct {
errorType ErrorType errorType ErrorType
errorCode ErrorCode errorCode ErrorCode
StatusCode int StatusCode int
Metadata json.RawMessage
} }
// Unwrap enables errors.Is / errors.As to work with NewAPIError by exposing the underlying error. // Unwrap enables errors.Is / errors.As to work with NewAPIError by exposing the underlying error.
@@ -301,6 +304,13 @@ func WithOpenAIError(openAIError OpenAIError, statusCode int, ops ...NewAPIError
Err: errors.New(openAIError.Message), Err: errors.New(openAIError.Message),
errorCode: ErrorCode(code), errorCode: ErrorCode(code),
} }
// OpenRouter
if len(openAIError.Metadata) > 0 {
openAIError.Message = fmt.Sprintf("%s (%s)", openAIError.Message, openAIError.Metadata)
e.Metadata = openAIError.Metadata
e.RelayError = openAIError
e.Err = errors.New(openAIError.Message)
}
for _, op := range ops { for _, op := range ops {
op(e) op(e)
} }