relay_info.go 19 KB

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