diff --git a/.gitignore b/.gitignore index 640e5ec6a..133f59090 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ web/bun.lock electron/node_modules electron/dist data/ +.gomodcache/ \ No newline at end of file diff --git a/controller/channel-test.go b/controller/channel-test.go index 1c77fb030..f9657edbd 100644 --- a/controller/channel-test.go +++ b/controller/channel-test.go @@ -97,6 +97,11 @@ func testChannel(channel *model.Channel, testModel string, endpointType string) if channel.Type == constant.ChannelTypeVolcEngine && strings.Contains(testModel, "seedream") { requestPath = "/v1/images/generations" } + + // responses-only models + if strings.Contains(strings.ToLower(testModel), "codex") { + requestPath = "/v1/responses" + } } 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) @@ -319,6 +324,16 @@ func testChannel(channel *model.Channel, testModel string, endpointType string) httpResp = resp.(*http.Response) if httpResp.StatusCode != http.StatusOK { 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{ context: c, 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 != "" { switch constant.EndpointType(endpointType) { @@ -423,7 +438,7 @@ func buildTestRequest(model string, endpointType string) dto.Request { } case constant.EndpointTypeAnthropic, constant.EndpointTypeGemini, constant.EndpointTypeOpenAI: // 返回 GeneralOpenAIRequest - maxTokens := uint(10) + maxTokens := uint(16) if constant.EndpointType(endpointType) == constant.EndpointTypeGemini { 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 testRequest := &dto.GeneralOpenAIRequest{ Model: model, @@ -466,7 +489,7 @@ func buildTestRequest(model string, endpointType string) dto.Request { } if strings.HasPrefix(model, "o") { - testRequest.MaxCompletionTokens = 10 + testRequest.MaxCompletionTokens = 16 } else if strings.Contains(model, "thinking") { if !strings.Contains(model, "claude") { testRequest.MaxTokens = 50 @@ -474,7 +497,7 @@ func buildTestRequest(model string, endpointType string) dto.Request { } else if strings.Contains(model, "gemini") { testRequest.MaxTokens = 3000 } else { - testRequest.MaxTokens = 10 + testRequest.MaxTokens = 16 } return testRequest diff --git a/dto/error.go b/dto/error.go index cf00d6772..78197765b 100644 --- a/dto/error.go +++ b/dto/error.go @@ -26,6 +26,7 @@ type GeneralErrorResponse struct { Msg string `json:"msg"` Err string `json:"err"` ErrorMsg string `json:"error_msg"` + Metadata json.RawMessage `json:"metadata,omitempty"` Header struct { Message string `json:"message"` } `json:"header"` diff --git a/service/error.go b/service/error.go index 9e517e85a..889964beb 100644 --- a/service/error.go +++ b/service/error.go @@ -90,11 +90,17 @@ func RelayErrorHandler(ctx context.Context, resp *http.Response, showBodyWhenFai } CloseResponseBodyGracefully(resp) 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) if err != nil { if showBodyWhenFail { - newApiErr.Err = fmt.Errorf("bad response status code %d, body: %s", resp.StatusCode, string(responseBody)) + newApiErr.Err = buildErrWithBody("") } else { 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) @@ -107,10 +113,16 @@ func RelayErrorHandler(ctx context.Context, resp *http.Response, showBodyWhenFai oaiError := errResponse.TryToOpenAIError() if oaiError != nil { newApiErr = types.WithOpenAIError(*oaiError, resp.StatusCode) + if showBodyWhenFail { + newApiErr.Err = buildErrWithBody(newApiErr.Error()) + } return } } newApiErr = types.NewOpenAIError(errors.New(errResponse.ToMessage()), types.ErrorCodeBadResponseStatusCode, resp.StatusCode) + if showBodyWhenFail { + newApiErr.Err = buildErrWithBody(newApiErr.Error()) + } return } diff --git a/types/error.go b/types/error.go index 3bfd0399a..b060a9db6 100644 --- a/types/error.go +++ b/types/error.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "errors" "fmt" "net/http" @@ -10,10 +11,11 @@ import ( ) type OpenAIError struct { - Message string `json:"message"` - Type string `json:"type"` - Param string `json:"param"` - Code any `json:"code"` + Message string `json:"message"` + Type string `json:"type"` + Param string `json:"param"` + Code any `json:"code"` + Metadata json.RawMessage `json:"metadata,omitempty"` } type ClaudeError struct { @@ -92,6 +94,7 @@ type NewAPIError struct { errorType ErrorType errorCode ErrorCode StatusCode int + Metadata json.RawMessage } // 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), 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 { op(e) }