Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

453 строки
13 KiB

  1. package controller
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "strconv"
  8. "strings"
  9. "github.com/QuantumNous/new-api/common"
  10. "github.com/QuantumNous/new-api/constant"
  11. "github.com/QuantumNous/new-api/dto"
  12. "github.com/QuantumNous/new-api/middleware"
  13. "github.com/QuantumNous/new-api/model"
  14. "github.com/QuantumNous/new-api/relay"
  15. klingaiping "github.com/QuantumNous/new-api/relay/channel/task/kling/aiping"
  16. relaycommon "github.com/QuantumNous/new-api/relay/common"
  17. "github.com/QuantumNous/new-api/service"
  18. "github.com/QuantumNous/new-api/types"
  19. "github.com/gin-gonic/gin"
  20. )
  21. func KlingAipingNativeTaskSubmit(c *gin.Context) {
  22. route, ok := klingaiping.FindRoute(c.Request.Method, c.FullPath(), klingaiping.RouteKindSubmit)
  23. if !ok {
  24. c.JSON(http.StatusNotFound, gin.H{"code": 404, "message": "route not found"})
  25. return
  26. }
  27. relayInfo, err := relaycommon.GenRelayInfo(c, types.RelayFormatTask, nil, nil)
  28. if err != nil {
  29. c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": err.Error()})
  30. return
  31. }
  32. payload, err := readJSONPayload(c)
  33. if err != nil {
  34. c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()})
  35. return
  36. }
  37. configureKlingAipingTaskRelayInfo(c, relayInfo, route, payload)
  38. relayTaskWithInfo(c, relayInfo)
  39. }
  40. func configureKlingAipingTaskRelayInfo(c *gin.Context, relayInfo *relaycommon.RelayInfo, route klingaiping.Route, payload map[string]any) {
  41. // Native Kling routes bypass Distribute(), so force relayTaskWithInfo to
  42. // select a channel instead of reading a preselected one from context.
  43. if relayInfo.ChannelMeta == nil {
  44. relayInfo.ChannelMeta = &relaycommon.ChannelMeta{}
  45. }
  46. modelName := resolveKlingAipingModel(payload, route)
  47. relayInfo.OriginModelName = modelName
  48. relayInfo.Action = route.Action
  49. relaycommon.StoreTaskRequest(c, relayInfo, route.Action, relaycommon.TaskSubmitReq{
  50. Model: modelName,
  51. Prompt: stringFromMap(payload, "prompt"),
  52. Seconds: fmt.Sprint(payload["seconds"]),
  53. Metadata: payload,
  54. })
  55. }
  56. func KlingAipingNativeTaskFetch(c *gin.Context) {
  57. route, ok := klingaiping.FindRoute(c.Request.Method, c.FullPath(), klingaiping.RouteKindFetch)
  58. if !ok {
  59. c.JSON(http.StatusNotFound, gin.H{"code": 404, "message": "route not found"})
  60. return
  61. }
  62. task, ok := getKlingAipingUserTask(c, c.Param("task_id"), route.Action)
  63. if !ok {
  64. return
  65. }
  66. refreshKlingAipingTaskIfNeeded(task)
  67. c.JSON(http.StatusOK, buildKlingAipingTaskPayload(task))
  68. }
  69. func KlingAipingNativeTaskList(c *gin.Context) {
  70. route, ok := klingaiping.FindRoute(c.Request.Method, c.FullPath(), klingaiping.RouteKindList)
  71. if !ok {
  72. c.JSON(http.StatusNotFound, gin.H{"code": 404, "message": "route not found"})
  73. return
  74. }
  75. pageNum, pageSize, err := parseKlingAipingPage(c)
  76. if err != nil {
  77. c.JSON(http.StatusUnprocessableEntity, gin.H{"code": 422, "message": err.Error()})
  78. return
  79. }
  80. tasks := model.TaskGetAllUserTask(c.GetInt("id"), (pageNum-1)*pageSize, pageSize, model.SyncTaskQueryParams{
  81. Platform: constant.TaskPlatform(strconv.Itoa(constant.ChannelTypeKlingAiping)),
  82. Action: route.Action,
  83. })
  84. data := make([]any, 0, len(tasks))
  85. for _, task := range tasks {
  86. data = append(data, taskDataObject(task))
  87. }
  88. c.JSON(http.StatusOK, gin.H{
  89. "code": 0,
  90. "message": "success",
  91. "request_id": c.GetString(common.RequestIdKey),
  92. "data": data,
  93. })
  94. }
  95. func KlingAipingNativeProxy(c *gin.Context) {
  96. route, ok := klingaiping.FindRoute(c.Request.Method, c.FullPath(), klingaiping.RouteKindProxy)
  97. if !ok {
  98. c.JSON(http.StatusNotFound, gin.H{"code": 404, "message": "route not found"})
  99. return
  100. }
  101. channel, _, err := service.CacheGetRandomSatisfiedChannel(&service.RetryParam{
  102. Ctx: c,
  103. TokenGroup: common.GetContextKeyString(c, constant.ContextKeyUsingGroup),
  104. ModelName: route.BillingModel,
  105. Retry: common.GetPointer(0),
  106. RequiredChannelType: constant.ChannelTypeKlingAiping,
  107. })
  108. if err != nil {
  109. c.JSON(http.StatusServiceUnavailable, gin.H{"code": 503, "message": err.Error()})
  110. return
  111. }
  112. if setupErr := middleware.SetupContextForSelectedChannel(c, channel, route.BillingModel); setupErr != nil {
  113. c.JSON(setupErr.StatusCode, gin.H{"code": setupErr.GetErrorCode(), "message": setupErr.Error()})
  114. return
  115. }
  116. resp, err := doKlingAipingProxyRequest(c, route, channel)
  117. if err != nil {
  118. c.JSON(http.StatusBadGateway, gin.H{"code": 502, "message": err.Error()})
  119. return
  120. }
  121. defer resp.Body.Close()
  122. copyProxyResponse(c, resp)
  123. }
  124. func readJSONPayload(c *gin.Context) (map[string]any, error) {
  125. body, err := common.GetBodyStorage(c)
  126. if err != nil {
  127. return nil, err
  128. }
  129. data, err := body.Bytes()
  130. if err != nil {
  131. return nil, err
  132. }
  133. _, _ = body.Seek(0, io.SeekStart)
  134. c.Request.Body = io.NopCloser(body)
  135. payload := map[string]any{}
  136. if strings.TrimSpace(string(data)) == "" {
  137. return payload, nil
  138. }
  139. if err := common.Unmarshal(data, &payload); err != nil {
  140. return nil, err
  141. }
  142. return payload, nil
  143. }
  144. func resolveKlingAipingModel(payload map[string]any, route klingaiping.Route) string {
  145. if modelName := stringFromMap(payload, "model_name"); modelName != "" {
  146. return modelName
  147. }
  148. if modelName := stringFromMap(payload, "model"); modelName != "" {
  149. return modelName
  150. }
  151. if route.BillingModel != "" {
  152. return route.BillingModel
  153. }
  154. return "kling-v3"
  155. }
  156. func stringFromMap(payload map[string]any, key string) string {
  157. if value, ok := payload[key].(string); ok {
  158. return strings.TrimSpace(value)
  159. }
  160. return ""
  161. }
  162. func getKlingAipingUserTask(c *gin.Context, taskID string, action string) (*model.Task, bool) {
  163. task, exist, err := model.GetByTaskId(c.GetInt("id"), taskID)
  164. if err != nil {
  165. c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": err.Error()})
  166. return nil, false
  167. }
  168. if !exist || task.Platform != constant.TaskPlatform(strconv.Itoa(constant.ChannelTypeKlingAiping)) || task.Action != action {
  169. c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "task not found"})
  170. return nil, false
  171. }
  172. return task, true
  173. }
  174. func refreshKlingAipingTaskIfNeeded(task *model.Task) {
  175. if task.Status == model.TaskStatusSuccess || task.Status == model.TaskStatusFailure {
  176. return
  177. }
  178. channelModel, err := model.GetChannelById(task.ChannelId, true)
  179. if err != nil || channelModel == nil {
  180. return
  181. }
  182. adaptor := relay.GetTaskAdaptor(task.Platform)
  183. if adaptor == nil {
  184. return
  185. }
  186. resp, err := adaptor.FetchTask(channelModel.GetBaseURL(), channelModel.Key, map[string]any{
  187. "task_id": task.GetUpstreamTaskID(),
  188. "action": task.Action,
  189. }, channelModel.GetSetting().Proxy)
  190. if err != nil || resp == nil {
  191. return
  192. }
  193. defer resp.Body.Close()
  194. body, err := io.ReadAll(resp.Body)
  195. if err != nil || resp.StatusCode < 200 || resp.StatusCode >= 300 {
  196. return
  197. }
  198. taskInfo, err := adaptor.ParseTaskResult(body)
  199. if err != nil || taskInfo == nil {
  200. return
  201. }
  202. snap := task.Snapshot()
  203. task.Data = body
  204. if taskInfo.Status != "" {
  205. task.Status = model.TaskStatus(taskInfo.Status)
  206. }
  207. if taskInfo.Progress != "" {
  208. task.Progress = taskInfo.Progress
  209. }
  210. if taskInfo.Url != "" {
  211. task.PrivateData.ResultURL = taskInfo.Url
  212. }
  213. if !snap.Equal(task.Snapshot()) {
  214. _, _ = task.UpdateWithStatus(snap.Status)
  215. }
  216. }
  217. func buildKlingAipingTaskPayload(task *model.Task) map[string]any {
  218. payload := map[string]any{
  219. "code": 0,
  220. "message": "success",
  221. "data": taskDataObject(task),
  222. }
  223. return payload
  224. }
  225. func taskDataObject(task *model.Task) map[string]any {
  226. payload := map[string]any{}
  227. _ = common.Unmarshal(task.Data, &payload)
  228. delete(payload, "aiping_id")
  229. data, _ := payload["data"].(map[string]any)
  230. if data == nil {
  231. data = map[string]any{}
  232. }
  233. data["task_id"] = task.TaskID
  234. if _, ok := data["task_status"]; !ok {
  235. data["task_status"] = mapKlingAipingTaskStatus(task.Status)
  236. }
  237. if _, ok := data["task_status_msg"]; !ok {
  238. data["task_status_msg"] = task.FailReason
  239. }
  240. if _, ok := data["created_at"]; !ok && task.CreatedAt != 0 {
  241. data["created_at"] = task.CreatedAt
  242. }
  243. if _, ok := data["updated_at"]; !ok && task.UpdatedAt != 0 {
  244. data["updated_at"] = task.UpdatedAt
  245. }
  246. ensureKlingAipingWatermarkURL(data)
  247. return data
  248. }
  249. func mapKlingAipingTaskStatus(status model.TaskStatus) string {
  250. switch status {
  251. case model.TaskStatusSubmitted, model.TaskStatusQueued:
  252. return "submitted"
  253. case model.TaskStatusInProgress:
  254. return "processing"
  255. case model.TaskStatusSuccess:
  256. return "succeed"
  257. case model.TaskStatusFailure:
  258. return "failed"
  259. default:
  260. return "processing"
  261. }
  262. }
  263. func ensureKlingAipingWatermarkURL(data map[string]any) {
  264. taskResult, _ := data["task_result"].(map[string]any)
  265. if taskResult == nil {
  266. return
  267. }
  268. videos, _ := taskResult["videos"].([]any)
  269. for _, videoAny := range videos {
  270. video, _ := videoAny.(map[string]any)
  271. if video == nil {
  272. continue
  273. }
  274. if _, ok := video["watermark_url"]; !ok {
  275. video["watermark_url"] = ""
  276. }
  277. }
  278. }
  279. func parseKlingAipingPage(c *gin.Context) (int, int, error) {
  280. pageNum := parseIntDefault(c.Query("pageNum"), 1)
  281. pageSize := parseIntDefault(c.Query("pageSize"), 30)
  282. if pageNum < 1 || pageNum > 1000 {
  283. return 0, 0, fmt.Errorf("pageNum must be in [1, 1000]")
  284. }
  285. if pageSize < 1 || pageSize > 500 {
  286. return 0, 0, fmt.Errorf("pageSize must be in [1, 500]")
  287. }
  288. return pageNum, pageSize, nil
  289. }
  290. func parseIntDefault(raw string, fallback int) int {
  291. if strings.TrimSpace(raw) == "" {
  292. return fallback
  293. }
  294. v, err := strconv.Atoi(raw)
  295. if err != nil {
  296. return -1
  297. }
  298. return v
  299. }
  300. func doKlingAipingProxyRequest(c *gin.Context, route klingaiping.Route, channelModel *model.Channel) (*http.Response, error) {
  301. baseURL := strings.TrimRight(channelModel.GetBaseURL(), "/")
  302. upstreamPath := strings.Replace(route.UpstreamPath, ":id", c.Param("id"), 1)
  303. url := baseURL + upstreamPath
  304. if c.Request.URL.RawQuery != "" {
  305. url += "?" + c.Request.URL.RawQuery
  306. }
  307. var body io.Reader
  308. if c.Request.Method != http.MethodGet {
  309. data, err := proxyBodyBytes(c, route)
  310. if err != nil {
  311. return nil, err
  312. }
  313. body = bytes.NewReader(data)
  314. }
  315. req, err := http.NewRequest(c.Request.Method, url, body)
  316. if err != nil {
  317. return nil, err
  318. }
  319. req.Header.Set("Accept", "application/json")
  320. req.Header.Set("Content-Type", "application/json")
  321. key := common.GetContextKeyString(c, constant.ContextKeyChannelKey)
  322. if key == "" {
  323. key = channelModel.Key
  324. }
  325. req.Header.Set("Authorization", "Bearer "+key)
  326. client, err := service.GetHttpClientWithProxy(channelModel.GetSetting().Proxy)
  327. if err != nil {
  328. return nil, err
  329. }
  330. return client.Do(req)
  331. }
  332. func proxyBodyBytes(c *gin.Context, route klingaiping.Route) ([]byte, error) {
  333. storage, err := common.GetBodyStorage(c)
  334. if err != nil {
  335. return nil, err
  336. }
  337. data, err := storage.Bytes()
  338. if err != nil {
  339. return nil, err
  340. }
  341. if route.PathPattern == "/v1/elements/delete" {
  342. payload := map[string]any{}
  343. if err := common.Unmarshal(data, &payload); err == nil {
  344. if _, ok := payload["element_id"]; !ok {
  345. if id := stringFromMap(payload, "id"); id != "" {
  346. payload["element_id"] = id
  347. delete(payload, "id")
  348. data, _ = common.Marshal(payload)
  349. }
  350. }
  351. }
  352. }
  353. return data, nil
  354. }
  355. func copyProxyResponse(c *gin.Context, resp *http.Response) {
  356. if resp.StatusCode >= http.StatusBadRequest {
  357. copyNormalizedProxyError(c, resp)
  358. return
  359. }
  360. for key, values := range resp.Header {
  361. for _, value := range values {
  362. c.Writer.Header().Add(key, value)
  363. }
  364. }
  365. c.Status(resp.StatusCode)
  366. _, _ = io.Copy(c.Writer, resp.Body)
  367. }
  368. func copyNormalizedProxyError(c *gin.Context, resp *http.Response) {
  369. body, _ := io.ReadAll(resp.Body)
  370. message := strings.TrimSpace(string(body))
  371. payload := map[string]any{}
  372. if len(body) > 0 && common.Unmarshal(body, &payload) == nil {
  373. if msg := stringFromMap(payload, "message"); msg != "" {
  374. message = msg
  375. } else if msg := stringFromMap(payload, "msg"); msg != "" {
  376. message = msg
  377. } else if detail, ok := payload["detail"]; ok {
  378. if detailMap, ok := detail.(map[string]any); ok {
  379. if msg := stringFromMap(detailMap, "message"); msg != "" {
  380. message = msg
  381. } else if msg := stringFromMap(detailMap, "msg"); msg != "" {
  382. message = msg
  383. } else {
  384. message = fmt.Sprint(detail)
  385. }
  386. } else {
  387. message = fmt.Sprint(detail)
  388. }
  389. }
  390. delete(payload, "msg")
  391. } else {
  392. payload = map[string]any{}
  393. }
  394. if message == "" {
  395. message = resp.Status
  396. }
  397. if _, ok := payload["code"]; !ok {
  398. payload["code"] = resp.StatusCode
  399. }
  400. payload["message"] = message
  401. payload["request_id"] = c.GetString(common.RequestIdKey)
  402. c.JSON(resp.StatusCode, payload)
  403. }
  404. func normalizeKlingAipingTaskError(taskErr *dto.TaskError) {
  405. if taskErr == nil || strings.TrimSpace(taskErr.Message) == "" {
  406. return
  407. }
  408. payload := map[string]any{}
  409. if common.Unmarshal([]byte(taskErr.Message), &payload) != nil {
  410. return
  411. }
  412. if msg := stringFromMap(payload, "message"); msg != "" {
  413. taskErr.Message = msg
  414. return
  415. }
  416. if msg := stringFromMap(payload, "msg"); msg != "" {
  417. taskErr.Message = msg
  418. return
  419. }
  420. if detail, ok := payload["detail"].(map[string]any); ok {
  421. if msg := stringFromMap(detail, "message"); msg != "" {
  422. taskErr.Message = msg
  423. }
  424. }
  425. }