new-api/relay/channel/gemini/relay-gemini-native.go

98 lines
3.1 KiB
Go
Raw Normal View History

2025-05-26 13:34:41 +08:00
package gemini
import (
2026-01-25 14:52:18 +08:00
"fmt"
2025-05-26 13:34:41 +08:00
"io"
"net/http"
"github.com/QuantumNous/new-api/common"
2026-01-25 14:52:18 +08:00
"github.com/QuantumNous/new-api/constant"
"github.com/QuantumNous/new-api/dto"
"github.com/QuantumNous/new-api/logger"
relaycommon "github.com/QuantumNous/new-api/relay/common"
"github.com/QuantumNous/new-api/relay/helper"
"github.com/QuantumNous/new-api/service"
"github.com/QuantumNous/new-api/types"
2025-05-26 13:34:41 +08:00
"github.com/gin-gonic/gin"
)
func GeminiTextGenerationHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Response) (*dto.Usage, *types.NewAPIError) {
defer service.CloseResponseBodyGracefully(resp)
2025-05-26 13:34:41 +08:00
// 读取响应体
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
2025-07-29 15:20:08 +08:00
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
2025-05-26 13:34:41 +08:00
}
if common.DebugEnabled {
println(string(responseBody))
}
// 解析为 Gemini 原生响应格式
var geminiResponse dto.GeminiChatResponse
err = common.Unmarshal(responseBody, &geminiResponse)
2025-05-26 13:34:41 +08:00
if err != nil {
2025-07-29 15:20:08 +08:00
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
2025-05-26 13:34:41 +08:00
}
2026-01-25 14:52:18 +08:00
if len(geminiResponse.Candidates) == 0 && geminiResponse.PromptFeedback != nil && geminiResponse.PromptFeedback.BlockReason != nil {
common.SetContextKey(c, constant.ContextKeyAdminRejectReason, fmt.Sprintf("gemini_block_reason=%s", *geminiResponse.PromptFeedback.BlockReason))
}
2025-05-26 13:34:41 +08:00
// 计算使用量(基于 UsageMetadata
usage := buildUsageFromGeminiMetadata(geminiResponse.UsageMetadata, info.GetEstimatePromptTokens())
2025-06-07 12:26:23 +08:00
service.IOCopyBytesGracefully(c, resp, responseBody)
2025-05-26 13:34:41 +08:00
2025-05-26 14:50:50 +08:00
return &usage, nil
}
2025-08-09 00:27:33 +08:00
func NativeGeminiEmbeddingHandler(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (*dto.Usage, *types.NewAPIError) {
defer service.CloseResponseBodyGracefully(resp)
2025-08-09 00:27:33 +08:00
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
}
if common.DebugEnabled {
println(string(responseBody))
}
usage := service.ResponseText2Usage(c, "", info.UpstreamModelName, info.GetEstimatePromptTokens())
if info.IsGeminiBatchEmbedding {
2025-08-09 00:27:33 +08:00
var geminiResponse dto.GeminiBatchEmbeddingResponse
err = common.Unmarshal(responseBody, &geminiResponse)
if err != nil {
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
}
} else {
var geminiResponse dto.GeminiEmbeddingResponse
err = common.Unmarshal(responseBody, &geminiResponse)
if err != nil {
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
}
}
service.IOCopyBytesGracefully(c, resp, responseBody)
2025-08-09 00:27:33 +08:00
return usage, nil
}
func GeminiTextGenerationStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Response) (*dto.Usage, *types.NewAPIError) {
2025-05-26 14:50:50 +08:00
helper.SetEventStreamHeaders(c)
return geminiStreamHandler(c, info, resp, func(data string, geminiResponse *dto.GeminiChatResponse) bool {
err := helper.StringData(c, data)
2025-05-26 14:50:50 +08:00
if err != nil {
logger.LogError(c, "failed to write stream data: "+err.Error())
return false
2025-05-26 14:50:50 +08:00
}
info.SendResponseCount++
2025-05-26 14:50:50 +08:00
return true
})
2025-05-26 13:34:41 +08:00
}