relay_info.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. package common
  2. import (
  3. "errors"
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. "time"
  8. "github.com/QuantumNous/new-api/common"
  9. "github.com/QuantumNous/new-api/constant"
  10. "github.com/QuantumNous/new-api/dto"
  11. relayconstant "github.com/QuantumNous/new-api/relay/constant"
  12. "github.com/QuantumNous/new-api/types"
  13. "github.com/gin-gonic/gin"
  14. "github.com/gorilla/websocket"
  15. )
  16. type ThinkingContentInfo struct {
  17. IsFirstThinkingContent bool
  18. SendLastThinkingContent bool
  19. HasSentThinkingContent bool
  20. }
  21. const (
  22. LastMessageTypeNone = "none"
  23. LastMessageTypeText = "text"
  24. LastMessageTypeTools = "tools"
  25. LastMessageTypeThinking = "thinking"
  26. )
  27. type ClaudeConvertInfo struct {
  28. LastMessagesType string
  29. Index int
  30. Usage *dto.Usage
  31. FinishReason string
  32. Done bool
  33. }
  34. type RerankerInfo struct {
  35. Documents []any
  36. ReturnDocuments bool
  37. }
  38. type BuildInToolInfo struct {
  39. ToolName string
  40. CallCount int
  41. SearchContextSize string
  42. }
  43. type ResponsesUsageInfo struct {
  44. BuiltInTools map[string]*BuildInToolInfo
  45. }
  46. type ChannelMeta struct {
  47. ChannelType int
  48. ChannelId int
  49. ChannelIsMultiKey bool
  50. ChannelMultiKeyIndex int
  51. ChannelBaseUrl string
  52. ApiType int
  53. ApiVersion string
  54. ApiKey string
  55. Organization string
  56. ChannelCreateTime int64
  57. ParamOverride map[string]interface{}
  58. HeadersOverride map[string]interface{}
  59. ChannelSetting dto.ChannelSettings
  60. ChannelOtherSettings dto.ChannelOtherSettings
  61. UpstreamModelName string
  62. IsModelMapped bool
  63. SupportStreamOptions bool // 是否支持流式选项
  64. }
  65. type RelayInfo struct {
  66. TokenId int
  67. TokenKey string
  68. UserId int
  69. UsingGroup string // 使用的分组
  70. UserGroup string // 用户所在分组
  71. TokenUnlimited bool
  72. StartTime time.Time
  73. FirstResponseTime time.Time
  74. isFirstResponse bool
  75. //SendLastReasoningResponse bool
  76. IsStream bool
  77. IsGeminiBatchEmbedding bool
  78. IsPlayground bool
  79. UsePrice bool
  80. RelayMode int
  81. OriginModelName string
  82. RequestURLPath string
  83. PromptTokens int
  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. IsClaudeBetaQuery bool // /v1/messages?beta=true
  101. PriceData types.PriceData
  102. Request dto.Request
  103. ThinkingContentInfo
  104. *ClaudeConvertInfo
  105. *RerankerInfo
  106. *ResponsesUsageInfo
  107. *ChannelMeta
  108. *TaskRelayInfo
  109. }
  110. func (info *RelayInfo) InitChannelMeta(c *gin.Context) {
  111. channelType := common.GetContextKeyInt(c, constant.ContextKeyChannelType)
  112. paramOverride := common.GetContextKeyStringMap(c, constant.ContextKeyChannelParamOverride)
  113. headerOverride := common.GetContextKeyStringMap(c, constant.ContextKeyChannelHeaderOverride)
  114. apiType, _ := common.ChannelType2APIType(channelType)
  115. channelMeta := &ChannelMeta{
  116. ChannelType: channelType,
  117. ChannelId: common.GetContextKeyInt(c, constant.ContextKeyChannelId),
  118. ChannelIsMultiKey: common.GetContextKeyBool(c, constant.ContextKeyChannelIsMultiKey),
  119. ChannelMultiKeyIndex: common.GetContextKeyInt(c, constant.ContextKeyChannelMultiKeyIndex),
  120. ChannelBaseUrl: common.GetContextKeyString(c, constant.ContextKeyChannelBaseUrl),
  121. ApiType: apiType,
  122. ApiVersion: c.GetString("api_version"),
  123. ApiKey: common.GetContextKeyString(c, constant.ContextKeyChannelKey),
  124. Organization: c.GetString("channel_organization"),
  125. ChannelCreateTime: c.GetInt64("channel_create_time"),
  126. ParamOverride: paramOverride,
  127. HeadersOverride: headerOverride,
  128. UpstreamModelName: common.GetContextKeyString(c, constant.ContextKeyOriginalModel),
  129. IsModelMapped: false,
  130. SupportStreamOptions: false,
  131. }
  132. if channelType == constant.ChannelTypeAzure {
  133. channelMeta.ApiVersion = GetAPIVersion(c)
  134. }
  135. if channelType == constant.ChannelTypeVertexAi {
  136. channelMeta.ApiVersion = c.GetString("region")
  137. }
  138. channelSetting, ok := common.GetContextKeyType[dto.ChannelSettings](c, constant.ContextKeyChannelSetting)
  139. if ok {
  140. channelMeta.ChannelSetting = channelSetting
  141. }
  142. channelOtherSettings, ok := common.GetContextKeyType[dto.ChannelOtherSettings](c, constant.ContextKeyChannelOtherSetting)
  143. if ok {
  144. channelMeta.ChannelOtherSettings = channelOtherSettings
  145. }
  146. if streamSupportedChannels[channelMeta.ChannelType] {
  147. channelMeta.SupportStreamOptions = true
  148. }
  149. info.ChannelMeta = channelMeta
  150. // reset some fields based on channel meta
  151. // 重置某些字段,例如模型名称等
  152. if info.Request != nil {
  153. info.Request.SetModelName(info.OriginModelName)
  154. }
  155. }
  156. func (info *RelayInfo) ToString() string {
  157. if info == nil {
  158. return "RelayInfo<nil>"
  159. }
  160. // Basic info
  161. b := &strings.Builder{}
  162. fmt.Fprintf(b, "RelayInfo{ ")
  163. fmt.Fprintf(b, "RelayFormat: %s, ", info.RelayFormat)
  164. fmt.Fprintf(b, "RelayMode: %d, ", info.RelayMode)
  165. fmt.Fprintf(b, "IsStream: %t, ", info.IsStream)
  166. fmt.Fprintf(b, "IsPlayground: %t, ", info.IsPlayground)
  167. fmt.Fprintf(b, "RequestURLPath: %q, ", info.RequestURLPath)
  168. fmt.Fprintf(b, "OriginModelName: %q, ", info.OriginModelName)
  169. fmt.Fprintf(b, "PromptTokens: %d, ", info.PromptTokens)
  170. fmt.Fprintf(b, "ShouldIncludeUsage: %t, ", info.ShouldIncludeUsage)
  171. fmt.Fprintf(b, "DisablePing: %t, ", info.DisablePing)
  172. fmt.Fprintf(b, "SendResponseCount: %d, ", info.SendResponseCount)
  173. fmt.Fprintf(b, "FinalPreConsumedQuota: %d, ", info.FinalPreConsumedQuota)
  174. // User & token info (mask secrets)
  175. fmt.Fprintf(b, "User{ Id: %d, Email: %q, Group: %q, UsingGroup: %q, Quota: %d }, ",
  176. info.UserId, common.MaskEmail(info.UserEmail), info.UserGroup, info.UsingGroup, info.UserQuota)
  177. fmt.Fprintf(b, "Token{ Id: %d, Unlimited: %t, Key: ***masked*** }, ", info.TokenId, info.TokenUnlimited)
  178. // Time info
  179. latencyMs := info.FirstResponseTime.Sub(info.StartTime).Milliseconds()
  180. fmt.Fprintf(b, "Timing{ Start: %s, FirstResponse: %s, LatencyMs: %d }, ",
  181. info.StartTime.Format(time.RFC3339Nano), info.FirstResponseTime.Format(time.RFC3339Nano), latencyMs)
  182. // Audio / realtime
  183. if info.InputAudioFormat != "" || info.OutputAudioFormat != "" || len(info.RealtimeTools) > 0 || info.AudioUsage {
  184. fmt.Fprintf(b, "Realtime{ AudioUsage: %t, InFmt: %q, OutFmt: %q, Tools: %d }, ",
  185. info.AudioUsage, info.InputAudioFormat, info.OutputAudioFormat, len(info.RealtimeTools))
  186. }
  187. // Reasoning
  188. if info.ReasoningEffort != "" {
  189. fmt.Fprintf(b, "ReasoningEffort: %q, ", info.ReasoningEffort)
  190. }
  191. // Price data (non-sensitive)
  192. if info.PriceData.UsePrice {
  193. fmt.Fprintf(b, "PriceData{ %s }, ", info.PriceData.ToSetting())
  194. }
  195. // Channel metadata (mask ApiKey)
  196. if info.ChannelMeta != nil {
  197. cm := info.ChannelMeta
  198. 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*** }, ",
  199. cm.ChannelType, cm.ChannelId, cm.ChannelIsMultiKey, cm.ChannelMultiKeyIndex, cm.ChannelBaseUrl, cm.ApiType, cm.ApiVersion, cm.Organization, cm.ChannelCreateTime, cm.UpstreamModelName, cm.IsModelMapped, cm.SupportStreamOptions)
  200. }
  201. // Responses usage info (non-sensitive)
  202. if info.ResponsesUsageInfo != nil && len(info.ResponsesUsageInfo.BuiltInTools) > 0 {
  203. fmt.Fprintf(b, "ResponsesTools{ ")
  204. first := true
  205. for name, tool := range info.ResponsesUsageInfo.BuiltInTools {
  206. if !first {
  207. fmt.Fprintf(b, ", ")
  208. }
  209. first = false
  210. if tool != nil {
  211. fmt.Fprintf(b, "%s: calls=%d", name, tool.CallCount)
  212. } else {
  213. fmt.Fprintf(b, "%s: calls=0", name)
  214. }
  215. }
  216. fmt.Fprintf(b, " }, ")
  217. }
  218. fmt.Fprintf(b, "}")
  219. return b.String()
  220. }
  221. // 定义支持流式选项的通道类型
  222. var streamSupportedChannels = map[int]bool{
  223. constant.ChannelTypeOpenAI: true,
  224. constant.ChannelTypeAnthropic: true,
  225. constant.ChannelTypeAws: true,
  226. constant.ChannelTypeGemini: true,
  227. constant.ChannelCloudflare: true,
  228. constant.ChannelTypeAzure: true,
  229. constant.ChannelTypeVolcEngine: true,
  230. constant.ChannelTypeOllama: true,
  231. constant.ChannelTypeXai: true,
  232. constant.ChannelTypeDeepSeek: true,
  233. constant.ChannelTypeBaiduV2: true,
  234. constant.ChannelTypeZhipu_v4: true,
  235. }
  236. func GenRelayInfoWs(c *gin.Context, ws *websocket.Conn) *RelayInfo {
  237. info := genBaseRelayInfo(c, nil)
  238. info.RelayFormat = types.RelayFormatOpenAIRealtime
  239. info.ClientWs = ws
  240. info.InputAudioFormat = "pcm16"
  241. info.OutputAudioFormat = "pcm16"
  242. info.IsFirstRequest = true
  243. return info
  244. }
  245. func GenRelayInfoClaude(c *gin.Context, request dto.Request) *RelayInfo {
  246. info := genBaseRelayInfo(c, request)
  247. info.RelayFormat = types.RelayFormatClaude
  248. info.ShouldIncludeUsage = false
  249. info.ClaudeConvertInfo = &ClaudeConvertInfo{
  250. LastMessagesType: LastMessageTypeNone,
  251. }
  252. if c.Query("beta") == "true" {
  253. info.IsClaudeBetaQuery = true
  254. }
  255. return info
  256. }
  257. func GenRelayInfoRerank(c *gin.Context, request *dto.RerankRequest) *RelayInfo {
  258. info := genBaseRelayInfo(c, request)
  259. info.RelayMode = relayconstant.RelayModeRerank
  260. info.RelayFormat = types.RelayFormatRerank
  261. info.RerankerInfo = &RerankerInfo{
  262. Documents: request.Documents,
  263. ReturnDocuments: request.GetReturnDocuments(),
  264. }
  265. return info
  266. }
  267. func GenRelayInfoOpenAIAudio(c *gin.Context, request dto.Request) *RelayInfo {
  268. info := genBaseRelayInfo(c, request)
  269. info.RelayFormat = types.RelayFormatOpenAIAudio
  270. return info
  271. }
  272. func GenRelayInfoEmbedding(c *gin.Context, request dto.Request) *RelayInfo {
  273. info := genBaseRelayInfo(c, request)
  274. info.RelayFormat = types.RelayFormatEmbedding
  275. return info
  276. }
  277. func GenRelayInfoResponses(c *gin.Context, request *dto.OpenAIResponsesRequest) *RelayInfo {
  278. info := genBaseRelayInfo(c, request)
  279. info.RelayMode = relayconstant.RelayModeResponses
  280. info.RelayFormat = types.RelayFormatOpenAIResponses
  281. info.ResponsesUsageInfo = &ResponsesUsageInfo{
  282. BuiltInTools: make(map[string]*BuildInToolInfo),
  283. }
  284. if len(request.Tools) > 0 {
  285. for _, tool := range request.GetToolsMap() {
  286. toolType := common.Interface2String(tool["type"])
  287. info.ResponsesUsageInfo.BuiltInTools[toolType] = &BuildInToolInfo{
  288. ToolName: toolType,
  289. CallCount: 0,
  290. }
  291. switch toolType {
  292. case dto.BuildInToolWebSearchPreview:
  293. searchContextSize := common.Interface2String(tool["search_context_size"])
  294. if searchContextSize == "" {
  295. searchContextSize = "medium"
  296. }
  297. info.ResponsesUsageInfo.BuiltInTools[toolType].SearchContextSize = searchContextSize
  298. }
  299. }
  300. }
  301. return info
  302. }
  303. func GenRelayInfoGemini(c *gin.Context, request dto.Request) *RelayInfo {
  304. info := genBaseRelayInfo(c, request)
  305. info.RelayFormat = types.RelayFormatGemini
  306. info.ShouldIncludeUsage = false
  307. return info
  308. }
  309. func GenRelayInfoImage(c *gin.Context, request dto.Request) *RelayInfo {
  310. info := genBaseRelayInfo(c, request)
  311. info.RelayFormat = types.RelayFormatOpenAIImage
  312. return info
  313. }
  314. func GenRelayInfoOpenAI(c *gin.Context, request dto.Request) *RelayInfo {
  315. info := genBaseRelayInfo(c, request)
  316. info.RelayFormat = types.RelayFormatOpenAI
  317. return info
  318. }
  319. func genBaseRelayInfo(c *gin.Context, request dto.Request) *RelayInfo {
  320. //channelType := common.GetContextKeyInt(c, constant.ContextKeyChannelType)
  321. //channelId := common.GetContextKeyInt(c, constant.ContextKeyChannelId)
  322. //paramOverride := common.GetContextKeyStringMap(c, constant.ContextKeyChannelParamOverride)
  323. startTime := common.GetContextKeyTime(c, constant.ContextKeyRequestStartTime)
  324. if startTime.IsZero() {
  325. startTime = time.Now()
  326. }
  327. isStream := false
  328. if request != nil {
  329. isStream = request.IsStream(c)
  330. }
  331. // firstResponseTime = time.Now() - 1 second
  332. info := &RelayInfo{
  333. Request: request,
  334. UserId: common.GetContextKeyInt(c, constant.ContextKeyUserId),
  335. UsingGroup: common.GetContextKeyString(c, constant.ContextKeyUsingGroup),
  336. UserGroup: common.GetContextKeyString(c, constant.ContextKeyUserGroup),
  337. UserQuota: common.GetContextKeyInt(c, constant.ContextKeyUserQuota),
  338. UserEmail: common.GetContextKeyString(c, constant.ContextKeyUserEmail),
  339. OriginModelName: common.GetContextKeyString(c, constant.ContextKeyOriginalModel),
  340. PromptTokens: common.GetContextKeyInt(c, constant.ContextKeyPromptTokens),
  341. TokenId: common.GetContextKeyInt(c, constant.ContextKeyTokenId),
  342. TokenKey: common.GetContextKeyString(c, constant.ContextKeyTokenKey),
  343. TokenUnlimited: common.GetContextKeyBool(c, constant.ContextKeyTokenUnlimited),
  344. isFirstResponse: true,
  345. RelayMode: relayconstant.Path2RelayMode(c.Request.URL.Path),
  346. RequestURLPath: c.Request.URL.String(),
  347. IsStream: isStream,
  348. StartTime: startTime,
  349. FirstResponseTime: startTime.Add(-time.Second),
  350. ThinkingContentInfo: ThinkingContentInfo{
  351. IsFirstThinkingContent: true,
  352. SendLastThinkingContent: false,
  353. },
  354. }
  355. if info.RelayMode == relayconstant.RelayModeUnknown {
  356. info.RelayMode = c.GetInt("relay_mode")
  357. }
  358. if strings.HasPrefix(c.Request.URL.Path, "/pg") {
  359. info.IsPlayground = true
  360. info.RequestURLPath = strings.TrimPrefix(info.RequestURLPath, "/pg")
  361. info.RequestURLPath = "/v1" + info.RequestURLPath
  362. }
  363. userSetting, ok := common.GetContextKeyType[dto.UserSetting](c, constant.ContextKeyUserSetting)
  364. if ok {
  365. info.UserSetting = userSetting
  366. }
  367. return info
  368. }
  369. func GenRelayInfo(c *gin.Context, relayFormat types.RelayFormat, request dto.Request, ws *websocket.Conn) (*RelayInfo, error) {
  370. switch relayFormat {
  371. case types.RelayFormatOpenAI:
  372. return GenRelayInfoOpenAI(c, request), nil
  373. case types.RelayFormatOpenAIAudio:
  374. return GenRelayInfoOpenAIAudio(c, request), nil
  375. case types.RelayFormatOpenAIImage:
  376. return GenRelayInfoImage(c, request), nil
  377. case types.RelayFormatOpenAIRealtime:
  378. return GenRelayInfoWs(c, ws), nil
  379. case types.RelayFormatClaude:
  380. return GenRelayInfoClaude(c, request), nil
  381. case types.RelayFormatRerank:
  382. if request, ok := request.(*dto.RerankRequest); ok {
  383. return GenRelayInfoRerank(c, request), nil
  384. }
  385. return nil, errors.New("request is not a RerankRequest")
  386. case types.RelayFormatGemini:
  387. return GenRelayInfoGemini(c, request), nil
  388. case types.RelayFormatEmbedding:
  389. return GenRelayInfoEmbedding(c, request), nil
  390. case types.RelayFormatOpenAIResponses:
  391. if request, ok := request.(*dto.OpenAIResponsesRequest); ok {
  392. return GenRelayInfoResponses(c, request), nil
  393. }
  394. return nil, errors.New("request is not a OpenAIResponsesRequest")
  395. case types.RelayFormatTask:
  396. return genBaseRelayInfo(c, nil), nil
  397. case types.RelayFormatMjProxy:
  398. return genBaseRelayInfo(c, nil), nil
  399. default:
  400. return nil, errors.New("invalid relay format")
  401. }
  402. }
  403. func (info *RelayInfo) SetPromptTokens(promptTokens int) {
  404. info.PromptTokens = promptTokens
  405. }
  406. func (info *RelayInfo) SetFirstResponseTime() {
  407. if info.isFirstResponse {
  408. info.FirstResponseTime = time.Now()
  409. info.isFirstResponse = false
  410. }
  411. }
  412. func (info *RelayInfo) HasSendResponse() bool {
  413. return info.FirstResponseTime.After(info.StartTime)
  414. }
  415. type TaskRelayInfo struct {
  416. Action string
  417. OriginTaskID string
  418. ConsumeQuota bool
  419. }
  420. type TaskSubmitReq struct {
  421. Prompt string `json:"prompt"`
  422. Model string `json:"model,omitempty"`
  423. Mode string `json:"mode,omitempty"`
  424. Image string `json:"image,omitempty"`
  425. Images []string `json:"images,omitempty"`
  426. Size string `json:"size,omitempty"`
  427. Duration int `json:"duration,omitempty"`
  428. Metadata map[string]interface{} `json:"metadata,omitempty"`
  429. }
  430. func (t TaskSubmitReq) GetPrompt() string {
  431. return t.Prompt
  432. }
  433. func (t TaskSubmitReq) HasImage() bool {
  434. return len(t.Images) > 0
  435. }
  436. type TaskInfo struct {
  437. Code int `json:"code"`
  438. TaskID string `json:"task_id"`
  439. Status string `json:"status"`
  440. Reason string `json:"reason,omitempty"`
  441. Url string `json:"url,omitempty"`
  442. Progress string `json:"progress,omitempty"`
  443. CompletionTokens int `json:"completion_tokens,omitempty"` // 用于按倍率计费
  444. TotalTokens int `json:"total_tokens,omitempty"` // 用于按倍率计费
  445. }
  446. // RemoveDisabledFields 从请求 JSON 数据中移除渠道设置中禁用的字段
  447. // service_tier: 服务层级字段,可能导致额外计费(OpenAI、Claude、Responses API 支持)
  448. // store: 数据存储授权字段,涉及用户隐私(仅 OpenAI、Responses API 支持,默认允许透传,禁用后可能导致 Codex 无法使用)
  449. // safety_identifier: 安全标识符,用于向 OpenAI 报告违规用户(仅 OpenAI 支持,涉及用户隐私)
  450. func RemoveDisabledFields(jsonData []byte, channelOtherSettings dto.ChannelOtherSettings) ([]byte, error) {
  451. var data map[string]interface{}
  452. if err := common.Unmarshal(jsonData, &data); err != nil {
  453. common.SysError("RemoveDisabledFields Unmarshal error :" + err.Error())
  454. return jsonData, nil
  455. }
  456. // 默认移除 service_tier,除非明确允许(避免额外计费风险)
  457. if !channelOtherSettings.AllowServiceTier {
  458. if _, exists := data["service_tier"]; exists {
  459. delete(data, "service_tier")
  460. }
  461. }
  462. // 默认允许 store 透传,除非明确禁用(禁用可能影响 Codex 使用)
  463. if channelOtherSettings.DisableStore {
  464. if _, exists := data["store"]; exists {
  465. delete(data, "store")
  466. }
  467. }
  468. // 默认移除 safety_identifier,除非明确允许(保护用户隐私,避免向 OpenAI 报告用户信息)
  469. if !channelOtherSettings.AllowSafetyIdentifier {
  470. if _, exists := data["safety_identifier"]; exists {
  471. delete(data, "safety_identifier")
  472. }
  473. }
  474. jsonDataAfter, err := common.Marshal(data)
  475. if err != nil {
  476. common.SysError("RemoveDisabledFields Marshal error :" + err.Error())
  477. return jsonData, nil
  478. }
  479. return jsonDataAfter, nil
  480. }
  481. type OpenAIVideo struct {
  482. ID string `json:"id"`
  483. TaskID string `json:"task_id,omitempty"` //兼容旧接口 待废弃
  484. Object string `json:"object"`
  485. Model string `json:"model"`
  486. Status string `json:"status"`
  487. Progress int `json:"progress"`
  488. CreatedAt int64 `json:"created_at"`
  489. CompletedAt int64 `json:"completed_at,omitempty"`
  490. ExpiresAt int64 `json:"expires_at,omitempty"`
  491. Seconds string `json:"seconds,omitempty"`
  492. Size string `json:"size,omitempty"`
  493. RemixedFromVideoID string `json:"remixed_from_video_id,omitempty"`
  494. Error *OpenAIVideoError `json:"error,omitempty"`
  495. Metadata map[string]any `json:"metadata,omitempty"`
  496. }
  497. func (m *OpenAIVideo) SetProgressStr(progress string) {
  498. progress = strings.TrimSuffix(progress, "%")
  499. m.Progress, _ = strconv.Atoi(progress)
  500. }
  501. func (m *OpenAIVideo) SetMetadata(k string, v any) {
  502. if m.Metadata == nil {
  503. m.Metadata = make(map[string]any)
  504. }
  505. m.Metadata[k] = v
  506. }
  507. func NewOpenAIVideo() *OpenAIVideo {
  508. return &OpenAIVideo{
  509. Object: "video",
  510. }
  511. }
  512. type OpenAIVideoError struct {
  513. Message string `json:"message"`
  514. Code string `json:"code"`
  515. }