|
|
@@ -81,7 +81,7 @@ func clampThinkingBudget(modelName string, budget int) int {
|
|
|
return budget
|
|
|
}
|
|
|
|
|
|
-func ThinkingAdaptor(geminiRequest *GeminiChatRequest, info *relaycommon.RelayInfo) {
|
|
|
+func ThinkingAdaptor(geminiRequest *dto.GeminiChatRequest, info *relaycommon.RelayInfo) {
|
|
|
if model_setting.GetGeminiSettings().ThinkingAdapterEnabled {
|
|
|
modelName := info.UpstreamModelName
|
|
|
isNew25Pro := strings.HasPrefix(modelName, "gemini-2.5-pro") &&
|
|
|
@@ -93,7 +93,7 @@ func ThinkingAdaptor(geminiRequest *GeminiChatRequest, info *relaycommon.RelayIn
|
|
|
if len(parts) == 2 && parts[1] != "" {
|
|
|
if budgetTokens, err := strconv.Atoi(parts[1]); err == nil {
|
|
|
clampedBudget := clampThinkingBudget(modelName, budgetTokens)
|
|
|
- geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{
|
|
|
+ geminiRequest.GenerationConfig.ThinkingConfig = &dto.GeminiThinkingConfig{
|
|
|
ThinkingBudget: common.GetPointer(clampedBudget),
|
|
|
IncludeThoughts: true,
|
|
|
}
|
|
|
@@ -113,11 +113,11 @@ func ThinkingAdaptor(geminiRequest *GeminiChatRequest, info *relaycommon.RelayIn
|
|
|
}
|
|
|
|
|
|
if isUnsupported {
|
|
|
- geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{
|
|
|
+ geminiRequest.GenerationConfig.ThinkingConfig = &dto.GeminiThinkingConfig{
|
|
|
IncludeThoughts: true,
|
|
|
}
|
|
|
} else {
|
|
|
- geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{
|
|
|
+ geminiRequest.GenerationConfig.ThinkingConfig = &dto.GeminiThinkingConfig{
|
|
|
IncludeThoughts: true,
|
|
|
}
|
|
|
if geminiRequest.GenerationConfig.MaxOutputTokens > 0 {
|
|
|
@@ -128,7 +128,7 @@ func ThinkingAdaptor(geminiRequest *GeminiChatRequest, info *relaycommon.RelayIn
|
|
|
}
|
|
|
} else if strings.HasSuffix(modelName, "-nothinking") {
|
|
|
if !isNew25Pro {
|
|
|
- geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{
|
|
|
+ geminiRequest.GenerationConfig.ThinkingConfig = &dto.GeminiThinkingConfig{
|
|
|
ThinkingBudget: common.GetPointer(0),
|
|
|
}
|
|
|
}
|
|
|
@@ -137,11 +137,11 @@ func ThinkingAdaptor(geminiRequest *GeminiChatRequest, info *relaycommon.RelayIn
|
|
|
}
|
|
|
|
|
|
// Setting safety to the lowest possible values since Gemini is already powerless enough
|
|
|
-func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon.RelayInfo) (*GeminiChatRequest, error) {
|
|
|
+func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon.RelayInfo) (*dto.GeminiChatRequest, error) {
|
|
|
|
|
|
- geminiRequest := GeminiChatRequest{
|
|
|
- Contents: make([]GeminiChatContent, 0, len(textRequest.Messages)),
|
|
|
- GenerationConfig: GeminiChatGenerationConfig{
|
|
|
+ geminiRequest := dto.GeminiChatRequest{
|
|
|
+ Contents: make([]dto.GeminiChatContent, 0, len(textRequest.Messages)),
|
|
|
+ GenerationConfig: dto.GeminiChatGenerationConfig{
|
|
|
Temperature: textRequest.Temperature,
|
|
|
TopP: textRequest.TopP,
|
|
|
MaxOutputTokens: textRequest.MaxTokens,
|
|
|
@@ -158,9 +158,9 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
|
|
|
|
|
|
ThinkingAdaptor(&geminiRequest, info)
|
|
|
|
|
|
- safetySettings := make([]GeminiChatSafetySettings, 0, len(SafetySettingList))
|
|
|
+ safetySettings := make([]dto.GeminiChatSafetySettings, 0, len(SafetySettingList))
|
|
|
for _, category := range SafetySettingList {
|
|
|
- safetySettings = append(safetySettings, GeminiChatSafetySettings{
|
|
|
+ safetySettings = append(safetySettings, dto.GeminiChatSafetySettings{
|
|
|
Category: category,
|
|
|
Threshold: model_setting.GetGeminiSafetySetting(category),
|
|
|
})
|
|
|
@@ -198,17 +198,17 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
|
|
|
functions = append(functions, tool.Function)
|
|
|
}
|
|
|
if codeExecution {
|
|
|
- geminiRequest.Tools = append(geminiRequest.Tools, GeminiChatTool{
|
|
|
+ geminiRequest.Tools = append(geminiRequest.Tools, dto.GeminiChatTool{
|
|
|
CodeExecution: make(map[string]string),
|
|
|
})
|
|
|
}
|
|
|
if googleSearch {
|
|
|
- geminiRequest.Tools = append(geminiRequest.Tools, GeminiChatTool{
|
|
|
+ geminiRequest.Tools = append(geminiRequest.Tools, dto.GeminiChatTool{
|
|
|
GoogleSearch: make(map[string]string),
|
|
|
})
|
|
|
}
|
|
|
if len(functions) > 0 {
|
|
|
- geminiRequest.Tools = append(geminiRequest.Tools, GeminiChatTool{
|
|
|
+ geminiRequest.Tools = append(geminiRequest.Tools, dto.GeminiChatTool{
|
|
|
FunctionDeclarations: functions,
|
|
|
})
|
|
|
}
|
|
|
@@ -238,7 +238,7 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
|
|
|
continue
|
|
|
} else if message.Role == "tool" || message.Role == "function" {
|
|
|
if len(geminiRequest.Contents) == 0 || geminiRequest.Contents[len(geminiRequest.Contents)-1].Role == "model" {
|
|
|
- geminiRequest.Contents = append(geminiRequest.Contents, GeminiChatContent{
|
|
|
+ geminiRequest.Contents = append(geminiRequest.Contents, dto.GeminiChatContent{
|
|
|
Role: "user",
|
|
|
})
|
|
|
}
|
|
|
@@ -265,18 +265,18 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- functionResp := &FunctionResponse{
|
|
|
+ functionResp := &dto.GeminiFunctionResponse{
|
|
|
Name: name,
|
|
|
Response: contentMap,
|
|
|
}
|
|
|
|
|
|
- *parts = append(*parts, GeminiPart{
|
|
|
+ *parts = append(*parts, dto.GeminiPart{
|
|
|
FunctionResponse: functionResp,
|
|
|
})
|
|
|
continue
|
|
|
}
|
|
|
- var parts []GeminiPart
|
|
|
- content := GeminiChatContent{
|
|
|
+ var parts []dto.GeminiPart
|
|
|
+ content := dto.GeminiChatContent{
|
|
|
Role: message.Role,
|
|
|
}
|
|
|
// isToolCall := false
|
|
|
@@ -290,8 +290,8 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
|
|
|
return nil, fmt.Errorf("invalid arguments for function %s, args: %s", call.Function.Name, call.Function.Arguments)
|
|
|
}
|
|
|
}
|
|
|
- toolCall := GeminiPart{
|
|
|
- FunctionCall: &FunctionCall{
|
|
|
+ toolCall := dto.GeminiPart{
|
|
|
+ FunctionCall: &dto.FunctionCall{
|
|
|
FunctionName: call.Function.Name,
|
|
|
Arguments: args,
|
|
|
},
|
|
|
@@ -308,7 +308,7 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
|
|
|
if part.Text == "" {
|
|
|
continue
|
|
|
}
|
|
|
- parts = append(parts, GeminiPart{
|
|
|
+ parts = append(parts, dto.GeminiPart{
|
|
|
Text: part.Text,
|
|
|
})
|
|
|
} else if part.Type == dto.ContentTypeImageURL {
|
|
|
@@ -331,8 +331,8 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
|
|
|
return nil, fmt.Errorf("mime type is not supported by Gemini: '%s', url: '%s', supported types are: %v", fileData.MimeType, url, getSupportedMimeTypesList())
|
|
|
}
|
|
|
|
|
|
- parts = append(parts, GeminiPart{
|
|
|
- InlineData: &GeminiInlineData{
|
|
|
+ parts = append(parts, dto.GeminiPart{
|
|
|
+ InlineData: &dto.GeminiInlineData{
|
|
|
MimeType: fileData.MimeType, // 使用原始的 MimeType,因为大小写可能对API有意义
|
|
|
Data: fileData.Base64Data,
|
|
|
},
|
|
|
@@ -342,8 +342,8 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
|
|
|
if err != nil {
|
|
|
return nil, fmt.Errorf("decode base64 image data failed: %s", err.Error())
|
|
|
}
|
|
|
- parts = append(parts, GeminiPart{
|
|
|
- InlineData: &GeminiInlineData{
|
|
|
+ parts = append(parts, dto.GeminiPart{
|
|
|
+ InlineData: &dto.GeminiInlineData{
|
|
|
MimeType: format,
|
|
|
Data: base64String,
|
|
|
},
|
|
|
@@ -357,8 +357,8 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
|
|
|
if err != nil {
|
|
|
return nil, fmt.Errorf("decode base64 file data failed: %s", err.Error())
|
|
|
}
|
|
|
- parts = append(parts, GeminiPart{
|
|
|
- InlineData: &GeminiInlineData{
|
|
|
+ parts = append(parts, dto.GeminiPart{
|
|
|
+ InlineData: &dto.GeminiInlineData{
|
|
|
MimeType: format,
|
|
|
Data: base64String,
|
|
|
},
|
|
|
@@ -371,8 +371,8 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
|
|
|
if err != nil {
|
|
|
return nil, fmt.Errorf("decode base64 audio data failed: %s", err.Error())
|
|
|
}
|
|
|
- parts = append(parts, GeminiPart{
|
|
|
- InlineData: &GeminiInlineData{
|
|
|
+ parts = append(parts, dto.GeminiPart{
|
|
|
+ InlineData: &dto.GeminiInlineData{
|
|
|
MimeType: "audio/" + part.GetInputAudio().Format,
|
|
|
Data: base64String,
|
|
|
},
|
|
|
@@ -392,8 +392,8 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon
|
|
|
}
|
|
|
|
|
|
if len(system_content) > 0 {
|
|
|
- geminiRequest.SystemInstructions = &GeminiChatContent{
|
|
|
- Parts: []GeminiPart{
|
|
|
+ geminiRequest.SystemInstructions = &dto.GeminiChatContent{
|
|
|
+ Parts: []dto.GeminiPart{
|
|
|
{
|
|
|
Text: strings.Join(system_content, "\n"),
|
|
|
},
|
|
|
@@ -636,7 +636,7 @@ func unescapeMapOrSlice(data interface{}) interface{} {
|
|
|
return data
|
|
|
}
|
|
|
|
|
|
-func getResponseToolCall(item *GeminiPart) *dto.ToolCallResponse {
|
|
|
+func getResponseToolCall(item *dto.GeminiPart) *dto.ToolCallResponse {
|
|
|
var argsBytes []byte
|
|
|
var err error
|
|
|
if result, ok := item.FunctionCall.Arguments.(map[string]interface{}); ok {
|
|
|
@@ -658,7 +658,7 @@ func getResponseToolCall(item *GeminiPart) *dto.ToolCallResponse {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func responseGeminiChat2OpenAI(c *gin.Context, response *GeminiChatResponse) *dto.OpenAITextResponse {
|
|
|
+func responseGeminiChat2OpenAI(c *gin.Context, response *dto.GeminiChatResponse) *dto.OpenAITextResponse {
|
|
|
fullTextResponse := dto.OpenAITextResponse{
|
|
|
Id: helper.GetResponseID(c),
|
|
|
Object: "chat.completion",
|
|
|
@@ -725,7 +725,7 @@ func responseGeminiChat2OpenAI(c *gin.Context, response *GeminiChatResponse) *dt
|
|
|
return &fullTextResponse
|
|
|
}
|
|
|
|
|
|
-func streamResponseGeminiChat2OpenAI(geminiResponse *GeminiChatResponse) (*dto.ChatCompletionsStreamResponse, bool, bool) {
|
|
|
+func streamResponseGeminiChat2OpenAI(geminiResponse *dto.GeminiChatResponse) (*dto.ChatCompletionsStreamResponse, bool, bool) {
|
|
|
choices := make([]dto.ChatCompletionsStreamResponseChoice, 0, len(geminiResponse.Candidates))
|
|
|
isStop := false
|
|
|
hasImage := false
|
|
|
@@ -830,7 +830,7 @@ func GeminiChatStreamHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *
|
|
|
respCount := 0
|
|
|
|
|
|
helper.StreamScannerHandler(c, resp, info, func(data string) bool {
|
|
|
- var geminiResponse GeminiChatResponse
|
|
|
+ var geminiResponse dto.GeminiChatResponse
|
|
|
err := common.UnmarshalJsonStr(data, &geminiResponse)
|
|
|
if err != nil {
|
|
|
common.LogError(c, "error unmarshalling stream response: "+err.Error())
|
|
|
@@ -913,7 +913,7 @@ func GeminiChatHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.R
|
|
|
if common.DebugEnabled {
|
|
|
println(string(responseBody))
|
|
|
}
|
|
|
- var geminiResponse GeminiChatResponse
|
|
|
+ var geminiResponse dto.GeminiChatResponse
|
|
|
err = common.Unmarshal(responseBody, &geminiResponse)
|
|
|
if err != nil {
|
|
|
return nil, types.NewOpenAIError(err, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
|
|
|
@@ -959,7 +959,7 @@ func GeminiEmbeddingHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *h
|
|
|
return nil, types.NewOpenAIError(readErr, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
|
|
|
}
|
|
|
|
|
|
- var geminiResponse GeminiEmbeddingResponse
|
|
|
+ var geminiResponse dto.GeminiEmbeddingResponse
|
|
|
if jsonErr := common.Unmarshal(responseBody, &geminiResponse); jsonErr != nil {
|
|
|
return nil, types.NewOpenAIError(jsonErr, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
|
|
|
}
|
|
|
@@ -1005,7 +1005,7 @@ func GeminiImageHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.
|
|
|
}
|
|
|
_ = resp.Body.Close()
|
|
|
|
|
|
- var geminiResponse GeminiImageResponse
|
|
|
+ var geminiResponse dto.GeminiImageResponse
|
|
|
if jsonErr := common.Unmarshal(responseBody, &geminiResponse); jsonErr != nil {
|
|
|
return nil, types.NewOpenAIError(jsonErr, types.ErrorCodeBadResponseBody, http.StatusInternalServerError)
|
|
|
}
|