| package dto |
|
|
| import ( |
| "encoding/json" |
| "strings" |
|
|
| "github.com/QuantumNous/new-api/common" |
| "github.com/QuantumNous/new-api/logger" |
| "github.com/QuantumNous/new-api/types" |
|
|
| "github.com/gin-gonic/gin" |
| ) |
|
|
| type GeminiChatRequest struct { |
| Requests []GeminiChatRequest `json:"requests,omitempty"` |
| Contents []GeminiChatContent `json:"contents"` |
| SafetySettings []GeminiChatSafetySettings `json:"safetySettings,omitempty"` |
| GenerationConfig GeminiChatGenerationConfig `json:"generationConfig,omitempty"` |
| Tools json.RawMessage `json:"tools,omitempty"` |
| ToolConfig *ToolConfig `json:"toolConfig,omitempty"` |
| SystemInstructions *GeminiChatContent `json:"systemInstruction,omitempty"` |
| CachedContent string `json:"cachedContent,omitempty"` |
| } |
|
|
| |
| func (r *GeminiChatRequest) UnmarshalJSON(data []byte) error { |
| type Alias GeminiChatRequest |
| var aux struct { |
| Alias |
| SystemInstructionSnake *GeminiChatContent `json:"system_instruction,omitempty"` |
| } |
|
|
| if err := common.Unmarshal(data, &aux); err != nil { |
| return err |
| } |
|
|
| *r = GeminiChatRequest(aux.Alias) |
|
|
| if aux.SystemInstructionSnake != nil { |
| r.SystemInstructions = aux.SystemInstructionSnake |
| } |
|
|
| return nil |
| } |
|
|
| type ToolConfig struct { |
| FunctionCallingConfig *FunctionCallingConfig `json:"functionCallingConfig,omitempty"` |
| RetrievalConfig *RetrievalConfig `json:"retrievalConfig,omitempty"` |
| } |
|
|
| type FunctionCallingConfig struct { |
| Mode FunctionCallingConfigMode `json:"mode,omitempty"` |
| AllowedFunctionNames []string `json:"allowedFunctionNames,omitempty"` |
| } |
| type FunctionCallingConfigMode string |
|
|
| type RetrievalConfig struct { |
| LatLng *LatLng `json:"latLng,omitempty"` |
| LanguageCode string `json:"languageCode,omitempty"` |
| } |
|
|
| type LatLng struct { |
| Latitude *float64 `json:"latitude,omitempty"` |
| Longitude *float64 `json:"longitude,omitempty"` |
| } |
|
|
| |
| func createGeminiFileSource(data string, mimeType string) *types.FileSource { |
| if strings.HasPrefix(data, "http://") || strings.HasPrefix(data, "https://") { |
| return types.NewURLFileSource(data) |
| } |
| return types.NewBase64FileSource(data, mimeType) |
| } |
|
|
| func (r *GeminiChatRequest) GetTokenCountMeta() *types.TokenCountMeta { |
| var files []*types.FileMeta = make([]*types.FileMeta, 0) |
|
|
| var maxTokens int |
|
|
| if r.GenerationConfig.MaxOutputTokens != nil && *r.GenerationConfig.MaxOutputTokens > 0 { |
| maxTokens = int(*r.GenerationConfig.MaxOutputTokens) |
| } |
|
|
| var inputTexts []string |
| for _, content := range r.Contents { |
| for _, part := range content.Parts { |
| if part.Text != "" { |
| inputTexts = append(inputTexts, part.Text) |
| } |
| if part.InlineData != nil && part.InlineData.Data != "" { |
| mimeType := part.InlineData.MimeType |
| source := createGeminiFileSource(part.InlineData.Data, mimeType) |
| var fileType types.FileType |
| if strings.HasPrefix(mimeType, "image/") { |
| fileType = types.FileTypeImage |
| } else if strings.HasPrefix(mimeType, "audio/") { |
| fileType = types.FileTypeAudio |
| } else if strings.HasPrefix(mimeType, "video/") { |
| fileType = types.FileTypeVideo |
| } else { |
| fileType = types.FileTypeFile |
| } |
| files = append(files, &types.FileMeta{ |
| FileType: fileType, |
| Source: source, |
| MimeType: mimeType, |
| }) |
| } |
| } |
| } |
|
|
| inputText := strings.Join(inputTexts, "\n") |
| return &types.TokenCountMeta{ |
| CombineText: inputText, |
| Files: files, |
| MaxTokens: maxTokens, |
| } |
| } |
|
|
| func (r *GeminiChatRequest) IsStream(c *gin.Context) bool { |
| if c.Query("alt") == "sse" { |
| return true |
| } |
| return false |
| } |
|
|
| func (r *GeminiChatRequest) SetModelName(modelName string) { |
| |
| } |
|
|
| func (r *GeminiChatRequest) GetTools() []GeminiChatTool { |
| var tools []GeminiChatTool |
| if strings.HasPrefix(string(r.Tools), "[") { |
| |
| if err := common.Unmarshal(r.Tools, &tools); err != nil { |
| logger.LogError(nil, "error_unmarshalling_tools: "+err.Error()) |
| return nil |
| } |
| } else if strings.HasPrefix(string(r.Tools), "{") { |
| |
| singleTool := GeminiChatTool{} |
| if err := common.Unmarshal(r.Tools, &singleTool); err != nil { |
| logger.LogError(nil, "error_unmarshalling_single_tool: "+err.Error()) |
| return nil |
| } |
| tools = []GeminiChatTool{singleTool} |
| } |
| return tools |
| } |
|
|
| func (r *GeminiChatRequest) SetTools(tools []GeminiChatTool) { |
| if len(tools) == 0 { |
| r.Tools = json.RawMessage("[]") |
| return |
| } |
|
|
| |
| data, err := common.Marshal(tools) |
| if err != nil { |
| logger.LogError(nil, "error_marshalling_tools: "+err.Error()) |
| return |
| } |
| r.Tools = data |
| } |
|
|
| type GeminiThinkingConfig struct { |
| IncludeThoughts bool `json:"includeThoughts,omitempty"` |
| ThinkingBudget *int `json:"thinkingBudget,omitempty"` |
| |
| ThinkingLevel string `json:"thinkingLevel,omitempty"` |
| } |
|
|
| |
| func (c *GeminiThinkingConfig) UnmarshalJSON(data []byte) error { |
| type Alias GeminiThinkingConfig |
| var aux struct { |
| Alias |
| IncludeThoughtsSnake *bool `json:"include_thoughts,omitempty"` |
| ThinkingBudgetSnake *int `json:"thinking_budget,omitempty"` |
| ThinkingLevelSnake string `json:"thinking_level,omitempty"` |
| } |
|
|
| if err := common.Unmarshal(data, &aux); err != nil { |
| return err |
| } |
|
|
| *c = GeminiThinkingConfig(aux.Alias) |
|
|
| if aux.IncludeThoughtsSnake != nil { |
| c.IncludeThoughts = *aux.IncludeThoughtsSnake |
| } |
|
|
| if aux.ThinkingBudgetSnake != nil { |
| c.ThinkingBudget = aux.ThinkingBudgetSnake |
| } |
|
|
| if aux.ThinkingLevelSnake != "" { |
| c.ThinkingLevel = aux.ThinkingLevelSnake |
| } |
|
|
| return nil |
| } |
|
|
| func (c *GeminiThinkingConfig) SetThinkingBudget(budget int) { |
| c.ThinkingBudget = &budget |
| } |
|
|
| type GeminiInlineData struct { |
| MimeType string `json:"mimeType"` |
| Data string `json:"data"` |
| } |
|
|
| |
| func (g *GeminiInlineData) UnmarshalJSON(data []byte) error { |
| type Alias GeminiInlineData |
| var aux struct { |
| Alias |
| MimeTypeSnake string `json:"mime_type"` |
| } |
|
|
| if err := common.Unmarshal(data, &aux); err != nil { |
| return err |
| } |
|
|
| *g = GeminiInlineData(aux.Alias) |
|
|
| |
| if aux.MimeTypeSnake != "" { |
| g.MimeType = aux.MimeTypeSnake |
| } else if aux.MimeType != "" { |
| g.MimeType = aux.MimeType |
| } |
| |
| return nil |
| } |
|
|
| type FunctionCall struct { |
| FunctionName string `json:"name"` |
| Arguments any `json:"args"` |
| } |
|
|
| type GeminiFunctionResponse struct { |
| Name string `json:"name"` |
| Response map[string]interface{} `json:"response"` |
| WillContinue json.RawMessage `json:"willContinue,omitempty"` |
| Scheduling json.RawMessage `json:"scheduling,omitempty"` |
| Parts json.RawMessage `json:"parts,omitempty"` |
| ID json.RawMessage `json:"id,omitempty"` |
| } |
|
|
| type GeminiPartExecutableCode struct { |
| Language string `json:"language,omitempty"` |
| Code string `json:"code,omitempty"` |
| } |
|
|
| type GeminiPartCodeExecutionResult struct { |
| Outcome string `json:"outcome,omitempty"` |
| Output string `json:"output,omitempty"` |
| } |
|
|
| type GeminiFileData struct { |
| MimeType string `json:"mimeType,omitempty"` |
| FileUri string `json:"fileUri,omitempty"` |
| } |
|
|
| type GeminiPart struct { |
| Text string `json:"text,omitempty"` |
| Thought bool `json:"thought,omitempty"` |
| InlineData *GeminiInlineData `json:"inlineData,omitempty"` |
| FunctionCall *FunctionCall `json:"functionCall,omitempty"` |
| ThoughtSignature json.RawMessage `json:"thoughtSignature,omitempty"` |
| FunctionResponse *GeminiFunctionResponse `json:"functionResponse,omitempty"` |
| |
| MediaResolution json.RawMessage `json:"mediaResolution,omitempty"` |
| VideoMetadata json.RawMessage `json:"videoMetadata,omitempty"` |
| FileData *GeminiFileData `json:"fileData,omitempty"` |
| ExecutableCode *GeminiPartExecutableCode `json:"executableCode,omitempty"` |
| CodeExecutionResult *GeminiPartCodeExecutionResult `json:"codeExecutionResult,omitempty"` |
| } |
|
|
| |
| func (p *GeminiPart) UnmarshalJSON(data []byte) error { |
| |
| type Alias GeminiPart |
| var aux struct { |
| Alias |
| InlineDataSnake *GeminiInlineData `json:"inline_data,omitempty"` |
| } |
|
|
| if err := common.Unmarshal(data, &aux); err != nil { |
| return err |
| } |
|
|
| |
| *p = GeminiPart(aux.Alias) |
|
|
| |
| if aux.InlineDataSnake != nil { |
| p.InlineData = aux.InlineDataSnake |
| } else if aux.InlineData != nil { |
| p.InlineData = aux.InlineData |
| } |
| |
|
|
| return nil |
| } |
|
|
| type GeminiChatContent struct { |
| Role string `json:"role,omitempty"` |
| Parts []GeminiPart `json:"parts"` |
| } |
|
|
| type GeminiChatSafetySettings struct { |
| Category string `json:"category"` |
| Threshold string `json:"threshold"` |
| } |
|
|
| type GeminiChatTool struct { |
| GoogleSearch any `json:"googleSearch,omitempty"` |
| GoogleSearchRetrieval any `json:"googleSearchRetrieval,omitempty"` |
| CodeExecution any `json:"codeExecution,omitempty"` |
| FunctionDeclarations any `json:"functionDeclarations,omitempty"` |
| URLContext any `json:"urlContext,omitempty"` |
| } |
|
|
| type GeminiChatGenerationConfig struct { |
| Temperature *float64 `json:"temperature,omitempty"` |
| TopP *float64 `json:"topP,omitempty"` |
| TopK *float64 `json:"topK,omitempty"` |
| MaxOutputTokens *uint `json:"maxOutputTokens,omitempty"` |
| CandidateCount *int `json:"candidateCount,omitempty"` |
| StopSequences []string `json:"stopSequences,omitempty"` |
| ResponseMimeType string `json:"responseMimeType,omitempty"` |
| ResponseSchema any `json:"responseSchema,omitempty"` |
| ResponseJsonSchema json.RawMessage `json:"responseJsonSchema,omitempty"` |
| PresencePenalty *float32 `json:"presencePenalty,omitempty"` |
| FrequencyPenalty *float32 `json:"frequencyPenalty,omitempty"` |
| ResponseLogprobs *bool `json:"responseLogprobs,omitempty"` |
| Logprobs *int32 `json:"logprobs,omitempty"` |
| EnableEnhancedCivicAnswers *bool `json:"enableEnhancedCivicAnswers,omitempty"` |
| MediaResolution MediaResolution `json:"mediaResolution,omitempty"` |
| Seed *int64 `json:"seed,omitempty"` |
| ResponseModalities []string `json:"responseModalities,omitempty"` |
| ThinkingConfig *GeminiThinkingConfig `json:"thinkingConfig,omitempty"` |
| SpeechConfig json.RawMessage `json:"speechConfig,omitempty"` |
| ImageConfig json.RawMessage `json:"imageConfig,omitempty"` |
| } |
|
|
| |
| func (c *GeminiChatGenerationConfig) UnmarshalJSON(data []byte) error { |
| type Alias GeminiChatGenerationConfig |
| var aux struct { |
| Alias |
| TopPSnake *float64 `json:"top_p,omitempty"` |
| TopKSnake *float64 `json:"top_k,omitempty"` |
| MaxOutputTokensSnake *uint `json:"max_output_tokens,omitempty"` |
| CandidateCountSnake *int `json:"candidate_count,omitempty"` |
| StopSequencesSnake []string `json:"stop_sequences,omitempty"` |
| ResponseMimeTypeSnake string `json:"response_mime_type,omitempty"` |
| ResponseSchemaSnake any `json:"response_schema,omitempty"` |
| ResponseJsonSchemaSnake json.RawMessage `json:"response_json_schema,omitempty"` |
| PresencePenaltySnake *float32 `json:"presence_penalty,omitempty"` |
| FrequencyPenaltySnake *float32 `json:"frequency_penalty,omitempty"` |
| ResponseLogprobsSnake *bool `json:"response_logprobs,omitempty"` |
| EnableEnhancedCivicAnswersSnake *bool `json:"enable_enhanced_civic_answers,omitempty"` |
| MediaResolutionSnake MediaResolution `json:"media_resolution,omitempty"` |
| ResponseModalitiesSnake []string `json:"response_modalities,omitempty"` |
| ThinkingConfigSnake *GeminiThinkingConfig `json:"thinking_config,omitempty"` |
| SpeechConfigSnake json.RawMessage `json:"speech_config,omitempty"` |
| ImageConfigSnake json.RawMessage `json:"image_config,omitempty"` |
| } |
|
|
| if err := common.Unmarshal(data, &aux); err != nil { |
| return err |
| } |
|
|
| *c = GeminiChatGenerationConfig(aux.Alias) |
|
|
| |
| if aux.TopPSnake != nil { |
| c.TopP = aux.TopPSnake |
| } |
| if aux.TopKSnake != nil { |
| c.TopK = aux.TopKSnake |
| } |
| if aux.MaxOutputTokensSnake != nil { |
| c.MaxOutputTokens = aux.MaxOutputTokensSnake |
| } |
| if aux.CandidateCountSnake != nil { |
| c.CandidateCount = aux.CandidateCountSnake |
| } |
| if len(aux.StopSequencesSnake) > 0 { |
| c.StopSequences = aux.StopSequencesSnake |
| } |
| if aux.ResponseMimeTypeSnake != "" { |
| c.ResponseMimeType = aux.ResponseMimeTypeSnake |
| } |
| if aux.ResponseSchemaSnake != nil { |
| c.ResponseSchema = aux.ResponseSchemaSnake |
| } |
| if len(aux.ResponseJsonSchemaSnake) > 0 { |
| c.ResponseJsonSchema = aux.ResponseJsonSchemaSnake |
| } |
| if aux.PresencePenaltySnake != nil { |
| c.PresencePenalty = aux.PresencePenaltySnake |
| } |
| if aux.FrequencyPenaltySnake != nil { |
| c.FrequencyPenalty = aux.FrequencyPenaltySnake |
| } |
| if aux.ResponseLogprobsSnake != nil { |
| c.ResponseLogprobs = aux.ResponseLogprobsSnake |
| } |
| if aux.EnableEnhancedCivicAnswersSnake != nil { |
| c.EnableEnhancedCivicAnswers = aux.EnableEnhancedCivicAnswersSnake |
| } |
| if aux.MediaResolutionSnake != "" { |
| c.MediaResolution = aux.MediaResolutionSnake |
| } |
| if len(aux.ResponseModalitiesSnake) > 0 { |
| c.ResponseModalities = aux.ResponseModalitiesSnake |
| } |
| if aux.ThinkingConfigSnake != nil { |
| c.ThinkingConfig = aux.ThinkingConfigSnake |
| } |
| if len(aux.SpeechConfigSnake) > 0 { |
| c.SpeechConfig = aux.SpeechConfigSnake |
| } |
| if len(aux.ImageConfigSnake) > 0 { |
| c.ImageConfig = aux.ImageConfigSnake |
| } |
|
|
| return nil |
| } |
|
|
| type MediaResolution string |
|
|
| type GeminiChatCandidate struct { |
| Content GeminiChatContent `json:"content"` |
| FinishReason *string `json:"finishReason"` |
| Index int64 `json:"index"` |
| SafetyRatings []GeminiChatSafetyRating `json:"safetyRatings"` |
| } |
|
|
| type GeminiChatSafetyRating struct { |
| Category string `json:"category"` |
| Probability string `json:"probability"` |
| } |
|
|
| type GeminiChatPromptFeedback struct { |
| SafetyRatings []GeminiChatSafetyRating `json:"safetyRatings"` |
| BlockReason *string `json:"blockReason,omitempty"` |
| } |
|
|
| type GeminiChatResponse struct { |
| Candidates []GeminiChatCandidate `json:"candidates"` |
| PromptFeedback *GeminiChatPromptFeedback `json:"promptFeedback,omitempty"` |
| UsageMetadata GeminiUsageMetadata `json:"usageMetadata"` |
| } |
|
|
| type GeminiUsageMetadata struct { |
| PromptTokenCount int `json:"promptTokenCount"` |
| ToolUsePromptTokenCount int `json:"toolUsePromptTokenCount"` |
| CandidatesTokenCount int `json:"candidatesTokenCount"` |
| TotalTokenCount int `json:"totalTokenCount"` |
| ThoughtsTokenCount int `json:"thoughtsTokenCount"` |
| CachedContentTokenCount int `json:"cachedContentTokenCount"` |
| PromptTokensDetails []GeminiPromptTokensDetails `json:"promptTokensDetails"` |
| ToolUsePromptTokensDetails []GeminiPromptTokensDetails `json:"toolUsePromptTokensDetails"` |
| } |
|
|
| type GeminiPromptTokensDetails struct { |
| Modality string `json:"modality"` |
| TokenCount int `json:"tokenCount"` |
| } |
|
|
| |
| type GeminiImageRequest struct { |
| Instances []GeminiImageInstance `json:"instances"` |
| Parameters GeminiImageParameters `json:"parameters"` |
| } |
|
|
| type GeminiImageInstance struct { |
| Prompt string `json:"prompt"` |
| } |
|
|
| type GeminiImageParameters struct { |
| SampleCount int `json:"sampleCount,omitempty"` |
| AspectRatio string `json:"aspectRatio,omitempty"` |
| PersonGeneration string `json:"personGeneration,omitempty"` |
| ImageSize string `json:"imageSize,omitempty"` |
| } |
|
|
| type GeminiImageResponse struct { |
| Predictions []GeminiImagePrediction `json:"predictions"` |
| } |
|
|
| type GeminiImagePrediction struct { |
| MimeType string `json:"mimeType"` |
| BytesBase64Encoded string `json:"bytesBase64Encoded"` |
| RaiFilteredReason string `json:"raiFilteredReason,omitempty"` |
| SafetyAttributes any `json:"safetyAttributes,omitempty"` |
| } |
|
|
| |
| type GeminiEmbeddingRequest struct { |
| Model string `json:"model,omitempty"` |
| Content GeminiChatContent `json:"content"` |
| TaskType string `json:"taskType,omitempty"` |
| Title string `json:"title,omitempty"` |
| OutputDimensionality int `json:"outputDimensionality,omitempty"` |
| } |
|
|
| func (r *GeminiEmbeddingRequest) IsStream(c *gin.Context) bool { |
| |
| return false |
| } |
|
|
| func (r *GeminiEmbeddingRequest) GetTokenCountMeta() *types.TokenCountMeta { |
| var inputTexts []string |
| for _, part := range r.Content.Parts { |
| if part.Text != "" { |
| inputTexts = append(inputTexts, part.Text) |
| } |
| } |
| inputText := strings.Join(inputTexts, "\n") |
| return &types.TokenCountMeta{ |
| CombineText: inputText, |
| } |
| } |
|
|
| func (r *GeminiEmbeddingRequest) SetModelName(modelName string) { |
| if modelName != "" { |
| r.Model = modelName |
| } |
| } |
|
|
| type GeminiBatchEmbeddingRequest struct { |
| Requests []*GeminiEmbeddingRequest `json:"requests"` |
| } |
|
|
| func (r *GeminiBatchEmbeddingRequest) IsStream(c *gin.Context) bool { |
| |
| return false |
| } |
|
|
| func (r *GeminiBatchEmbeddingRequest) GetTokenCountMeta() *types.TokenCountMeta { |
| var inputTexts []string |
| for _, request := range r.Requests { |
| meta := request.GetTokenCountMeta() |
| if meta != nil && meta.CombineText != "" { |
| inputTexts = append(inputTexts, meta.CombineText) |
| } |
| } |
| inputText := strings.Join(inputTexts, "\n") |
| return &types.TokenCountMeta{ |
| CombineText: inputText, |
| } |
| } |
|
|
| func (r *GeminiBatchEmbeddingRequest) SetModelName(modelName string) { |
| if modelName != "" { |
| for _, req := range r.Requests { |
| req.SetModelName(modelName) |
| } |
| } |
| } |
|
|
| type GeminiEmbeddingResponse struct { |
| Embedding ContentEmbedding `json:"embedding"` |
| } |
|
|
| type GeminiBatchEmbeddingResponse struct { |
| Embeddings []*ContentEmbedding `json:"embeddings"` |
| } |
|
|
| type ContentEmbedding struct { |
| Values []float64 `json:"values"` |
| } |
|
|