relay_gemini_file.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. package controller
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strings"
  6. "github.com/QuantumNous/new-api/common"
  7. "github.com/QuantumNous/new-api/constant"
  8. "github.com/QuantumNous/new-api/logger"
  9. "github.com/QuantumNous/new-api/relay/channel/gemini"
  10. "github.com/gin-gonic/gin"
  11. )
  12. // RelayGeminiFileUpload handles file upload to Gemini File API
  13. func RelayGeminiFileUpload(c *gin.Context) {
  14. // Parse multipart form
  15. form, err := common.ParseMultipartFormReusable(c)
  16. if err != nil {
  17. logger.LogError(c, fmt.Sprintf("failed to parse multipart form: %s", err.Error()))
  18. c.JSON(http.StatusBadRequest, gin.H{
  19. "error": gin.H{
  20. "message": fmt.Sprintf("failed to parse multipart form: %s", err.Error()),
  21. "type": "invalid_request_error",
  22. "code": "invalid_multipart_form",
  23. },
  24. })
  25. return
  26. }
  27. defer form.RemoveAll()
  28. // Get API key from channel context (set by middleware)
  29. apiKey := common.GetContextKeyString(c, constant.ContextKeyChannelKey)
  30. if apiKey == "" {
  31. logger.LogError(c, "API key not found in context")
  32. c.JSON(http.StatusUnauthorized, gin.H{
  33. "error": gin.H{
  34. "message": "API key not found",
  35. "type": "authentication_error",
  36. "code": "invalid_api_key",
  37. },
  38. })
  39. return
  40. }
  41. // Rebuild multipart form for upstream request
  42. body, contentType, err := gemini.RebuildMultipartForm(form)
  43. if err != nil {
  44. logger.LogError(c, fmt.Sprintf("failed to rebuild multipart form: %s", err.Error()))
  45. c.JSON(http.StatusInternalServerError, gin.H{
  46. "error": gin.H{
  47. "message": fmt.Sprintf("failed to rebuild multipart form: %s", err.Error()),
  48. "type": "internal_error",
  49. "code": "form_rebuild_error",
  50. },
  51. })
  52. return
  53. }
  54. // Build upstream URL
  55. url := gemini.BuildGeminiFileURL("/upload/v1beta/files")
  56. // Prepare headers
  57. headers := map[string]string{
  58. "Content-Type": contentType,
  59. "x-goog-api-key": apiKey,
  60. }
  61. // Forward request to Gemini
  62. err = gemini.ForwardGeminiFileRequest(c, http.MethodPost, url, body, headers)
  63. if err != nil {
  64. logger.LogError(c, fmt.Sprintf("failed to forward file upload request: %s", err.Error()))
  65. // Error response already sent by ForwardGeminiFileRequest
  66. return
  67. }
  68. }
  69. // RelayGeminiFileGet retrieves file metadata from Gemini File API
  70. func RelayGeminiFileGet(c *gin.Context) {
  71. // Get file name from URL parameter
  72. fileName := c.Param("name")
  73. if fileName == "" {
  74. c.JSON(http.StatusBadRequest, gin.H{
  75. "error": gin.H{
  76. "message": "file name is required",
  77. "type": "invalid_request_error",
  78. "code": "missing_file_name",
  79. },
  80. })
  81. return
  82. }
  83. // Get API key from channel context
  84. apiKey := common.GetContextKeyString(c, constant.ContextKeyChannelKey)
  85. if apiKey == "" {
  86. logger.LogError(c, "API key not found in context")
  87. c.JSON(http.StatusUnauthorized, gin.H{
  88. "error": gin.H{
  89. "message": "API key not found",
  90. "type": "authentication_error",
  91. "code": "invalid_api_key",
  92. },
  93. })
  94. return
  95. }
  96. // Build upstream URL - fileName already includes "files/" prefix from route
  97. url := gemini.BuildGeminiFileURL(fmt.Sprintf("/v1beta/%s", fileName))
  98. // Prepare headers
  99. headers := map[string]string{
  100. "x-goog-api-key": apiKey,
  101. }
  102. // Forward request to Gemini
  103. err := gemini.ForwardGeminiFileRequest(c, http.MethodGet, url, nil, headers)
  104. if err != nil {
  105. logger.LogError(c, fmt.Sprintf("failed to forward file get request: %s", err.Error()))
  106. return
  107. }
  108. }
  109. // RelayGeminiFileDelete deletes a file from Gemini File API
  110. func RelayGeminiFileDelete(c *gin.Context) {
  111. // Get file name from URL parameter
  112. fileName := c.Param("name")
  113. if fileName == "" {
  114. c.JSON(http.StatusBadRequest, gin.H{
  115. "error": gin.H{
  116. "message": "file name is required",
  117. "type": "invalid_request_error",
  118. "code": "missing_file_name",
  119. },
  120. })
  121. return
  122. }
  123. // Get API key from channel context
  124. apiKey := common.GetContextKeyString(c, constant.ContextKeyChannelKey)
  125. if apiKey == "" {
  126. logger.LogError(c, "API key not found in context")
  127. c.JSON(http.StatusUnauthorized, gin.H{
  128. "error": gin.H{
  129. "message": "API key not found",
  130. "type": "authentication_error",
  131. "code": "invalid_api_key",
  132. },
  133. })
  134. return
  135. }
  136. // Build upstream URL - fileName already includes "files/" prefix from route
  137. url := gemini.BuildGeminiFileURL(fmt.Sprintf("/v1beta/%s", fileName))
  138. // Prepare headers
  139. headers := map[string]string{
  140. "x-goog-api-key": apiKey,
  141. }
  142. // Forward request to Gemini
  143. err := gemini.ForwardGeminiFileRequest(c, http.MethodDelete, url, nil, headers)
  144. if err != nil {
  145. logger.LogError(c, fmt.Sprintf("failed to forward file delete request: %s", err.Error()))
  146. return
  147. }
  148. }
  149. // RelayGeminiFileList lists files from Gemini File API
  150. func RelayGeminiFileList(c *gin.Context) {
  151. // Get API key from channel context
  152. apiKey := common.GetContextKeyString(c, constant.ContextKeyChannelKey)
  153. if apiKey == "" {
  154. logger.LogError(c, "API key not found in context")
  155. c.JSON(http.StatusUnauthorized, gin.H{
  156. "error": gin.H{
  157. "message": "API key not found",
  158. "type": "authentication_error",
  159. "code": "invalid_api_key",
  160. },
  161. })
  162. return
  163. }
  164. // Build upstream URL with query parameters
  165. url := gemini.BuildGeminiFileURL("/v1beta/files")
  166. // Add query parameters if present
  167. queryParams := []string{}
  168. if pageSize := c.Query("pageSize"); pageSize != "" {
  169. queryParams = append(queryParams, fmt.Sprintf("pageSize=%s", pageSize))
  170. }
  171. if pageToken := c.Query("pageToken"); pageToken != "" {
  172. queryParams = append(queryParams, fmt.Sprintf("pageToken=%s", pageToken))
  173. }
  174. if len(queryParams) > 0 {
  175. url = fmt.Sprintf("%s?%s", url, strings.Join(queryParams, "&"))
  176. }
  177. // Prepare headers
  178. headers := map[string]string{
  179. "x-goog-api-key": apiKey,
  180. }
  181. // Forward request to Gemini
  182. err := gemini.ForwardGeminiFileRequest(c, http.MethodGet, url, nil, headers)
  183. if err != nil {
  184. logger.LogError(c, fmt.Sprintf("failed to forward file list request: %s", err.Error()))
  185. return
  186. }
  187. }