relay_info.go 15 KB

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