relay_info.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  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.SupportStreamOptions = false
  261. info.ResponsesUsageInfo = &ResponsesUsageInfo{
  262. BuiltInTools: make(map[string]*BuildInToolInfo),
  263. }
  264. if len(request.Tools) > 0 {
  265. for _, tool := range request.Tools {
  266. toolType := common.Interface2String(tool["type"])
  267. info.ResponsesUsageInfo.BuiltInTools[toolType] = &BuildInToolInfo{
  268. ToolName: toolType,
  269. CallCount: 0,
  270. }
  271. switch toolType {
  272. case dto.BuildInToolWebSearchPreview:
  273. searchContextSize := common.Interface2String(tool["search_context_size"])
  274. if searchContextSize == "" {
  275. searchContextSize = "medium"
  276. }
  277. info.ResponsesUsageInfo.BuiltInTools[toolType].SearchContextSize = searchContextSize
  278. }
  279. }
  280. }
  281. return info
  282. }
  283. func GenRelayInfoGemini(c *gin.Context, request dto.Request) *RelayInfo {
  284. info := genBaseRelayInfo(c, request)
  285. info.RelayFormat = types.RelayFormatGemini
  286. info.ShouldIncludeUsage = false
  287. return info
  288. }
  289. func GenRelayInfoImage(c *gin.Context, request dto.Request) *RelayInfo {
  290. info := genBaseRelayInfo(c, request)
  291. info.RelayFormat = types.RelayFormatOpenAIImage
  292. return info
  293. }
  294. func GenRelayInfoOpenAI(c *gin.Context, request dto.Request) *RelayInfo {
  295. info := genBaseRelayInfo(c, request)
  296. info.RelayFormat = types.RelayFormatOpenAI
  297. return info
  298. }
  299. func genBaseRelayInfo(c *gin.Context, request dto.Request) *RelayInfo {
  300. //channelType := common.GetContextKeyInt(c, constant.ContextKeyChannelType)
  301. //channelId := common.GetContextKeyInt(c, constant.ContextKeyChannelId)
  302. //paramOverride := common.GetContextKeyStringMap(c, constant.ContextKeyChannelParamOverride)
  303. startTime := common.GetContextKeyTime(c, constant.ContextKeyRequestStartTime)
  304. if startTime.IsZero() {
  305. startTime = time.Now()
  306. }
  307. isStream := false
  308. if request != nil {
  309. isStream = request.IsStream(c)
  310. }
  311. // firstResponseTime = time.Now() - 1 second
  312. info := &RelayInfo{
  313. Request: request,
  314. UserId: common.GetContextKeyInt(c, constant.ContextKeyUserId),
  315. UsingGroup: common.GetContextKeyString(c, constant.ContextKeyUsingGroup),
  316. UserGroup: common.GetContextKeyString(c, constant.ContextKeyUserGroup),
  317. UserQuota: common.GetContextKeyInt(c, constant.ContextKeyUserQuota),
  318. UserEmail: common.GetContextKeyString(c, constant.ContextKeyUserEmail),
  319. OriginModelName: common.GetContextKeyString(c, constant.ContextKeyOriginalModel),
  320. PromptTokens: common.GetContextKeyInt(c, constant.ContextKeyPromptTokens),
  321. TokenId: common.GetContextKeyInt(c, constant.ContextKeyTokenId),
  322. TokenKey: common.GetContextKeyString(c, constant.ContextKeyTokenKey),
  323. TokenUnlimited: common.GetContextKeyBool(c, constant.ContextKeyTokenUnlimited),
  324. isFirstResponse: true,
  325. RelayMode: relayconstant.Path2RelayMode(c.Request.URL.Path),
  326. RequestURLPath: c.Request.URL.String(),
  327. IsStream: isStream,
  328. StartTime: startTime,
  329. FirstResponseTime: startTime.Add(-time.Second),
  330. ThinkingContentInfo: ThinkingContentInfo{
  331. IsFirstThinkingContent: true,
  332. SendLastThinkingContent: false,
  333. },
  334. }
  335. if strings.HasPrefix(c.Request.URL.Path, "/pg") {
  336. info.IsPlayground = true
  337. info.RequestURLPath = strings.TrimPrefix(info.RequestURLPath, "/pg")
  338. info.RequestURLPath = "/v1" + info.RequestURLPath
  339. }
  340. userSetting, ok := common.GetContextKeyType[dto.UserSetting](c, constant.ContextKeyUserSetting)
  341. if ok {
  342. info.UserSetting = userSetting
  343. }
  344. return info
  345. }
  346. func GenRelayInfo(c *gin.Context, relayFormat types.RelayFormat, request dto.Request, ws *websocket.Conn) (*RelayInfo, error) {
  347. switch relayFormat {
  348. case types.RelayFormatOpenAI:
  349. return GenRelayInfoOpenAI(c, request), nil
  350. case types.RelayFormatOpenAIAudio:
  351. return GenRelayInfoOpenAIAudio(c, request), nil
  352. case types.RelayFormatOpenAIImage:
  353. return GenRelayInfoImage(c, request), nil
  354. case types.RelayFormatOpenAIRealtime:
  355. return GenRelayInfoWs(c, ws), nil
  356. case types.RelayFormatClaude:
  357. return GenRelayInfoClaude(c, request), nil
  358. case types.RelayFormatRerank:
  359. if request, ok := request.(*dto.RerankRequest); ok {
  360. return GenRelayInfoRerank(c, request), nil
  361. }
  362. return nil, errors.New("request is not a RerankRequest")
  363. case types.RelayFormatGemini:
  364. return GenRelayInfoGemini(c, request), nil
  365. case types.RelayFormatEmbedding:
  366. return GenRelayInfoEmbedding(c, request), nil
  367. case types.RelayFormatOpenAIResponses:
  368. if request, ok := request.(*dto.OpenAIResponsesRequest); ok {
  369. return GenRelayInfoResponses(c, request), nil
  370. }
  371. return nil, errors.New("request is not a OpenAIResponsesRequest")
  372. case types.RelayFormatTask:
  373. return genBaseRelayInfo(c, nil), nil
  374. case types.RelayFormatMjProxy:
  375. return genBaseRelayInfo(c, nil), nil
  376. default:
  377. return nil, errors.New("invalid relay format")
  378. }
  379. }
  380. func (info *RelayInfo) SetPromptTokens(promptTokens int) {
  381. info.PromptTokens = promptTokens
  382. }
  383. func (info *RelayInfo) SetFirstResponseTime() {
  384. if info.isFirstResponse {
  385. info.FirstResponseTime = time.Now()
  386. info.isFirstResponse = false
  387. }
  388. }
  389. func (info *RelayInfo) HasSendResponse() bool {
  390. return info.FirstResponseTime.After(info.StartTime)
  391. }
  392. type TaskRelayInfo struct {
  393. *RelayInfo
  394. Action string
  395. OriginTaskID string
  396. ConsumeQuota bool
  397. }
  398. func GenTaskRelayInfo(c *gin.Context) (*TaskRelayInfo, error) {
  399. relayInfo, err := GenRelayInfo(c, types.RelayFormatTask, nil, nil)
  400. if err != nil {
  401. return nil, err
  402. }
  403. info := &TaskRelayInfo{
  404. RelayInfo: relayInfo,
  405. }
  406. return info, nil
  407. }
  408. type TaskSubmitReq struct {
  409. Prompt string `json:"prompt"`
  410. Model string `json:"model,omitempty"`
  411. Mode string `json:"mode,omitempty"`
  412. Image string `json:"image,omitempty"`
  413. Size string `json:"size,omitempty"`
  414. Duration int `json:"duration,omitempty"`
  415. Metadata map[string]interface{} `json:"metadata,omitempty"`
  416. }
  417. type TaskInfo struct {
  418. Code int `json:"code"`
  419. TaskID string `json:"task_id"`
  420. Status string `json:"status"`
  421. Reason string `json:"reason,omitempty"`
  422. Url string `json:"url,omitempty"`
  423. Progress string `json:"progress,omitempty"`
  424. }