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"
|
|
|
|
|
|
|
2025-10-11 15:30:09 +08:00
|
|
|
|
"github.com/QuantumNous/new-api/common"
|
2026-01-25 14:52:18 +08:00
|
|
|
|
"github.com/QuantumNous/new-api/constant"
|
2025-10-11 15:30:09 +08:00
|
|
|
|
"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"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-07-10 15:02:40 +08:00
|
|
|
|
func GeminiTextGenerationHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Response) (*dto.Usage, *types.NewAPIError) {
|
2025-08-14 20:05:06 +08:00
|
|
|
|
defer service.CloseResponseBodyGracefully(resp)
|
2025-06-28 00:02:07 +08:00
|
|
|
|
|
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 原生响应格式
|
2025-08-01 22:23:35 +08:00
|
|
|
|
var geminiResponse dto.GeminiChatResponse
|
2025-07-10 15:02:40 +08:00
|
|
|
|
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)
|
2026-02-17 15:45:14 +08:00
|
|
|
|
usage := buildUsageFromGeminiMetadata(geminiResponse.UsageMetadata, info.GetEstimatePromptTokens())
|
2025-06-07 12:26:23 +08:00
|
|
|
|
|
2025-08-14 20:05:06 +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) {
|
2025-08-14 20:05:06 +08:00
|
|
|
|
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))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-02 21:34:39 +08:00
|
|
|
|
usage := service.ResponseText2Usage(c, "", info.UpstreamModelName, info.GetEstimatePromptTokens())
|
2025-11-23 23:50:04 +08:00
|
|
|
|
|
2025-08-09 01:07:48 +08:00
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-14 20:05:06 +08:00
|
|
|
|
service.IOCopyBytesGracefully(c, resp, responseBody)
|
2025-08-09 00:27:33 +08:00
|
|
|
|
|
|
|
|
|
|
return usage, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-10 15:02:40 +08:00
|
|
|
|
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)
|
|
|
|
|
|
|
2025-11-21 18:16:40 +08:00
|
|
|
|
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 {
|
2025-12-13 13:29:21 +08:00
|
|
|
|
logger.LogError(c, "failed to write stream data: "+err.Error())
|
|
|
|
|
|
return false
|
2025-05-26 14:50:50 +08:00
|
|
|
|
}
|
2025-08-01 17:21:25 +08:00
|
|
|
|
info.SendResponseCount++
|
2025-05-26 14:50:50 +08:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
2025-05-26 13:34:41 +08:00
|
|
|
|
}
|