relay_info.go 15 KB

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