relay_info.go 16 KB

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