relay_info.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. package common
  2. import (
  3. "errors"
  4. "fmt"
  5. "one-api/common"
  6. "one-api/constant"
  7. "one-api/dto"
  8. relayconstant "one-api/relay/constant"
  9. "one-api/types"
  10. "strings"
  11. "time"
  12. "github.com/gin-gonic/gin"
  13. "github.com/gorilla/websocket"
  14. )
  15. type ThinkingContentInfo struct {
  16. IsFirstThinkingContent bool
  17. SendLastThinkingContent bool
  18. HasSentThinkingContent bool
  19. }
  20. const (
  21. LastMessageTypeNone = "none"
  22. LastMessageTypeText = "text"
  23. LastMessageTypeTools = "tools"
  24. LastMessageTypeThinking = "thinking"
  25. )
  26. type ClaudeConvertInfo struct {
  27. LastMessagesType string
  28. Index int
  29. Usage *dto.Usage
  30. FinishReason string
  31. Done bool
  32. }
  33. type RerankerInfo struct {
  34. Documents []any
  35. ReturnDocuments bool
  36. }
  37. type BuildInToolInfo struct {
  38. ToolName string
  39. CallCount int
  40. SearchContextSize string
  41. }
  42. type ResponsesUsageInfo struct {
  43. BuiltInTools map[string]*BuildInToolInfo
  44. }
  45. type ChannelMeta struct {
  46. ChannelType int
  47. ChannelId int
  48. ChannelIsMultiKey bool
  49. ChannelMultiKeyIndex int
  50. ChannelBaseUrl string
  51. ApiType int
  52. ApiVersion string
  53. ApiKey string
  54. Organization string
  55. ChannelCreateTime int64
  56. ParamOverride map[string]interface{}
  57. ChannelSetting dto.ChannelSettings
  58. ChannelOtherSettings dto.ChannelOtherSettings
  59. UpstreamModelName string
  60. IsModelMapped bool
  61. SupportStreamOptions bool // 是否支持流式选项
  62. }
  63. type RelayInfo struct {
  64. TokenId int
  65. TokenKey string
  66. UserId int
  67. UsingGroup string // 使用的分组
  68. UserGroup string // 用户所在分组
  69. TokenUnlimited bool
  70. StartTime time.Time
  71. FirstResponseTime time.Time
  72. isFirstResponse bool
  73. //SendLastReasoningResponse bool
  74. IsStream bool
  75. IsGeminiBatchEmbedding bool
  76. IsPlayground bool
  77. UsePrice bool
  78. RelayMode int
  79. OriginModelName string
  80. RequestURLPath string
  81. PromptTokens int
  82. ShouldIncludeUsage bool
  83. DisablePing bool // 是否禁止向下游发送自定义 Ping
  84. ClientWs *websocket.Conn
  85. TargetWs *websocket.Conn
  86. InputAudioFormat string
  87. OutputAudioFormat string
  88. RealtimeTools []dto.RealTimeTool
  89. IsFirstRequest bool
  90. AudioUsage bool
  91. ReasoningEffort string
  92. UserSetting dto.UserSetting
  93. UserEmail string
  94. UserQuota int
  95. RelayFormat types.RelayFormat
  96. SendResponseCount int
  97. FinalPreConsumedQuota int // 最终预消耗的配额
  98. PriceData types.PriceData
  99. Request dto.Request
  100. ThinkingContentInfo
  101. *ClaudeConvertInfo
  102. *RerankerInfo
  103. *ResponsesUsageInfo
  104. *ChannelMeta
  105. }
  106. func (info *RelayInfo) InitChannelMeta(c *gin.Context) {
  107. channelType := common.GetContextKeyInt(c, constant.ContextKeyChannelType)
  108. paramOverride := common.GetContextKeyStringMap(c, constant.ContextKeyChannelParamOverride)
  109. apiType, _ := common.ChannelType2APIType(channelType)
  110. channelMeta := &ChannelMeta{
  111. ChannelType: channelType,
  112. ChannelId: common.GetContextKeyInt(c, constant.ContextKeyChannelId),
  113. ChannelIsMultiKey: common.GetContextKeyBool(c, constant.ContextKeyChannelIsMultiKey),
  114. ChannelMultiKeyIndex: common.GetContextKeyInt(c, constant.ContextKeyChannelMultiKeyIndex),
  115. ChannelBaseUrl: common.GetContextKeyString(c, constant.ContextKeyChannelBaseUrl),
  116. ApiType: apiType,
  117. ApiVersion: c.GetString("api_version"),
  118. ApiKey: common.GetContextKeyString(c, constant.ContextKeyChannelKey),
  119. Organization: c.GetString("channel_organization"),
  120. ChannelCreateTime: c.GetInt64("channel_create_time"),
  121. ParamOverride: paramOverride,
  122. UpstreamModelName: common.GetContextKeyString(c, constant.ContextKeyOriginalModel),
  123. IsModelMapped: false,
  124. SupportStreamOptions: false,
  125. }
  126. channelSetting, ok := common.GetContextKeyType[dto.ChannelSettings](c, constant.ContextKeyChannelSetting)
  127. if ok {
  128. channelMeta.ChannelSetting = channelSetting
  129. }
  130. channelOtherSettings, ok := common.GetContextKeyType[dto.ChannelOtherSettings](c, constant.ContextKeyChannelOtherSetting)
  131. if ok {
  132. channelMeta.ChannelOtherSettings = channelOtherSettings
  133. }
  134. if streamSupportedChannels[channelMeta.ChannelType] {
  135. channelMeta.SupportStreamOptions = true
  136. }
  137. info.ChannelMeta = channelMeta
  138. }
  139. func (info *RelayInfo) ToString() string {
  140. if info == nil {
  141. return "RelayInfo<nil>"
  142. }
  143. // Basic info
  144. b := &strings.Builder{}
  145. fmt.Fprintf(b, "RelayInfo{ ")
  146. fmt.Fprintf(b, "RelayFormat: %s, ", info.RelayFormat)
  147. fmt.Fprintf(b, "RelayMode: %d, ", info.RelayMode)
  148. fmt.Fprintf(b, "IsStream: %t, ", info.IsStream)
  149. fmt.Fprintf(b, "IsPlayground: %t, ", info.IsPlayground)
  150. fmt.Fprintf(b, "RequestURLPath: %q, ", info.RequestURLPath)
  151. fmt.Fprintf(b, "OriginModelName: %q, ", info.OriginModelName)
  152. fmt.Fprintf(b, "PromptTokens: %d, ", info.PromptTokens)
  153. fmt.Fprintf(b, "ShouldIncludeUsage: %t, ", info.ShouldIncludeUsage)
  154. fmt.Fprintf(b, "DisablePing: %t, ", info.DisablePing)
  155. fmt.Fprintf(b, "SendResponseCount: %d, ", info.SendResponseCount)
  156. fmt.Fprintf(b, "FinalPreConsumedQuota: %d, ", info.FinalPreConsumedQuota)
  157. // User & token info (mask secrets)
  158. fmt.Fprintf(b, "User{ Id: %d, Email: %q, Group: %q, UsingGroup: %q, Quota: %d }, ",
  159. info.UserId, common.MaskEmail(info.UserEmail), info.UserGroup, info.UsingGroup, info.UserQuota)
  160. fmt.Fprintf(b, "Token{ Id: %d, Unlimited: %t, Key: ***masked*** }, ", info.TokenId, info.TokenUnlimited)
  161. // Time info
  162. latencyMs := info.FirstResponseTime.Sub(info.StartTime).Milliseconds()
  163. fmt.Fprintf(b, "Timing{ Start: %s, FirstResponse: %s, LatencyMs: %d }, ",
  164. info.StartTime.Format(time.RFC3339Nano), info.FirstResponseTime.Format(time.RFC3339Nano), latencyMs)
  165. // Audio / realtime
  166. if info.InputAudioFormat != "" || info.OutputAudioFormat != "" || len(info.RealtimeTools) > 0 || info.AudioUsage {
  167. fmt.Fprintf(b, "Realtime{ AudioUsage: %t, InFmt: %q, OutFmt: %q, Tools: %d }, ",
  168. info.AudioUsage, info.InputAudioFormat, info.OutputAudioFormat, len(info.RealtimeTools))
  169. }
  170. // Reasoning
  171. if info.ReasoningEffort != "" {
  172. fmt.Fprintf(b, "ReasoningEffort: %q, ", info.ReasoningEffort)
  173. }
  174. // Price data (non-sensitive)
  175. if info.PriceData.UsePrice {
  176. fmt.Fprintf(b, "PriceData{ %s }, ", info.PriceData.ToSetting())
  177. }
  178. // Channel metadata (mask ApiKey)
  179. if info.ChannelMeta != nil {
  180. cm := info.ChannelMeta
  181. fmt.Fprintf(b, "ChannelMeta{ Type: %d, Id: %d, IsMultiKey: %t, MultiKeyIndex: %d, BaseURL: %q, ApiType: %d, ApiVersion: %q, Organization: %q, CreateTime: %d, UpstreamModelName: %q, IsModelMapped: %t, SupportStreamOptions: %t, ApiKey: ***masked*** }, ",
  182. cm.ChannelType, cm.ChannelId, cm.ChannelIsMultiKey, cm.ChannelMultiKeyIndex, cm.ChannelBaseUrl, cm.ApiType, cm.ApiVersion, cm.Organization, cm.ChannelCreateTime, cm.UpstreamModelName, cm.IsModelMapped, cm.SupportStreamOptions)
  183. }
  184. // Responses usage info (non-sensitive)
  185. if info.ResponsesUsageInfo != nil && len(info.ResponsesUsageInfo.BuiltInTools) > 0 {
  186. fmt.Fprintf(b, "ResponsesTools{ ")
  187. first := true
  188. for name, tool := range info.ResponsesUsageInfo.BuiltInTools {
  189. if !first {
  190. fmt.Fprintf(b, ", ")
  191. }
  192. first = false
  193. if tool != nil {
  194. fmt.Fprintf(b, "%s: calls=%d", name, tool.CallCount)
  195. } else {
  196. fmt.Fprintf(b, "%s: calls=0", name)
  197. }
  198. }
  199. fmt.Fprintf(b, " }, ")
  200. }
  201. fmt.Fprintf(b, "}")
  202. return b.String()
  203. }
  204. // 定义支持流式选项的通道类型
  205. var streamSupportedChannels = map[int]bool{
  206. constant.ChannelTypeOpenAI: true,
  207. constant.ChannelTypeAnthropic: true,
  208. constant.ChannelTypeAws: true,
  209. constant.ChannelTypeGemini: true,
  210. constant.ChannelCloudflare: true,
  211. constant.ChannelTypeAzure: true,
  212. constant.ChannelTypeVolcEngine: true,
  213. constant.ChannelTypeOllama: true,
  214. constant.ChannelTypeXai: true,
  215. constant.ChannelTypeDeepSeek: true,
  216. constant.ChannelTypeBaiduV2: true,
  217. }
  218. func GenRelayInfoWs(c *gin.Context, ws *websocket.Conn) *RelayInfo {
  219. info := genBaseRelayInfo(c, nil)
  220. info.RelayFormat = types.RelayFormatOpenAIRealtime
  221. info.ClientWs = ws
  222. info.InputAudioFormat = "pcm16"
  223. info.OutputAudioFormat = "pcm16"
  224. info.IsFirstRequest = true
  225. return info
  226. }
  227. func GenRelayInfoClaude(c *gin.Context, request dto.Request) *RelayInfo {
  228. info := genBaseRelayInfo(c, request)
  229. info.RelayFormat = types.RelayFormatClaude
  230. info.ShouldIncludeUsage = false
  231. info.ClaudeConvertInfo = &ClaudeConvertInfo{
  232. LastMessagesType: LastMessageTypeNone,
  233. }
  234. return info
  235. }
  236. func GenRelayInfoRerank(c *gin.Context, request *dto.RerankRequest) *RelayInfo {
  237. info := genBaseRelayInfo(c, request)
  238. info.RelayMode = relayconstant.RelayModeRerank
  239. info.RelayFormat = types.RelayFormatRerank
  240. info.RerankerInfo = &RerankerInfo{
  241. Documents: request.Documents,
  242. ReturnDocuments: request.GetReturnDocuments(),
  243. }
  244. return info
  245. }
  246. func GenRelayInfoOpenAIAudio(c *gin.Context, request dto.Request) *RelayInfo {
  247. info := genBaseRelayInfo(c, request)
  248. info.RelayFormat = types.RelayFormatOpenAIAudio
  249. return info
  250. }
  251. func GenRelayInfoEmbedding(c *gin.Context, request dto.Request) *RelayInfo {
  252. info := genBaseRelayInfo(c, request)
  253. info.RelayFormat = types.RelayFormatEmbedding
  254. return info
  255. }
  256. func GenRelayInfoResponses(c *gin.Context, request *dto.OpenAIResponsesRequest) *RelayInfo {
  257. info := genBaseRelayInfo(c, request)
  258. info.RelayMode = relayconstant.RelayModeResponses
  259. info.RelayFormat = types.RelayFormatOpenAIResponses
  260. info.ResponsesUsageInfo = &ResponsesUsageInfo{
  261. BuiltInTools: make(map[string]*BuildInToolInfo),
  262. }
  263. if len(request.Tools) > 0 {
  264. for _, tool := range request.Tools {
  265. toolType := common.Interface2String(tool["type"])
  266. info.ResponsesUsageInfo.BuiltInTools[toolType] = &BuildInToolInfo{
  267. ToolName: toolType,
  268. CallCount: 0,
  269. }
  270. switch toolType {
  271. case dto.BuildInToolWebSearchPreview:
  272. searchContextSize := common.Interface2String(tool["search_context_size"])
  273. if searchContextSize == "" {
  274. searchContextSize = "medium"
  275. }
  276. info.ResponsesUsageInfo.BuiltInTools[toolType].SearchContextSize = searchContextSize
  277. }
  278. }
  279. }
  280. return info
  281. }
  282. func GenRelayInfoGemini(c *gin.Context, request dto.Request) *RelayInfo {
  283. info := genBaseRelayInfo(c, request)
  284. info.RelayFormat = types.RelayFormatGemini
  285. info.ShouldIncludeUsage = false
  286. return info
  287. }
  288. func GenRelayInfoImage(c *gin.Context, request dto.Request) *RelayInfo {
  289. info := genBaseRelayInfo(c, request)
  290. info.RelayFormat = types.RelayFormatOpenAIImage
  291. return info
  292. }
  293. func GenRelayInfoOpenAI(c *gin.Context, request dto.Request) *RelayInfo {
  294. info := genBaseRelayInfo(c, request)
  295. info.RelayFormat = types.RelayFormatOpenAI
  296. return info
  297. }
  298. func genBaseRelayInfo(c *gin.Context, request dto.Request) *RelayInfo {
  299. //channelType := common.GetContextKeyInt(c, constant.ContextKeyChannelType)
  300. //channelId := common.GetContextKeyInt(c, constant.ContextKeyChannelId)
  301. //paramOverride := common.GetContextKeyStringMap(c, constant.ContextKeyChannelParamOverride)
  302. startTime := common.GetContextKeyTime(c, constant.ContextKeyRequestStartTime)
  303. if startTime.IsZero() {
  304. startTime = time.Now()
  305. }
  306. isStream := false
  307. if request != nil {
  308. isStream = request.IsStream(c)
  309. }
  310. // firstResponseTime = time.Now() - 1 second
  311. info := &RelayInfo{
  312. Request: request,
  313. UserId: common.GetContextKeyInt(c, constant.ContextKeyUserId),
  314. UsingGroup: common.GetContextKeyString(c, constant.ContextKeyUsingGroup),
  315. UserGroup: common.GetContextKeyString(c, constant.ContextKeyUserGroup),
  316. UserQuota: common.GetContextKeyInt(c, constant.ContextKeyUserQuota),
  317. UserEmail: common.GetContextKeyString(c, constant.ContextKeyUserEmail),
  318. OriginModelName: common.GetContextKeyString(c, constant.ContextKeyOriginalModel),
  319. PromptTokens: common.GetContextKeyInt(c, constant.ContextKeyPromptTokens),
  320. TokenId: common.GetContextKeyInt(c, constant.ContextKeyTokenId),
  321. TokenKey: common.GetContextKeyString(c, constant.ContextKeyTokenKey),
  322. TokenUnlimited: common.GetContextKeyBool(c, constant.ContextKeyTokenUnlimited),
  323. isFirstResponse: true,
  324. RelayMode: relayconstant.Path2RelayMode(c.Request.URL.Path),
  325. RequestURLPath: c.Request.URL.String(),
  326. IsStream: isStream,
  327. StartTime: startTime,
  328. FirstResponseTime: startTime.Add(-time.Second),
  329. ThinkingContentInfo: ThinkingContentInfo{
  330. IsFirstThinkingContent: true,
  331. SendLastThinkingContent: false,
  332. },
  333. }
  334. if strings.HasPrefix(c.Request.URL.Path, "/pg") {
  335. info.IsPlayground = true
  336. info.RequestURLPath = strings.TrimPrefix(info.RequestURLPath, "/pg")
  337. info.RequestURLPath = "/v1" + info.RequestURLPath
  338. }
  339. userSetting, ok := common.GetContextKeyType[dto.UserSetting](c, constant.ContextKeyUserSetting)
  340. if ok {
  341. info.UserSetting = userSetting
  342. }
  343. return info
  344. }
  345. func GenRelayInfo(c *gin.Context, relayFormat types.RelayFormat, request dto.Request, ws *websocket.Conn) (*RelayInfo, error) {
  346. switch relayFormat {
  347. case types.RelayFormatOpenAI:
  348. return GenRelayInfoOpenAI(c, request), nil
  349. case types.RelayFormatOpenAIAudio:
  350. return GenRelayInfoOpenAIAudio(c, request), nil
  351. case types.RelayFormatOpenAIImage:
  352. return GenRelayInfoImage(c, request), nil
  353. case types.RelayFormatOpenAIRealtime:
  354. return GenRelayInfoWs(c, ws), nil
  355. case types.RelayFormatClaude:
  356. return GenRelayInfoClaude(c, request), nil
  357. case types.RelayFormatRerank:
  358. if request, ok := request.(*dto.RerankRequest); ok {
  359. return GenRelayInfoRerank(c, request), nil
  360. }
  361. return nil, errors.New("request is not a RerankRequest")
  362. case types.RelayFormatGemini:
  363. return GenRelayInfoGemini(c, request), nil
  364. case types.RelayFormatEmbedding:
  365. return GenRelayInfoEmbedding(c, request), nil
  366. case types.RelayFormatOpenAIResponses:
  367. if request, ok := request.(*dto.OpenAIResponsesRequest); ok {
  368. return GenRelayInfoResponses(c, request), nil
  369. }
  370. return nil, errors.New("request is not a OpenAIResponsesRequest")
  371. case types.RelayFormatTask:
  372. return genBaseRelayInfo(c, nil), nil
  373. case types.RelayFormatMjProxy:
  374. return genBaseRelayInfo(c, nil), nil
  375. default:
  376. return nil, errors.New("invalid relay format")
  377. }
  378. }
  379. func (info *RelayInfo) SetPromptTokens(promptTokens int) {
  380. info.PromptTokens = promptTokens
  381. }
  382. func (info *RelayInfo) SetFirstResponseTime() {
  383. if info.isFirstResponse {
  384. info.FirstResponseTime = time.Now()
  385. info.isFirstResponse = false
  386. }
  387. }
  388. func (info *RelayInfo) HasSendResponse() bool {
  389. return info.FirstResponseTime.After(info.StartTime)
  390. }
  391. type TaskRelayInfo struct {
  392. *RelayInfo
  393. Action string
  394. OriginTaskID string
  395. ConsumeQuota bool
  396. }
  397. func GenTaskRelayInfo(c *gin.Context) (*TaskRelayInfo, error) {
  398. relayInfo, err := GenRelayInfo(c, types.RelayFormatTask, nil, nil)
  399. if err != nil {
  400. return nil, err
  401. }
  402. info := &TaskRelayInfo{
  403. RelayInfo: relayInfo,
  404. }
  405. return info, nil
  406. }
  407. type TaskSubmitReq struct {
  408. Prompt string `json:"prompt"`
  409. Model string `json:"model,omitempty"`
  410. Mode string `json:"mode,omitempty"`
  411. Image string `json:"image,omitempty"`
  412. Size string `json:"size,omitempty"`
  413. Duration int `json:"duration,omitempty"`
  414. Metadata map[string]interface{} `json:"metadata,omitempty"`
  415. }
  416. type TaskInfo struct {
  417. Code int `json:"code"`
  418. TaskID string `json:"task_id"`
  419. Status string `json:"status"`
  420. Reason string `json:"reason,omitempty"`
  421. Url string `json:"url,omitempty"`
  422. Progress string `json:"progress,omitempty"`
  423. }