Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

280 linhas
9.3 KiB

  1. package openai
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "strings"
  6. "github.com/QuantumNous/new-api/common"
  7. "github.com/QuantumNous/new-api/dto"
  8. "github.com/QuantumNous/new-api/logger"
  9. relaycommon "github.com/QuantumNous/new-api/relay/common"
  10. relayconstant "github.com/QuantumNous/new-api/relay/constant"
  11. "github.com/QuantumNous/new-api/relay/helper"
  12. "github.com/QuantumNous/new-api/service"
  13. "github.com/QuantumNous/new-api/types"
  14. "github.com/samber/lo"
  15. "github.com/gin-gonic/gin"
  16. )
  17. // 辅助函数
  18. func streamOpenAIErrorFromData(data string, statusCode int) *types.NewAPIError {
  19. var errorResp dto.GeneralErrorResponse
  20. if err := common.UnmarshalJsonStr(data, &errorResp); err != nil {
  21. return nil
  22. }
  23. openaiErr := errorResp.TryToOpenAIError()
  24. if openaiErr == nil {
  25. return nil
  26. }
  27. if statusCode < 100 || statusCode > 599 {
  28. statusCode = http.StatusInternalServerError
  29. }
  30. apiErr := types.WithOpenAIError(*openaiErr, statusCode)
  31. apiErr.UpstreamBody = service.TruncateBody(data)
  32. return apiErr
  33. }
  34. func HandleStreamFormat(c *gin.Context, info *relaycommon.RelayInfo, data string, forceFormat bool, thinkToContent bool) error {
  35. info.SendResponseCount++
  36. switch info.RelayFormat {
  37. case types.RelayFormatOpenAI:
  38. return sendStreamData(c, info, data, forceFormat, thinkToContent)
  39. case types.RelayFormatClaude:
  40. return handleClaudeFormat(c, data, info)
  41. case types.RelayFormatGemini:
  42. return handleGeminiFormat(c, data, info)
  43. }
  44. return nil
  45. }
  46. func handleClaudeFormat(c *gin.Context, data string, info *relaycommon.RelayInfo) error {
  47. var streamResponse dto.ChatCompletionsStreamResponse
  48. if err := common.Unmarshal(common.StringToByteSlice(data), &streamResponse); err != nil {
  49. return err
  50. }
  51. if streamResponse.Usage != nil {
  52. info.ClaudeConvertInfo.Usage = streamResponse.Usage
  53. }
  54. claudeResponses := service.StreamResponseOpenAI2Claude(&streamResponse, info)
  55. for _, resp := range claudeResponses {
  56. helper.ClaudeData(c, *resp)
  57. }
  58. return nil
  59. }
  60. func handleGeminiFormat(c *gin.Context, data string, info *relaycommon.RelayInfo) error {
  61. var streamResponse dto.ChatCompletionsStreamResponse
  62. if err := common.Unmarshal(common.StringToByteSlice(data), &streamResponse); err != nil {
  63. logger.LogError(c, "failed to unmarshal stream response: "+err.Error())
  64. return err
  65. }
  66. geminiResponse := service.StreamResponseOpenAI2Gemini(&streamResponse, info)
  67. // 如果返回 nil,表示没有实际内容,跳过发送
  68. if geminiResponse == nil {
  69. return nil
  70. }
  71. geminiResponseStr, err := common.Marshal(geminiResponse)
  72. if err != nil {
  73. logger.LogError(c, "failed to marshal gemini response: "+err.Error())
  74. return err
  75. }
  76. // send gemini format response
  77. c.Render(-1, common.CustomEvent{Data: "data: " + string(geminiResponseStr)})
  78. _ = helper.FlushWriter(c)
  79. return nil
  80. }
  81. func ProcessStreamResponse(streamResponse dto.ChatCompletionsStreamResponse, responseTextBuilder *strings.Builder, toolCount *int) error {
  82. for _, choice := range streamResponse.Choices {
  83. responseTextBuilder.WriteString(choice.Delta.GetContentString())
  84. responseTextBuilder.WriteString(choice.Delta.GetReasoningContent())
  85. if choice.Delta.ToolCalls != nil {
  86. if len(choice.Delta.ToolCalls) > *toolCount {
  87. *toolCount = len(choice.Delta.ToolCalls)
  88. }
  89. for _, tool := range choice.Delta.ToolCalls {
  90. responseTextBuilder.WriteString(tool.Function.Name)
  91. responseTextBuilder.WriteString(tool.Function.Arguments)
  92. }
  93. }
  94. }
  95. return nil
  96. }
  97. func processTokens(relayMode int, streamItems []string, responseTextBuilder *strings.Builder, toolCount *int) error {
  98. streamResp := "[" + strings.Join(streamItems, ",") + "]"
  99. switch relayMode {
  100. case relayconstant.RelayModeChatCompletions:
  101. return processChatCompletions(streamResp, streamItems, responseTextBuilder, toolCount)
  102. case relayconstant.RelayModeCompletions:
  103. return processCompletions(streamResp, streamItems, responseTextBuilder)
  104. }
  105. return nil
  106. }
  107. func processChatCompletions(streamResp string, streamItems []string, responseTextBuilder *strings.Builder, toolCount *int) error {
  108. var streamResponses []dto.ChatCompletionsStreamResponse
  109. if err := json.Unmarshal(common.StringToByteSlice(streamResp), &streamResponses); err != nil {
  110. // 一次性解析失败,逐个解析
  111. common.SysLog("error unmarshalling stream response: " + err.Error())
  112. for _, item := range streamItems {
  113. var streamResponse dto.ChatCompletionsStreamResponse
  114. if err := json.Unmarshal(common.StringToByteSlice(item), &streamResponse); err != nil {
  115. return err
  116. }
  117. if err := ProcessStreamResponse(streamResponse, responseTextBuilder, toolCount); err != nil {
  118. common.SysLog("error processing stream response: " + err.Error())
  119. }
  120. }
  121. return nil
  122. }
  123. // 批量处理所有响应
  124. for _, streamResponse := range streamResponses {
  125. for _, choice := range streamResponse.Choices {
  126. responseTextBuilder.WriteString(choice.Delta.GetContentString())
  127. responseTextBuilder.WriteString(choice.Delta.GetReasoningContent())
  128. if choice.Delta.ToolCalls != nil {
  129. if len(choice.Delta.ToolCalls) > *toolCount {
  130. *toolCount = len(choice.Delta.ToolCalls)
  131. }
  132. for _, tool := range choice.Delta.ToolCalls {
  133. responseTextBuilder.WriteString(tool.Function.Name)
  134. responseTextBuilder.WriteString(tool.Function.Arguments)
  135. }
  136. }
  137. }
  138. }
  139. return nil
  140. }
  141. func processCompletions(streamResp string, streamItems []string, responseTextBuilder *strings.Builder) error {
  142. var streamResponses []dto.CompletionsStreamResponse
  143. if err := json.Unmarshal(common.StringToByteSlice(streamResp), &streamResponses); err != nil {
  144. // 一次性解析失败,逐个解析
  145. common.SysLog("error unmarshalling stream response: " + err.Error())
  146. for _, item := range streamItems {
  147. var streamResponse dto.CompletionsStreamResponse
  148. if err := json.Unmarshal(common.StringToByteSlice(item), &streamResponse); err != nil {
  149. continue
  150. }
  151. for _, choice := range streamResponse.Choices {
  152. responseTextBuilder.WriteString(choice.Text)
  153. }
  154. }
  155. return nil
  156. }
  157. // 批量处理所有响应
  158. for _, streamResponse := range streamResponses {
  159. for _, choice := range streamResponse.Choices {
  160. responseTextBuilder.WriteString(choice.Text)
  161. }
  162. }
  163. return nil
  164. }
  165. func handleLastResponse(lastStreamData string, responseId *string, createAt *int64,
  166. systemFingerprint *string, model *string, usage **dto.Usage,
  167. containStreamUsage *bool, info *relaycommon.RelayInfo,
  168. shouldSendLastResp *bool) error {
  169. var lastStreamResponse dto.ChatCompletionsStreamResponse
  170. if err := common.Unmarshal(common.StringToByteSlice(lastStreamData), &lastStreamResponse); err != nil {
  171. return err
  172. }
  173. *responseId = lastStreamResponse.Id
  174. *createAt = lastStreamResponse.Created
  175. *systemFingerprint = lastStreamResponse.GetSystemFingerprint()
  176. *model = lastStreamResponse.Model
  177. if service.ValidUsage(lastStreamResponse.Usage) {
  178. *containStreamUsage = true
  179. *usage = lastStreamResponse.Usage
  180. if !info.ShouldIncludeUsage {
  181. *shouldSendLastResp = lo.SomeBy(lastStreamResponse.Choices, func(choice dto.ChatCompletionsStreamResponseChoice) bool {
  182. return choice.Delta.GetContentString() != "" || choice.Delta.GetReasoningContent() != ""
  183. })
  184. }
  185. }
  186. return nil
  187. }
  188. func HandleFinalResponse(c *gin.Context, info *relaycommon.RelayInfo, lastStreamData string,
  189. responseId string, createAt int64, model string, systemFingerprint string,
  190. usage *dto.Usage, containStreamUsage bool) {
  191. switch info.RelayFormat {
  192. case types.RelayFormatOpenAI:
  193. if info.ShouldIncludeUsage && !containStreamUsage {
  194. response := helper.GenerateFinalUsageResponse(responseId, createAt, model, *usage)
  195. response.SetSystemFingerprint(systemFingerprint)
  196. helper.ObjectData(c, response)
  197. }
  198. helper.Done(c)
  199. case types.RelayFormatClaude:
  200. var streamResponse dto.ChatCompletionsStreamResponse
  201. if err := common.Unmarshal(common.StringToByteSlice(lastStreamData), &streamResponse); err != nil {
  202. common.SysLog("error unmarshalling stream response: " + err.Error())
  203. return
  204. }
  205. info.ClaudeConvertInfo.Usage = usage
  206. claudeResponses := service.StreamResponseOpenAI2Claude(&streamResponse, info)
  207. for _, resp := range claudeResponses {
  208. _ = helper.ClaudeData(c, *resp)
  209. }
  210. info.ClaudeConvertInfo.Done = true
  211. case types.RelayFormatGemini:
  212. var streamResponse dto.ChatCompletionsStreamResponse
  213. if err := common.Unmarshal(common.StringToByteSlice(lastStreamData), &streamResponse); err != nil {
  214. common.SysLog("error unmarshalling stream response: " + err.Error())
  215. return
  216. }
  217. // 这里处理的是 openai 最后一个流响应,其 delta 为空,有 finish_reason 字段
  218. // 因此相比较于 google 官方的流响应,由 openai 转换而来会多一个 parts 为空,finishReason 为 STOP 的响应
  219. // 而包含最后一段文本输出的响应(倒数第二个)的 finishReason 为 null
  220. // 暂不知是否有程序会不兼容。
  221. geminiResponse := service.StreamResponseOpenAI2Gemini(&streamResponse, info)
  222. // openai 流响应开头的空数据
  223. if geminiResponse == nil {
  224. return
  225. }
  226. geminiResponseStr, err := common.Marshal(geminiResponse)
  227. if err != nil {
  228. common.SysLog("error marshalling gemini response: " + err.Error())
  229. return
  230. }
  231. // 发送最终的 Gemini 响应
  232. c.Render(-1, common.CustomEvent{Data: "data: " + string(geminiResponseStr)})
  233. _ = helper.FlushWriter(c)
  234. }
  235. }
  236. func sendResponsesStreamData(c *gin.Context, streamResponse dto.ResponsesStreamResponse, data string) {
  237. if data == "" {
  238. return
  239. }
  240. helper.ResponseChunkData(c, streamResponse, data)
  241. }