您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

287 行
8.9 KiB

  1. package aiping
  2. import (
  3. "io"
  4. "net/http"
  5. "net/http/httptest"
  6. "testing"
  7. "github.com/QuantumNous/new-api/common"
  8. relaycommon "github.com/QuantumNous/new-api/relay/common"
  9. "github.com/QuantumNous/new-api/service"
  10. "github.com/gin-gonic/gin"
  11. "github.com/stretchr/testify/require"
  12. )
  13. func TestRoutesUseOfficialExternalAndAipingUpstreamPaths(t *testing.T) {
  14. route, ok := FindRoute(http.MethodPost, "/v1/videos/text2video", RouteKindSubmit)
  15. require.True(t, ok)
  16. require.Equal(t, ActionText2Video, route.Action)
  17. require.Equal(t, "/api/v1/multimodal/kling/videos/text2video", route.UpstreamPath)
  18. voice, ok := FindRoute(http.MethodPost, "/v1/general/custom-voices", RouteKindSubmit)
  19. require.True(t, ok)
  20. require.Equal(t, "/api/v1/multimodal/kling/general/custom-voices", voice.UpstreamPath)
  21. presets, ok := FindRoute(http.MethodGet, "/v1/general/presets-voices", RouteKindProxy)
  22. require.True(t, ok)
  23. require.Equal(t, "/api/v1/multimodal/kling/general/presets-voices", presets.UpstreamPath)
  24. deleteVoices, ok := FindRoute(http.MethodPost, "/v1/general/delete-voices", RouteKindProxy)
  25. require.True(t, ok)
  26. require.Equal(t, ActionDeleteVoices, deleteVoices.Action)
  27. deleteElements, ok := FindRoute(http.MethodPost, "/v1/general/delete-advanced-elements", RouteKindProxy)
  28. require.True(t, ok)
  29. require.Equal(t, ActionDeleteElements, deleteElements.Action)
  30. videoExtend, ok := FindRoute(http.MethodPost, "/v1/videos/video-extend", RouteKindSubmit)
  31. require.True(t, ok)
  32. require.Equal(t, ActionVideoExtend, videoExtend.Action)
  33. require.Equal(t, "/api/v1/multimodal/kling/videos/video-extend", videoExtend.UpstreamPath)
  34. motionControl, ok := FindRoute(http.MethodPost, "/v1/videos/motion-control", RouteKindSubmit)
  35. require.True(t, ok)
  36. require.Equal(t, "", motionControl.BillingModel, "motion-control BillingModel should be empty so client model_name is used")
  37. }
  38. func TestBuildRequestURLUsesAipingKlingPath(t *testing.T) {
  39. adaptor := &TaskAdaptor{}
  40. info := &relaycommon.RelayInfo{
  41. ChannelMeta: &relaycommon.ChannelMeta{ChannelBaseUrl: "https://aiping.cn"},
  42. TaskRelayInfo: &relaycommon.TaskRelayInfo{
  43. Action: ActionText2Video,
  44. },
  45. }
  46. adaptor.Init(info)
  47. url, err := adaptor.BuildRequestURL(info)
  48. require.NoError(t, err)
  49. require.Equal(t, "https://aiping.cn/api/v1/multimodal/kling/videos/text2video", url)
  50. }
  51. func TestFetchTaskUsesSingleTaskPath(t *testing.T) {
  52. service.InitHttpClient()
  53. var gotPath string
  54. server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  55. gotPath = r.URL.Path
  56. w.Header().Set("Content-Type", "application/json")
  57. _, _ = w.Write([]byte(`{"code":0,"data":{"task_id":"upstream-task","task_status":"submitted"}}`))
  58. }))
  59. defer server.Close()
  60. resp, err := (&TaskAdaptor{}).FetchTask(server.URL, "key", map[string]any{
  61. "task_id": "upstream-task",
  62. "action": ActionText2Video,
  63. }, "")
  64. require.NoError(t, err)
  65. defer resp.Body.Close()
  66. require.Equal(t, "/api/v1/multimodal/kling/videos/text2video/upstream-task", gotPath)
  67. }
  68. func TestBuildRequestBodyNormalizesOfficialCompatibleFields(t *testing.T) {
  69. gin.SetMode(gin.TestMode)
  70. c, _ := gin.CreateTestContext(nil)
  71. info := &relaycommon.RelayInfo{
  72. ChannelMeta: &relaycommon.ChannelMeta{},
  73. TaskRelayInfo: &relaycommon.TaskRelayInfo{
  74. Action: ActionMultiImage2Video,
  75. },
  76. OriginModelName: "Kling-V2.6",
  77. }
  78. relaycommon.StoreTaskRequest(c, info, ActionMultiImage2Video, relaycommon.TaskSubmitReq{
  79. Model: "Kling-V2.6",
  80. Metadata: map[string]any{
  81. "model": "Kling-V1.6",
  82. "model_name": "Kling-V2.6",
  83. "seconds": 3,
  84. "duration": 5,
  85. "action_control": map[string]any{"type": "continuous"},
  86. "uid": "user-1",
  87. "create_at": float64(1750000000000),
  88. "_standard_model": "Kling-V-2-6",
  89. "image_list": []any{
  90. "https://example.com/1.png",
  91. map[string]any{"image_url": "https://example.com/2.png"},
  92. map[string]any{"url": "https://example.com/3.png"},
  93. map[string]any{"base64": "abc"},
  94. },
  95. },
  96. })
  97. body, err := (&TaskAdaptor{}).BuildRequestBody(c, info)
  98. require.NoError(t, err)
  99. data, err := io.ReadAll(body)
  100. require.NoError(t, err)
  101. var got map[string]any
  102. require.NoError(t, common.Unmarshal(data, &got))
  103. require.Equal(t, "Kling-V2.6", got["model_name"])
  104. require.NotContains(t, got, "model")
  105. require.Equal(t, "5", got["duration"])
  106. require.NotContains(t, got, "seconds")
  107. require.NotContains(t, got, "action_control")
  108. require.NotContains(t, got, "uid")
  109. require.NotContains(t, got, "create_at")
  110. require.NotContains(t, got, "_standard_model")
  111. require.Equal(t, []any{
  112. map[string]any{"image": "https://example.com/1.png"},
  113. map[string]any{"image": "https://example.com/2.png"},
  114. map[string]any{"image": "https://example.com/3.png"},
  115. map[string]any{"image": "abc"},
  116. }, got["image_list"])
  117. }
  118. func TestBuildRequestBodyMapsCanonicalModelNameForAiping(t *testing.T) {
  119. gin.SetMode(gin.TestMode)
  120. c, _ := gin.CreateTestContext(nil)
  121. info := &relaycommon.RelayInfo{
  122. ChannelMeta: &relaycommon.ChannelMeta{},
  123. TaskRelayInfo: &relaycommon.TaskRelayInfo{
  124. Action: ActionText2Video,
  125. },
  126. OriginModelName: "kling-v2-6",
  127. }
  128. relaycommon.StoreTaskRequest(c, info, ActionText2Video, relaycommon.TaskSubmitReq{
  129. Model: "kling-v2-6",
  130. Metadata: map[string]any{
  131. "model_name": "kling-v2-6",
  132. "prompt": "prompt",
  133. "duration": "5",
  134. },
  135. })
  136. body, err := (&TaskAdaptor{}).BuildRequestBody(c, info)
  137. require.NoError(t, err)
  138. data, err := io.ReadAll(body)
  139. require.NoError(t, err)
  140. var got map[string]any
  141. require.NoError(t, common.Unmarshal(data, &got))
  142. require.Equal(t, "Kling-V2.6", got["model_name"])
  143. }
  144. func TestBuildRequestBodyUsesMappedKlingUpstreamModel(t *testing.T) {
  145. gin.SetMode(gin.TestMode)
  146. c, _ := gin.CreateTestContext(nil)
  147. info := &relaycommon.RelayInfo{
  148. OriginModelName: "public-kling-alias",
  149. ChannelMeta: &relaycommon.ChannelMeta{
  150. UpstreamModelName: "kling-v2-6",
  151. IsModelMapped: true,
  152. },
  153. TaskRelayInfo: &relaycommon.TaskRelayInfo{
  154. Action: ActionText2Video,
  155. },
  156. }
  157. relaycommon.StoreTaskRequest(c, info, ActionText2Video, relaycommon.TaskSubmitReq{
  158. Model: "public-kling-alias",
  159. Metadata: map[string]any{
  160. "model_name": "public-kling-alias",
  161. "prompt": "prompt",
  162. "duration": "5",
  163. },
  164. })
  165. body, err := (&TaskAdaptor{}).BuildRequestBody(c, info)
  166. require.NoError(t, err)
  167. data, err := io.ReadAll(body)
  168. require.NoError(t, err)
  169. var got map[string]any
  170. require.NoError(t, common.Unmarshal(data, &got))
  171. require.Equal(t, "Kling-V2.6", got["model_name"])
  172. require.NotContains(t, got, "model")
  173. }
  174. func TestValidateRequestRejectsOmniModelOnTextOrImageRoutes(t *testing.T) {
  175. gin.SetMode(gin.TestMode)
  176. c, _ := gin.CreateTestContext(nil)
  177. info := &relaycommon.RelayInfo{
  178. TaskRelayInfo: &relaycommon.TaskRelayInfo{
  179. Action: ActionText2Video,
  180. },
  181. }
  182. relaycommon.StoreTaskRequest(c, info, ActionText2Video, relaycommon.TaskSubmitReq{
  183. Model: "kling-v3-omni",
  184. Metadata: map[string]any{
  185. "model_name": "kling-v3-omni",
  186. },
  187. })
  188. taskErr := (&TaskAdaptor{}).ValidateRequestAndSetAction(c, info)
  189. require.NotNil(t, taskErr)
  190. require.Equal(t, http.StatusUnprocessableEntity, taskErr.StatusCode)
  191. require.Contains(t, taskErr.Message, "/v1/videos/omni-video")
  192. }
  193. func TestParseTaskResultConvertsFinalUnitDeductionToTokens(t *testing.T) {
  194. taskInfo, err := (&TaskAdaptor{}).ParseTaskResult([]byte(`{
  195. "code": 0,
  196. "message": "success",
  197. "request_id": "req-1",
  198. "data": {
  199. "task_id": "upstream-task",
  200. "task_status": "succeed",
  201. "task_result": {
  202. "videos": [
  203. {"id": "v1", "url": "https://example.com/video.mp4", "duration": "5"}
  204. ]
  205. },
  206. "final_unit_deduction": "1.234567"
  207. },
  208. "aiping_id": "internal"
  209. }`))
  210. require.NoError(t, err)
  211. require.Equal(t, 1234567, taskInfo.CompletionTokens)
  212. require.Equal(t, 1234567, taskInfo.TotalTokens)
  213. }
  214. func TestFinalUnitDeductionTokens(t *testing.T) {
  215. cases := []struct {
  216. name string
  217. input any
  218. want int
  219. }{
  220. {"string decimal", "1.234567", 1234567},
  221. {"float64", float64(1.234567), 1234567},
  222. {"int", int(2), 2000000},
  223. {"int64", int64(3), 3000000},
  224. {"empty string", "", 0},
  225. {"whitespace string", " ", 0},
  226. {"zero float", float64(0), 0},
  227. {"negative float", float64(-1.5), 0},
  228. {"nil", nil, 0},
  229. }
  230. for _, tc := range cases {
  231. t.Run(tc.name, func(t *testing.T) {
  232. require.Equal(t, tc.want, finalUnitDeductionTokens(tc.input))
  233. })
  234. }
  235. }
  236. func TestSanitizeNativeTaskPayloadUsesPublicTaskIDAndAddsWatermarkURL(t *testing.T) {
  237. payload := map[string]any{
  238. "aiping_id": "internal",
  239. "data": map[string]any{
  240. "task_id": "899333358055493641",
  241. "task_result": map[string]any{
  242. "videos": []any{
  243. map[string]any{"id": "v1", "url": "https://example.com/video.mp4"},
  244. },
  245. },
  246. },
  247. }
  248. sanitizeNativeTaskPayload(payload, "task_public")
  249. require.NotContains(t, payload, "aiping_id")
  250. data := payload["data"].(map[string]any)
  251. require.Equal(t, "task_public", data["task_id"])
  252. videos := data["task_result"].(map[string]any)["videos"].([]any)
  253. require.Equal(t, "", videos[0].(map[string]any)["watermark_url"])
  254. }