relay_info.go 16 KB

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