gemini.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. package dto
  2. import (
  3. "encoding/json"
  4. "strings"
  5. "github.com/QuantumNous/new-api/common"
  6. "github.com/QuantumNous/new-api/logger"
  7. "github.com/QuantumNous/new-api/types"
  8. "github.com/gin-gonic/gin"
  9. )
  10. type GeminiChatRequest struct {
  11. Requests []GeminiChatRequest `json:"requests,omitempty"` // For batch requests
  12. Contents []GeminiChatContent `json:"contents"`
  13. SafetySettings []GeminiChatSafetySettings `json:"safetySettings,omitempty"`
  14. GenerationConfig GeminiChatGenerationConfig `json:"generationConfig,omitempty"`
  15. Tools json.RawMessage `json:"tools,omitempty"`
  16. ToolConfig *ToolConfig `json:"toolConfig,omitempty"`
  17. SystemInstructions *GeminiChatContent `json:"systemInstruction,omitempty"`
  18. CachedContent string `json:"cachedContent,omitempty"`
  19. }
  20. type ToolConfig struct {
  21. FunctionCallingConfig *FunctionCallingConfig `json:"functionCallingConfig,omitempty"`
  22. RetrievalConfig *RetrievalConfig `json:"retrievalConfig,omitempty"`
  23. }
  24. type FunctionCallingConfig struct {
  25. Mode FunctionCallingConfigMode `json:"mode,omitempty"`
  26. AllowedFunctionNames []string `json:"allowedFunctionNames,omitempty"`
  27. }
  28. type FunctionCallingConfigMode string
  29. type RetrievalConfig struct {
  30. LatLng *LatLng `json:"latLng,omitempty"`
  31. LanguageCode string `json:"languageCode,omitempty"`
  32. }
  33. type LatLng struct {
  34. Latitude *float64 `json:"latitude,omitempty"`
  35. Longitude *float64 `json:"longitude,omitempty"`
  36. }
  37. func (r *GeminiChatRequest) GetTokenCountMeta() *types.TokenCountMeta {
  38. var files []*types.FileMeta = make([]*types.FileMeta, 0)
  39. var maxTokens int
  40. if r.GenerationConfig.MaxOutputTokens > 0 {
  41. maxTokens = int(r.GenerationConfig.MaxOutputTokens)
  42. }
  43. var inputTexts []string
  44. for _, content := range r.Contents {
  45. for _, part := range content.Parts {
  46. if part.Text != "" {
  47. inputTexts = append(inputTexts, part.Text)
  48. }
  49. if part.InlineData != nil && part.InlineData.Data != "" {
  50. if strings.HasPrefix(part.InlineData.MimeType, "image/") {
  51. files = append(files, &types.FileMeta{
  52. FileType: types.FileTypeImage,
  53. OriginData: part.InlineData.Data,
  54. })
  55. } else if strings.HasPrefix(part.InlineData.MimeType, "audio/") {
  56. files = append(files, &types.FileMeta{
  57. FileType: types.FileTypeAudio,
  58. OriginData: part.InlineData.Data,
  59. })
  60. } else if strings.HasPrefix(part.InlineData.MimeType, "video/") {
  61. files = append(files, &types.FileMeta{
  62. FileType: types.FileTypeVideo,
  63. OriginData: part.InlineData.Data,
  64. })
  65. } else {
  66. files = append(files, &types.FileMeta{
  67. FileType: types.FileTypeFile,
  68. OriginData: part.InlineData.Data,
  69. })
  70. }
  71. }
  72. }
  73. }
  74. inputText := strings.Join(inputTexts, "\n")
  75. return &types.TokenCountMeta{
  76. CombineText: inputText,
  77. Files: files,
  78. MaxTokens: maxTokens,
  79. }
  80. }
  81. func (r *GeminiChatRequest) IsStream(c *gin.Context) bool {
  82. if c.Query("alt") == "sse" {
  83. return true
  84. }
  85. return false
  86. }
  87. func (r *GeminiChatRequest) SetModelName(modelName string) {
  88. // GeminiChatRequest does not have a model field, so this method does nothing.
  89. }
  90. func (r *GeminiChatRequest) GetTools() []GeminiChatTool {
  91. var tools []GeminiChatTool
  92. if strings.HasSuffix(string(r.Tools), "[") {
  93. // is array
  94. if err := common.Unmarshal(r.Tools, &tools); err != nil {
  95. logger.LogError(nil, "error_unmarshalling_tools: "+err.Error())
  96. return nil
  97. }
  98. } else if strings.HasPrefix(string(r.Tools), "{") {
  99. // is object
  100. singleTool := GeminiChatTool{}
  101. if err := common.Unmarshal(r.Tools, &singleTool); err != nil {
  102. logger.LogError(nil, "error_unmarshalling_single_tool: "+err.Error())
  103. return nil
  104. }
  105. tools = []GeminiChatTool{singleTool}
  106. }
  107. return tools
  108. }
  109. func (r *GeminiChatRequest) SetTools(tools []GeminiChatTool) {
  110. if len(tools) == 0 {
  111. r.Tools = json.RawMessage("[]")
  112. return
  113. }
  114. // Marshal the tools to JSON
  115. data, err := common.Marshal(tools)
  116. if err != nil {
  117. logger.LogError(nil, "error_marshalling_tools: "+err.Error())
  118. return
  119. }
  120. r.Tools = data
  121. }
  122. type GeminiThinkingConfig struct {
  123. IncludeThoughts bool `json:"includeThoughts,omitempty"`
  124. ThinkingBudget *int `json:"thinkingBudget,omitempty"`
  125. // TODO Conflict with thinkingbudget.
  126. ThinkingLevel json.RawMessage `json:"thinkingLevel,omitempty"`
  127. }
  128. // UnmarshalJSON allows GeminiThinkingConfig to accept both snake_case and camelCase fields.
  129. func (c *GeminiThinkingConfig) UnmarshalJSON(data []byte) error {
  130. type Alias GeminiThinkingConfig
  131. var aux struct {
  132. Alias
  133. IncludeThoughtsSnake *bool `json:"include_thoughts,omitempty"`
  134. ThinkingBudgetSnake *int `json:"thinking_budget,omitempty"`
  135. ThinkingLevelSnake json.RawMessage `json:"thinking_level,omitempty"`
  136. }
  137. if err := common.Unmarshal(data, &aux); err != nil {
  138. return err
  139. }
  140. *c = GeminiThinkingConfig(aux.Alias)
  141. if aux.IncludeThoughtsSnake != nil {
  142. c.IncludeThoughts = *aux.IncludeThoughtsSnake
  143. }
  144. if aux.ThinkingBudgetSnake != nil {
  145. c.ThinkingBudget = aux.ThinkingBudgetSnake
  146. }
  147. if len(aux.ThinkingLevelSnake) > 0 {
  148. c.ThinkingLevel = aux.ThinkingLevelSnake
  149. }
  150. return nil
  151. }
  152. func (c *GeminiThinkingConfig) SetThinkingBudget(budget int) {
  153. c.ThinkingBudget = &budget
  154. }
  155. type GeminiInlineData struct {
  156. MimeType string `json:"mimeType"`
  157. Data string `json:"data"`
  158. }
  159. // UnmarshalJSON custom unmarshaler for GeminiInlineData to support snake_case and camelCase for MimeType
  160. func (g *GeminiInlineData) UnmarshalJSON(data []byte) error {
  161. type Alias GeminiInlineData // Use type alias to avoid recursion
  162. var aux struct {
  163. Alias
  164. MimeTypeSnake string `json:"mime_type"`
  165. }
  166. if err := common.Unmarshal(data, &aux); err != nil {
  167. return err
  168. }
  169. *g = GeminiInlineData(aux.Alias) // Copy other fields if any in future
  170. // Prioritize snake_case if present
  171. if aux.MimeTypeSnake != "" {
  172. g.MimeType = aux.MimeTypeSnake
  173. } else if aux.MimeType != "" { // Fallback to camelCase from Alias
  174. g.MimeType = aux.MimeType
  175. }
  176. // g.Data would be populated by aux.Alias.Data
  177. return nil
  178. }
  179. type FunctionCall struct {
  180. FunctionName string `json:"name"`
  181. Arguments any `json:"args"`
  182. }
  183. type GeminiFunctionResponse struct {
  184. Name string `json:"name"`
  185. Response map[string]interface{} `json:"response"`
  186. WillContinue json.RawMessage `json:"willContinue,omitempty"`
  187. Scheduling json.RawMessage `json:"scheduling,omitempty"`
  188. Parts json.RawMessage `json:"parts,omitempty"`
  189. ID json.RawMessage `json:"id,omitempty"`
  190. }
  191. type GeminiPartExecutableCode struct {
  192. Language string `json:"language,omitempty"`
  193. Code string `json:"code,omitempty"`
  194. }
  195. type GeminiPartCodeExecutionResult struct {
  196. Outcome string `json:"outcome,omitempty"`
  197. Output string `json:"output,omitempty"`
  198. }
  199. type GeminiFileData struct {
  200. MimeType string `json:"mimeType,omitempty"`
  201. FileUri string `json:"fileUri,omitempty"`
  202. }
  203. type GeminiPart struct {
  204. Text string `json:"text,omitempty"`
  205. Thought bool `json:"thought,omitempty"`
  206. InlineData *GeminiInlineData `json:"inlineData,omitempty"`
  207. FunctionCall *FunctionCall `json:"functionCall,omitempty"`
  208. ThoughtSignature json.RawMessage `json:"thoughtSignature,omitempty"`
  209. FunctionResponse *GeminiFunctionResponse `json:"functionResponse,omitempty"`
  210. // Optional. Media resolution for the input media.
  211. MediaResolution json.RawMessage `json:"mediaResolution,omitempty"`
  212. VideoMetadata json.RawMessage `json:"videoMetadata,omitempty"`
  213. FileData *GeminiFileData `json:"fileData,omitempty"`
  214. ExecutableCode *GeminiPartExecutableCode `json:"executableCode,omitempty"`
  215. CodeExecutionResult *GeminiPartCodeExecutionResult `json:"codeExecutionResult,omitempty"`
  216. }
  217. // UnmarshalJSON custom unmarshaler for GeminiPart to support snake_case and camelCase for InlineData
  218. func (p *GeminiPart) UnmarshalJSON(data []byte) error {
  219. // Alias to avoid recursion during unmarshalling
  220. type Alias GeminiPart
  221. var aux struct {
  222. Alias
  223. InlineDataSnake *GeminiInlineData `json:"inline_data,omitempty"` // snake_case variant
  224. }
  225. if err := common.Unmarshal(data, &aux); err != nil {
  226. return err
  227. }
  228. // Assign fields from alias
  229. *p = GeminiPart(aux.Alias)
  230. // Prioritize snake_case for InlineData if present
  231. if aux.InlineDataSnake != nil {
  232. p.InlineData = aux.InlineDataSnake
  233. } else if aux.InlineData != nil { // Fallback to camelCase from Alias
  234. p.InlineData = aux.InlineData
  235. }
  236. // Other fields like Text, FunctionCall etc. are already populated via aux.Alias
  237. return nil
  238. }
  239. type GeminiChatContent struct {
  240. Role string `json:"role,omitempty"`
  241. Parts []GeminiPart `json:"parts"`
  242. }
  243. type GeminiChatSafetySettings struct {
  244. Category string `json:"category"`
  245. Threshold string `json:"threshold"`
  246. }
  247. type GeminiChatTool struct {
  248. GoogleSearch any `json:"googleSearch,omitempty"`
  249. GoogleSearchRetrieval any `json:"googleSearchRetrieval,omitempty"`
  250. CodeExecution any `json:"codeExecution,omitempty"`
  251. FunctionDeclarations any `json:"functionDeclarations,omitempty"`
  252. URLContext any `json:"urlContext,omitempty"`
  253. }
  254. type GeminiChatGenerationConfig struct {
  255. Temperature *float64 `json:"temperature,omitempty"`
  256. TopP float64 `json:"topP,omitempty"`
  257. TopK float64 `json:"topK,omitempty"`
  258. MaxOutputTokens uint `json:"maxOutputTokens,omitempty"`
  259. CandidateCount int `json:"candidateCount,omitempty"`
  260. StopSequences []string `json:"stopSequences,omitempty"`
  261. ResponseMimeType string `json:"responseMimeType,omitempty"`
  262. ResponseSchema any `json:"responseSchema,omitempty"`
  263. ResponseJsonSchema json.RawMessage `json:"responseJsonSchema,omitempty"`
  264. PresencePenalty *float32 `json:"presencePenalty,omitempty"`
  265. FrequencyPenalty *float32 `json:"frequencyPenalty,omitempty"`
  266. ResponseLogprobs bool `json:"responseLogprobs,omitempty"`
  267. Logprobs *int32 `json:"logprobs,omitempty"`
  268. MediaResolution MediaResolution `json:"mediaResolution,omitempty"`
  269. Seed int64 `json:"seed,omitempty"`
  270. ResponseModalities []string `json:"responseModalities,omitempty"`
  271. ThinkingConfig *GeminiThinkingConfig `json:"thinkingConfig,omitempty"`
  272. SpeechConfig json.RawMessage `json:"speechConfig,omitempty"` // RawMessage to allow flexible speech config
  273. ImageConfig json.RawMessage `json:"imageConfig,omitempty"` // RawMessage to allow flexible image config
  274. }
  275. type MediaResolution string
  276. type GeminiChatCandidate struct {
  277. Content GeminiChatContent `json:"content"`
  278. FinishReason *string `json:"finishReason"`
  279. Index int64 `json:"index"`
  280. SafetyRatings []GeminiChatSafetyRating `json:"safetyRatings"`
  281. }
  282. type GeminiChatSafetyRating struct {
  283. Category string `json:"category"`
  284. Probability string `json:"probability"`
  285. }
  286. type GeminiChatPromptFeedback struct {
  287. SafetyRatings []GeminiChatSafetyRating `json:"safetyRatings"`
  288. BlockReason *string `json:"blockReason,omitempty"`
  289. }
  290. type GeminiChatResponse struct {
  291. Candidates []GeminiChatCandidate `json:"candidates"`
  292. PromptFeedback *GeminiChatPromptFeedback `json:"promptFeedback,omitempty"`
  293. UsageMetadata GeminiUsageMetadata `json:"usageMetadata"`
  294. }
  295. type GeminiUsageMetadata struct {
  296. PromptTokenCount int `json:"promptTokenCount"`
  297. CandidatesTokenCount int `json:"candidatesTokenCount"`
  298. TotalTokenCount int `json:"totalTokenCount"`
  299. ThoughtsTokenCount int `json:"thoughtsTokenCount"`
  300. PromptTokensDetails []GeminiPromptTokensDetails `json:"promptTokensDetails"`
  301. }
  302. type GeminiPromptTokensDetails struct {
  303. Modality string `json:"modality"`
  304. TokenCount int `json:"tokenCount"`
  305. }
  306. // Imagen related structs
  307. type GeminiImageRequest struct {
  308. Instances []GeminiImageInstance `json:"instances"`
  309. Parameters GeminiImageParameters `json:"parameters"`
  310. }
  311. type GeminiImageInstance struct {
  312. Prompt string `json:"prompt"`
  313. }
  314. type GeminiImageParameters struct {
  315. SampleCount int `json:"sampleCount,omitempty"`
  316. AspectRatio string `json:"aspectRatio,omitempty"`
  317. PersonGeneration string `json:"personGeneration,omitempty"`
  318. ImageSize string `json:"imageSize,omitempty"`
  319. }
  320. type GeminiImageResponse struct {
  321. Predictions []GeminiImagePrediction `json:"predictions"`
  322. }
  323. type GeminiImagePrediction struct {
  324. MimeType string `json:"mimeType"`
  325. BytesBase64Encoded string `json:"bytesBase64Encoded"`
  326. RaiFilteredReason string `json:"raiFilteredReason,omitempty"`
  327. SafetyAttributes any `json:"safetyAttributes,omitempty"`
  328. }
  329. // Embedding related structs
  330. type GeminiEmbeddingRequest struct {
  331. Model string `json:"model,omitempty"`
  332. Content GeminiChatContent `json:"content"`
  333. TaskType string `json:"taskType,omitempty"`
  334. Title string `json:"title,omitempty"`
  335. OutputDimensionality int `json:"outputDimensionality,omitempty"`
  336. }
  337. func (r *GeminiEmbeddingRequest) IsStream(c *gin.Context) bool {
  338. // Gemini embedding requests are not streamed
  339. return false
  340. }
  341. func (r *GeminiEmbeddingRequest) GetTokenCountMeta() *types.TokenCountMeta {
  342. var inputTexts []string
  343. for _, part := range r.Content.Parts {
  344. if part.Text != "" {
  345. inputTexts = append(inputTexts, part.Text)
  346. }
  347. }
  348. inputText := strings.Join(inputTexts, "\n")
  349. return &types.TokenCountMeta{
  350. CombineText: inputText,
  351. }
  352. }
  353. func (r *GeminiEmbeddingRequest) SetModelName(modelName string) {
  354. if modelName != "" {
  355. r.Model = modelName
  356. }
  357. }
  358. type GeminiBatchEmbeddingRequest struct {
  359. Requests []*GeminiEmbeddingRequest `json:"requests"`
  360. }
  361. func (r *GeminiBatchEmbeddingRequest) IsStream(c *gin.Context) bool {
  362. // Gemini batch embedding requests are not streamed
  363. return false
  364. }
  365. func (r *GeminiBatchEmbeddingRequest) GetTokenCountMeta() *types.TokenCountMeta {
  366. var inputTexts []string
  367. for _, request := range r.Requests {
  368. meta := request.GetTokenCountMeta()
  369. if meta != nil && meta.CombineText != "" {
  370. inputTexts = append(inputTexts, meta.CombineText)
  371. }
  372. }
  373. inputText := strings.Join(inputTexts, "\n")
  374. return &types.TokenCountMeta{
  375. CombineText: inputText,
  376. }
  377. }
  378. func (r *GeminiBatchEmbeddingRequest) SetModelName(modelName string) {
  379. if modelName != "" {
  380. for _, req := range r.Requests {
  381. req.SetModelName(modelName)
  382. }
  383. }
  384. }
  385. type GeminiEmbeddingResponse struct {
  386. Embedding ContentEmbedding `json:"embedding"`
  387. }
  388. type GeminiBatchEmbeddingResponse struct {
  389. Embeddings []*ContentEmbedding `json:"embeddings"`
  390. }
  391. type ContentEmbedding struct {
  392. Values []float64 `json:"values"`
  393. }