From a59b77cf7044bdcda7f6b7827ff2ab658fd6d13c Mon Sep 17 00:00:00 2001 From: ddCat <2695092045@qq.com> Date: Sat, 19 Sep 2026 20:29:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(plugin):=20=E6=B5=B7=E8=9E=BA=E8=A7=86?= =?UTF-8?q?=E9=A2=91=20-=20=E6=9B=B4=E6=96=B0=20v2=20=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E5=90=88=E5=90=8C=E5=B9=B6=E8=A1=A5=E5=85=85=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E5=93=8D=E5=BA=94=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/app/protocol_runtime_test.go | 54 ++ .../internal/protocol/minimax_video_test.go | 39 + .../minimax-hailuo-video-v2.yingce-plugin | Bin 10808 -> 11432 bytes .../minimax-hailuo-video-v2/README.md | 14 + .../minimax-hailuo-video-v2/docs/interface.md | 671 ++++++++++++++++++ .../minimax-hailuo-video-v2/manifest.json | 609 ++++++++++++++++ 6 files changed, 1387 insertions(+) create mode 100644 backend/internal/protocol/minimax_video_test.go create mode 100644 plugin-packages/minimax-hailuo-video-v2/README.md create mode 100644 plugin-packages/minimax-hailuo-video-v2/docs/interface.md create mode 100644 plugin-packages/minimax-hailuo-video-v2/manifest.json diff --git a/backend/internal/app/protocol_runtime_test.go b/backend/internal/app/protocol_runtime_test.go index 63c932f85..7ceac98b1 100644 --- a/backend/internal/app/protocol_runtime_test.go +++ b/backend/internal/app/protocol_runtime_test.go @@ -599,6 +599,60 @@ func TestDeclarativeNewAPIChannel2TaskNotExistExhaustion(t *testing.T) { } } +func TestDeclarativeMiniMaxFailureReachesTaskError(t *testing.T) { + allowLoopbackProviderTest(t) + data, err := os.ReadFile(filepath.Join("..", "..", "..", "plugin-packages", "minimax-hailuo-video-v2.yingce-plugin")) + if err != nil { + t.Fatal(err) + } + pkg, err := protocol.ParsePluginPackage(data) + if err != nil { + t.Fatal(err) + } + adapters, err := protocol.LoadInstalledProviders(pkg.ManifestRaw, nil) + if err != nil || len(adapters) != 1 { + t.Fatalf("load MiniMax adapter: count=%d, error=%v", len(adapters), err) + } + const message = "content[1].image_url: media dimensions must be between 256 and 5760 pixels" + const failedResponse = `{"task":{"id":"video-1","status":"failed","error":{"code":"2013","message":"` + message + `"}}}` + for _, phase := range []string{"create", "poll"} { + t.Run(phase, func(t *testing.T) { + createCalls, pollCalls := 0, 0 + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + switch r.URL.Path { + case "/v2/video_generation": + createCalls++ + if phase == "create" { + _, _ = w.Write([]byte(failedResponse)) + } else { + _, _ = w.Write([]byte(`{"task":{"id":"video-1","status":"pending"}}`)) + } + case "/v2/query/video_generation/video-1": + pollCalls++ + _, _ = w.Write([]byte(failedResponse)) + default: + http.NotFound(w, r) + } + })) + defer server.Close() + config := providerConfig{BaseURL: server.URL, APIKey: "test-key", Model: "MiniMax-H3", InterfaceType: "minimax-video", VideoSeconds: "12"} + result, err := runProtocolAdapterTaskWithPolicy(context.Background(), canvasGenerationInput{Mode: "video", Prompt: "test", Config: config}, adapters[0], fastVideoPollPolicy()) + want := "声明式协议任务失败(任务 video-1):" + message + if result != nil || err == nil || taskFailureMessage(err) != want { + t.Fatalf("result=%#v, error=%v, want task error %q", result, err, want) + } + wantPollCalls := 0 + if phase == "poll" { + wantPollCalls = 1 + } + if createCalls != 1 || pollCalls != wantPollCalls { + t.Fatalf("create calls=%d, poll calls=%d; failed tasks must not be retried", createCalls, pollCalls) + } + }) + } +} + func TestProviderTaskNotReadyStrictClassification(t *testing.T) { tests := []struct { name string diff --git a/backend/internal/protocol/minimax_video_test.go b/backend/internal/protocol/minimax_video_test.go new file mode 100644 index 000000000..670ac3ded --- /dev/null +++ b/backend/internal/protocol/minimax_video_test.go @@ -0,0 +1,39 @@ +package protocol + +import ( + "context" + "testing" +) + +func TestMiniMaxVideoFailureMessages(t *testing.T) { + adapter := officialPackageAdapter(t, "minimax-hailuo-video-v2.yingce-plugin", "minimax-video") + const dimensionError = "content[1].image_url: media dimensions must be between 256 and 5760 pixels" + for _, tc := range []struct { + name, payload, message string + status Status + }{ + {"nested task error", `{"task":{"id":"video-1","status":"failed","error":{"code":"2013","message":"` + dimensionError + `"}}}`, dimensionError, StatusFailed}, + {"nested error without status", `{"task":{"id":"video-1","error":{"code":"2013","message":"` + dimensionError + `"}}}`, dimensionError, StatusFailed}, + {"specific error precedes envelope", `{"base_resp":{"status_code":0,"status_msg":"success"},"task":{"id":"video-1","status":"failed","message":"task failed","error":{"code":"2013","message":"` + dimensionError + `"}}}`, dimensionError, StatusFailed}, + {"base response error", `{"task_id":"video-1","base_resp":{"status_code":2013,"status_msg":"invalid input"}}`, "invalid input", StatusFailed}, + {"task message", `{"task":{"id":"video-1","status":"failed","message":"task rejected"}}`, "task rejected", StatusFailed}, + {"successful task", `{"task":{"id":"video-1","status":"succeeded","error":{"code":0},"content":{"url":"https://cdn.example/video.mp4"}}}`, "", StatusSucceeded}, + } { + t.Run(tc.name, func(t *testing.T) { + created, err := adapter.ParseCreate(context.Background(), []byte(tc.payload)) + if err != nil { + t.Fatal(err) + } + if created.TaskID != "video-1" || created.Status != tc.status || created.Message != tc.message { + t.Fatalf("create = %#v, want status %s and message %q", created, tc.status, tc.message) + } + polled, err := adapter.ParsePoll(context.Background(), PollContext{TaskID: "video-1"}, []byte(tc.payload)) + if err != nil { + t.Fatal(err) + } + if polled.TaskID != "video-1" || polled.Status != tc.status || polled.Message != tc.message { + t.Fatalf("poll = %#v, want status %s and message %q", polled, tc.status, tc.message) + } + }) + } +} diff --git a/plugin-packages/minimax-hailuo-video-v2.yingce-plugin b/plugin-packages/minimax-hailuo-video-v2.yingce-plugin index c0c06fa5eb38d9f03afec5a9a2fb1000b76ea800..58398c2f90b73895aeb04e366fa6588b3930a444 100644 GIT binary patch delta 10854 zcmZX)V{j#I(7qY-#I|ianb@{Hu{D|C#LmQ;m=oK^iEZ1qwZFHv>c8*qK3(;6b@g5S zwX6ENTQy2`kyPX%p)kR~z~I0v)Hv#pR3MQ5%ZArxO-&uBU|>?3Np=u;KnL}8sW*Wh zvE!$8ECQJ%?hGR<>oKY}BO~hk?6y-@ofX8Bvlcm5^fh)!SwhmCALwFY0Z{m2yy&cr z$zVr^!wU@Sxo2fQ`mJDIZ*c!M{KJNGfqRRvV{0r(NY`D%yO<8G0;##aLDv`i0^?&BNnv49$SBi0 z7gtU`aX~m!#|WV1c8X(NE~+nog0gpn#XBP;t}ukjd;{Nvftr1Y8QNn*TwveB z#rMbl>zSig*K~1Ky8>GsC6&VCmJKgU{Z2mSzZ?|sa(XdZ+|~<2Km9rzier$DJk@^L z)3Ytd|3wgP;pw*4g2z%A%Y)B)Ot^pddT)wFg5cW*Xo zcxy)iLqfUzK~CZxbwXT5X8YY(yK+?fpEI&|80azTyBWbBBk}Z6b1^YhL%I zJ52){p4?u%K#v_sfCR^a-9l zn22GxK&CF}7gWZfgyK+?CM2RKVgaR>XS6RSGyf7)#;XL9*8Rs7>7D0Sp%UaDBFcw6boCq_R)e3BY@YrGQ_99z^Oq6t@p4TWhZ5=-XhX&hwZEf=i zvN97pHNXjf3rzgLU3pwb({IKC~ z-VfKA*p{@`C60eeu48Z1N;T&?ipz9v|pp zrVZ4Zm85cJse7+bs(}Y_{RQZUHQZ|Yk9Gr5aYHGqn;-X_M%gni^)IV<;R&j{0+efN zmw&dAL)nEBj5nAp=GwZ#Yiakpz_ZkXktb!IJX2mn_r`E1#nQK@kaq^9H(X9KzdcM; zfRB_*zn~a>q471}dAQ~nAr(yD`6ziONN+$$SS1-I6xcHBeYc3q&xkzSck;U2MVuQJ zvQP@%j|g|v1#E7DNr{HvFdP>P>yU{XIHJYZw)BPGSlTOceS76TTK+$iD6OLjLMN>l zaj}j|Q-p~?6byP(iR(s87d@adq0VM?An(fPHmq#lTWQSpq-BfqPuOdvGg*@vZm0d5 zOBxf&*G=&(VwTD(@FTYqx${ttjZq_EY;m+|Jj(LT7H86~{uu-#pX7DC-6V9GU$(%$#3+KxkzW zcvp)k{qbMFWN4yR2f8?&K|wfdk))&KpAhK|0h?as&j%!skU%SMe0c;iD+qL*zhkwB z&z|=UpfnTk>FwO18h^E)((WcDl-zNY_pWLMFHQP>$gKXDh~k0>0U6=(lnMHDcnE>& z`ObL+^}H`Qd!n~!t%&%y<*Hd#z%_8cs>Pmj8^}+oAhPp%))9li;(mb8_`0aMlz-&E z;xNPvM&evG_#XwhIG?I{_>5^opiCQ#fF;SJ>pd#7K{c@yX)BGhd?G1Qh9SJH-;#+? zwo@i9zkYV}-jq7?f}jkuvY0K{LkDC9?|E~xsqLPHnO+$x>wmF zF$Cik{JwF&p_OZ6RuarH#fIrOU$fHcThWkOSU`+{63*yf(Gwkf0*)G+P(0Rf94lqp zp9`-4zM9xxXKwP!5d>C$nw^h6P03$Y;jEk1Jkr7AS-qyuAj#8dE`KU|%*zkpM2Nft zFb5J0DP{jG)WW-*{EVWaFj+kN_1!~-&J+!aVJ|I1=xB{$&BVAlF|AOSAG(Rtb|DOs zUzx!EM>C?9EO-383>-n)-0erQ#z&Wf5ZC6O6Whde)FWdaFA8sG2+f9I>qok`{V7qD z{g-i;oyaUInl#UZmsl={*^&vQ9wj;lLR;dI@r3xvpQt4XpQkCXKp8y3Zdep2t| zf<4641inbMv%69-9X2%z((Cri&_q9F)s{IE0_jj&`bQqG5HNRLxq(T~)D*9=xKcQ@ zM#HMlBtD{c_Z{ZXeV^wS10}lteW2=-Xf1#YNS=6sW>zcrDon~Mmi}PFC&97_+n`aP z3K`j_6a?0`$cKAyIA_oFMGfSzJG80miy-UW{fzxM!#qc!prb_K%w&VK!DuQK;7JXy z8;okDGn8xG0Gh^IA}Ksij6Y65yN}eT^xt=cCgdVyXvR~F$Y*btN{jW;D7Z|lMTnKb z@L>>6p!9`ztSr5~u5tr873Tc|;vp!>Fgv_axf+sNFhi$P zRzQ*%@K)j#Oc)UIS}ZIpH9LeVe+NhcmrxU{ZSQqO^#TjXI$~$hUHj3akyfIW1ETji z8wvU7TVAO-uo$c^ex=2*yZDpt=x9x{a<98{NB&JRiI;$*07oB5O2u(8&f zIvzi(ul%9gA94;#`3$FCcl&@{$-GuYZ zscH1%0Wa(ssFFd}9Ot7*X!8m$`pl#DY0lM)AlOeH;-r-1hYQY~CP^Ar*9&7hqX9V3 zk*E-bs605J1aHzXzw|UyQyv(T(#a@_MzAXV*GI2bUfPE0p<7!7H*CBO$vQ^$=m3p(pTp=j5Q31j`Ryy znSWAv{4};6T)gLR?@ErSGn;>AHe+WtQ*b+QGjftVX8h&PV(OLM!cHNu&0n-_)Ih0X z&ccsymog>8c*-CwYZS|}c6@7%{BU{&1}%s+$}!N5ZTt-+Btdan+;b|fx4WEJiEX|y zE$gq7LtAHZ)iX@PCTw45oqfT+RnvgKO{6>{TigUCG^b7@-m zi!j`zZqKZ#9pzWubZW~-dU!%lksavVF^lcWpgmxx^hZweYE9A@UR`rTCqb7F*q}IO zYJ=L(385w9;SJ9XUr#k(B6KZnVbRd6!*()5`!iVbJQOQgQt-)MVzS>p;C+1Ut?C?X zn5d=EJ-z!`A8UZ#x+HcD?T?VK3HCIxY%M~Lc7-=bkh)uk9<*1{R#2g`bqtEsw&QJC ziZz@H#)C*9lS2@)-vzn zF!#a^_%Z5#7BF0Q%_6C zV1uingF$gx6H6A;2h9ge*ER~%i1IsJ*EV)Jm*2O9u6*7o*@Jb{Wy5{P7iA9RPIu2! z1=--=aM+i0lVr;ToHpk`a#Lj`Ay4p1>T)eL68kcrz(}@MD;oE+RMx>g&r8P+1sV)8 z0Y#j5*ZY1s|Fc!~t~Xo7Exp+oA)iXCTq<`2HWZ`tK_|OA1C;J@@4TtK-4s==x{-u& zCVGu4uRjD_gP1f|7pW3HbM~s8ZUV4b*Beuoj^lNZPSx6;!dv@5)_bxyk=*svCf)oA z)U8DDG-GbsdAoxmZlUZ_fHGF@me$epgIi1r3HfZHi53uy{a}k%$X3!{)mAt(W#_bv zDR*-ki*a~j1ufR|F#|tWMj{0D-H(pX))wX}D*SzHLrqB3GObys01nbUrAqD(6ZZ+^ zn4V<9b%TJt>9q|Ic~{3L%-W5$97_Dn;7m^IX+hlZsjZwx;Gumw>JI)mQ74r2+Q&F5 z`p(Mlc(i8aOb@4mT3$O1@^{#XEO^j*zxMC=Dxxn}=C+g{2CG$2|80+5tK6yEhBMrw z!stI8a$+vRK}GTR;@FA zX?2aQ+K2;7xx2V;o!RcSW}I0nP17-kjh5Hz*z5p|Uy(S%t_VvAr_7k1%!_}1=dX7+ z!@v6LIPYWtW8KTjI(;^FZe4=^A&g~uZ>Y4ns#&(xN0}A-DDHpXMrJEt6{lX$EB#uB zn+PXQOq%_rgDL2g5(~Rop*KC+A$cLC;orM&G+#%^@Qm6j-&-BQ^sh&Vl$%7rOHxVM zdYxE20>M?ElPpc!&i-GeZ_GNqo0m;$c8C8ZXX@k$P{}EHv+EQm>NbK)o&$43si|#Y zJ!@;Jq^r7W`->;xKoiibt(y@SOJCF8=3Q99yUOYR*hFF(RL`HIZIJEJ|JakST6|dU zXI{y=`&7qc+DWZjF>+2u5m)Ma_O9Veyvl;rYz+HdvTVIDd2tpsbJfM@A!?vZzIk|} zx@fBlJbScWySdA5cQ$bHEnd?Ssw4e@77qQ%9ShmTpNb=-9xAeiQK*!u%$_iOXBK3I4r)HS4ep?1$e*IXQhl z9VBH|#L#7AhW40<^>V@!pgMHmgn$!D&lA3P#;uiOXWAZ`B4Mef%V*AcIpvj=W0ez? zm6c}?JOY@-Oons9>f`NNjE$G-Yg5*tNq9|N$rU*Z2t@xn*0;!5^->oOn}UF;%D}FB z4ZU&AXaQwAU92xl(olCJsvqHCCn|4thZqP&zET0n)9oq9he?KT-fO2-9bFAwi1PD` zV`XNmvH9X`OS#L(H70&qiyW3!8m3G{q!opZ#kVI0_beg{R#)lf^Fs$3=BB%8O_6Iy zRd0vqli}Jihtfb2eC`~F+B9HULu~$aOeVq2AEa1$j7b4taN1CzeC%xxH}f`^H7)7QB(BLTCy5Fj;sqFjC!svS85N%J_)cxS3lQb>Jur$#gt_$f6eIeOY~b@xFG45MV>Y@u`#o?F&Tb`}d^LFcaMos5zfo zqFD*9px&*aIs%xm@6Ahzdh?LvOJMlfT}#JVLNiezKeT`{nZJ9qk$C&TXgB2S9E5P6 zpS%d(REf=A4guk+rfEyiGBFBmWkmfQv|>7ztYIaMQ~TR&>Ejh56VKeVzw@I%y~mdM zW8)WRr7v<~r5Dhe6?hoPS^8Ue{n@Fz0B9BKb+sY;6*9szTBy@M`0v)3Oi}m)E)Lf)T$aO7f&G;1>K)ezcPuh3L zA^h^kiQeWjI(SvI!qn{})cagut~PuP>v9^?G55}g%MhqFJ_c*@_?4IQIuqo?*v*-n z3;pu_^)O-@|L^TA?&wnY#i25r>zHIgOFQ=7t@mfY0xR=8j@>v8{`vV(1(lmw>ys@g zy;V)$ADpA&<96knbqgE^wQ3;U35$B9QQ}prOvISW(KrCd+zpie9+|AN2*$;xU9xLs zehZBYehAc`!=@Hb_>u=xdo|OVEY6duN$!gui1v0s`+}A%D&Ka5+!6HDnU7jiPKK3o zyD`rTFtvzM`l}-P)j2IS2Kp4itWqi0a%X?~r)+xBbVvhNfoe#`4+h}MF(|M4woVC# z63M2x(_;+QRmUd~!7pU!RuZ|ktI47(D{^X5nFOvA9WPd!uHDgBn#K;AnjhHseZo?kbWhp0`(^KLgn5LWll4d4tZzK*4ntGm z`vXHR1B#KhBsNhijx4dT2#KqpQ}JiUeisAt09(@1%nm(k+zq)(LFYV&N#{Jpa(kwhs#^Ea}O=%sWdqS58cw!@|AwL3}b^VX=m!J(9i&X3A&g!3`z`>Rri zHr;FY#{Mmv*gUUgPIPFDr>mWR3y8P#_&_}WBZ?TqzQ~vv)#foq0aL;qL3mc-^8=XQ zURKMF#a}0h!TpyCtD!K_Z(rA$uobYj7<^n!VZ z8rs?mHE<_XRcQ+fhw0EmZ6qfeI^D3k+)}Bw=gU=n?w6}A%T%UW&!UVDXz3ZuFTjq+ zAK>?SYdZd=4}^j@31-4RrV7GtGp@pNsCrYg;0wttM-$|gj5pH{H^w_D;J0Ls-Ket>PHuHpR+}5BA29+c5B8VCEl0Qwz|AmJIE5Wy;RlP#x{&f zOH0*#dJdIHcoeChqCiWB!A^l?Um$y*FQ&%cD%t)Qk|oQ+R+FKHRx4u2Cp;#*<_%`M zAm2?uIt|aK5ab#8Nd)7ZD=TE|^%B9hNR5a=Q1DE*qM(cOy?H)M(zau94+UKy?Tbe)8>OlP**jx-A z*4S9>$MIy-TIYJA!950Z%b`^oxNL#J)?cs^<=yp>nUuZ$I-ljoC!&Xi>Zf9&lXQl? zI_F*b;KcnoeLAh+?`(%MvP=(z60jOAVGVq9S^5shv6(nuC@%`!xHvgW!1e%h2rY{H z2y_2utd}X;t)U0Xi;E4IAx%~ohG8Mjv5`?1l%u09IugpvQ2p8Dy z=LWC4;VtSsn(%6h7Y&PL$xU65wy}QMH2PsbQu*y&&Cx|GlD&hdwKTsye+B<#?{AMw z$p8}FT2&&_Cp^vN(F&tUiyz|)_QPPU-ZwN!FlZxv`eVWAx?Tpe0>BH)l`n^P%m*B{ z1f9Wzk*CcC%bzc^!{L!`Neb>x7L|3awhD%(6e{C~W^F? zkrL-rLkVA<{)t+xfxlvSViVFpfq98YK~@``>xAvID}<6pJXX~_*f<2497B`qkBjFP ze8fJdrs(7o0sD-^Awa`@82hbu#V7dVYg}==p(<7L3t_bFLoEHin9-iTvp8Q>VLtzl zQZ#vkq5A0`17;^?SV$|y24i%>c7|91v)$* zn~NicqNvjYbgGTg#znTmx55(_?`#7>VeVAMi+$%RyFFvt<$5aZUe~H~J2%(($hDQ4f@I0?YXqh$OhR7pCU=eY(f}j9zY4s^LJZ_yJibI@vnGV%%l7d?)_C? z2agb_P~g#UVDZqw=Gq(O)iJ(XoaxxatIti6q5*vhxxxjL@4UXs5Atj%3q|Ws3w)k=I=hj!i_ybxy1r-#z0OvHkQi3DbBEnSvPsO@)#z-(TCsu z*Ygzj=c}dj!$sTo46!|YmbK!)urR=*o0{Ah5lL~E0TZjUS|4||Ek0#H@*|ss$plPe z(nK+NX?L9-z}lAie5hK-L+5$~PO9Kys`f8Ps8&y#!?LMg^LUPCfp7_j+)DS!CDTO681y0{n~Y(etr`(7i5aJVbI_0R0R+E~ z;>KZYq`520t8>Wa!Zk&-Ktfc4H#sO;-zqN6$aQbWq_1`+uk=&;f#&b5?+ERSg#Tch z?F_i0Qflq8il?g%aR2`RpYm*2)oy~0ZM1bRe8cYp>r_x23;%-8}AJGzn%4Q0@VD8#^%4wCS+ zC)y+voC~I_GJa3bKqkDxpa^(}hP3r*czASg%YJ!&2SR06vhqX*WcR>l(wN(yawV@v z`qGFNK*h!JGvi0~@o(faXcc9{_@vraXzPG5ui`Z8#x3F$vo4FhR{ZHNO>(tb#gJ7Mslh z+uLc%rJITDeEkBk^a@Rp$4Z2zF9A;F$FhMW7IUnygc4#Csl-pn04oz+BSG8-19jDC zQQ&T#Q=eLLm#=DWOMsI&(3b+A@vwQK15;2~?68l64zMzW-o!JlK7Q_plykL)pEEZv z@C^PYf@{Up^ImntsP_)Vm&t5l?)p^Z&RkTbBTq6;bgi}_2Qt6wWT3x_=9X3q^uD(U z^de5E1|3WY&kTv1m_OC|l^FVXrB) zC{8fN@$_p(FsOe4RUZ-a3r}zf9H4|Kl;Oz37@NBjXwuz5go@WA6pKP@DV=J1RP(x{ zr2;Lzew$i>4pW5XHm#~BluJ-Bt2%G*DE+HgfiX@%^TdF@)7mOp><#JOZ0=T#K zNKY;dN-$}2*cAecsWtm_rD3-L~hN!pN>1}ZhwXf z^CDBX3!vWwCVKk?`dWxyBV?Lu|9xiu>$QBLKZrJsaB3*ma}*D*sf4xKC2Bza|V*kdZ(f9kVs*BG4+{!aCN^qWw7yr?J*;Mf+6ZOC2d1t_Vhu z3t-Y1(D4~Ne89W4B#SJUo9uS$XYAxXfhbxGROJ_$T~d^ZY$4K&F~4BEKUFj7WOLUA zC;VM!@cNlSaj?Er1;;K=TshMWuFxNxign~YQGX9BW) z$CQO5+Q+0b^F52rA(xDhA4DM=MkEcFh~9dbT|6h;?l=1_XW0U(LSYFZyl_x=Yd5GY zsFJ87N6b-H-+Ke%Q_>~1Y9@1Z&5+`MPfCM|`xnV8VZZERB7~fcLqi^eNMvOkyAGPh zzI@Bb5n@N%QNGjrMdr*sv+ZQ;n`x z$YVWM)US>KI;yPL1c<@;eF(&3P8aprhkEGFbN|L&RGxq|(d=mU*ev1}L+0@?ZG6L( z68b_US}db%to%}mJ;5cNcYg5rAe?2oAuaZyq-$E6j7@~jf+9WHz3m{f@LS$64IS0~ zDl7zTqbADNT1jie+o6Maz0rLmr}mQxl2A=W_nFYA`IH2OoBTpnpG>f+SdiiJFcO@K zrd!Hqu{zL}cuBoos$>4Lwe;R%>y!yFC#Yen3wk2?!gY%ac<-UjV+S%4YMgxM25>pkkh(s~oTGLa&K=u^ zWrV-L6f}xgPl1WWyNw_Qz=yZ8r#g)2%c&zpN)OKg(1atB0Psgq)2yJ9DQKBF#MOJy z7~^g4gxD6iTA-V3iXqR5*cm0}Ui#_P5LNyISb>cSt;L@;wbR^skd7RA{BBTx{BS%jZb>1gxC2SmH};nq}>r-ono6P)=>v$xxp?T_!RiUmFjdJK#h1z}05Xk2e zWN7%I6@K4ngY*igU)y|xYj52|+H1Dhj}V1D?MJ*y4MzNsf^>HtP;hZ5_a@i_`x@Do z_s6P`dfFF5({>B6pYF6wzwb#k$!q*OvnA7SqRy9KHGGI0{HYjm7574b&a}A_x_NKK z)q522x#ZUDTj`lGXvsu%6Qw|#HIs_{12^QtB}}TlEzv5 zp&fnt+HPs84LoT|iN7s1mV-tj@iv#}g>!W*9f=@|OD2sP$5M@_xE&`KXG*=(sxsF$ zTT)Fo`l;Ut)GxRU8UFI7BJdi3m*M{GA(Y0t2+&QfNfuoTK+VS&?GtN)045C10q&p> z)iIIs19tU-_7N8`@1Q6|CF(fC1Or3VvQ#<41=%n2yJXHSxX`mP;+i>hkDc@j4g=H9_49!E8d4^}{QSsYd`)frxg{00 zKb3U}9;q?v3=%VIw7=IAfk#p^hE)A0yhte0c;*Qd%JG_THEHXysrFGZ{IiL)#D7r- zo7ZHW9V-lfY;QCLTbCb?7ogW=@Pv*zDLeRC3MS)_|K*r*9ZJ3tr&LJoKZSh%5fBq} zAbY#jPfh|GkFOeqdG$9Vi6oS`#&uP-vzL-9N||XDi9TVyUl$Du06btK_=B1$fD@&1 zZvIPj39n$sEE!V#3}R1C(ECCyZACklI$ZTx_tjmN7#)%AE{CNLBQ|>Qc5c=6$2ICG z^$$e7m#kp>^Sd;`gQEbbL;{$dy@+#lr?$@+hk+CAEi!epO#@}>A1A&Wn{UYJ#vMhq zJ!;7}Rcd&zqIK!S@IXaOFEe^`W9sCJ7|8l6cEDt6DZpzp3+NwBPtik2S|TlmF_@h` zw({S$q2CX%uK*Fk{u(hU+3C`lJ!t}Wv7euUCr?=HO&@!55eMyu$)F|k0Nwuceegx%RmY%J0{+;1 zP~@!4ZV^`>R1QB2bS@Iwk7n8)$hB^7=Q~*G6ayTr;1&>=9o3VR9zlvLe3v&SRJ9Ky4SUS zIX8UZ>x1mV1mTG#B`go;tfOVRF;=z^-h?uU&@nnB(4y`vDT8=NMPcsQ_B0x57nUX( z8N4LPF==9_BORy$C&&C}&op?wS$qYLlZQR0=x%;gA5|kvt3u|U6-*}e^^pyx zjGGSc{P~7ekq3vsg#14y&ZGc3EZ+a%1z1tw|NLr_q7sUd%nla+AKCx%c!GhU{g3j0 yUeY8TvEu(4_Wwg7|F`Uah6`c*A3);HZ0qLas3H#y^FM9K|Dx`{yvy)E)&B*3G0gn{ delta 10212 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 rA+Z0w903Ci3}EJDXKm%~!EEpD?4+sy4f8()`。 +- 请求:JSON;结果:异步任务,完成后下载并持久化临时 URL。 + +## 请求字段 + +| 上游字段 | 类型 | 必填 | 统一来源 | 说明 | +| --- | --- | --- | --- | --- | +| `model` | string | 是 | `request.model` | Hailuo/MiniMax 视频模型 ID。 | +| `content` | block[] | 是 | prompt + inputs | 第一个 block 为 text;媒体 block 依次按 order 输出。 | +| `content[].type` | enum | 是 | media.kind | `text/image_url/video_url/audio_url`。 | +| `content[].text` | string | 文本必填 | `request.prompt` | 不重复发送顶层 prompt。 | +| `content[].image_url.url` | string | 图片 block 必填 | `media.value` | 由宿主保证公网 URL。 | +| `content[].video_url.url` | string | 视频 block 必填 | `media.value` | 参考视频。 | +| `content[].audio_url.url` | string | 音频 block 必填 | `media.value` | 参考音频/声音。 | +| `content[].role` | string | 媒体必填 | `media.role` | `first_frame/last_frame/reference_image/reference_video/reference_audio/reference_voice`。 | +| `images` | media[] | 否 | image content blocks | 图片按显式 role 与 order 映射,不使用数组下标猜测。 | +| `videos` | media[] | 否 | video content blocks | 参考视频按 role 与 order 映射。 | +| `audios` | media[] | 否 | audio content blocks | 参考音频或声音按 role 与 order 映射。 | +| `duration` | integer | 是 | `request.duration` | 4-15 秒。 | +| `resolution` | string | 否 | `request.resolution` | 默认 `768P`;实际枚举按模型 profile。 | +| `ratio` | string | 否 | `request.aspectRatio` | 首尾帧模式为 `adaptive`,其余默认 `16:9`。 | +| `aigc_watermark` | boolean | 否 | `request.watermark` | 是否带 AIGC 水印。 | +| `generate_audio` | boolean | 否 | `request.generateAudio` | 模型支持时生成音频。 | +| `seed` | integer | 否 | `providerOptions.minimax-video.seed` | 随机种子。 | +| `prompt_optimizer` | boolean | 否 | `providerOptions.minimax-video.prompt_optimizer` | 提示词优化开关。 | + +## 强校验 + +- 图片最多 9 张、视频最多 3 个、音频最多 3 个。 +- 首帧和尾帧各最多 1 张;尾帧不能脱离首帧。 +- 首尾帧模式不能混入 `reference_image/subject_reference/style_reference`。 +- duration 超出 4-15 秒直接失败,不静默裁剪。 + +## 中转兼容边界 + +本插件对应 MiniMax `/v2/video_generation` 原生线协议。NewAPI Channel 1、Channel 2 和自定义 OpenAI Videos 中转必须选择各自独立插件;不能只根据模型名包含 `H3` 自动切协议。 + +## 响应映射 + +- `task.id` / `task_id` / `id` 映射为统一任务 ID;查询响应缺省时保留已有任务 ID。 +- `task.status` / `status` 映射为 `pending/processing/succeeded/failed/cancelled`。 +- `task.content.url` / `content.url` / `video_url` / `file.url` 映射为统一视频结果。 +- 创建和查询响应优先读取 `task.error.message`,再读取 `task.message` / `base_resp.status_msg` / `message` / `error.message`;具体失败原因通过任务错误返回给用户。 +- `task.error.code` / `base_resp.status_code` / `error.code` 非成功值均判定为失败,即使响应 HTTP 状态为 200 或未提供任务状态。 +- 完成后的临时媒体地址由宿主立即下载并持久化。 + +例如,参考图片尺寸不符合要求时,上游返回 `task.error.code: "2013"` 和 `task.error.message: "content[1].image_url: media dimensions must be between 256 and 5760 pixels"`。任务应展示这条具体消息;只有上游未提供可读取的错误原因时,才使用通用失败提示。 + + +## Manifest 完整接口定义 + +以下 JSON 与插件包内实际 `manifest.json` 逐字段一致,覆盖插件身份、权限、配置、鉴权、参数、校验、创建、Agent、查询、取消、结果下载、响应和 Agent 响应映射。`documentation` 字段的值就是当前完整文档;为避免文档在自身内部无限递归,JSON 中仅用等义占位文本表示正文。 + +```json +{ + "apiVersion": "yingce.plugin/v2", + "id": "minimax-hailuo-video-v2", + "name": "MiniMax Hailuo Video V2 / H3", + "version": "2.0.1", + "author": "MiniMax / 影策", + "description": "MiniMax / Hailuo V2 视频生成协议,保留首帧、尾帧、参考图、视频和音频角色语义。", + "documentation": "<当前插件的完整 documentation,由 README.md 与 docs/interface.md 拼接而成;为避免 JSON 递归,此处不重复展开正文。>", + "permissions": [ + "generation.run", + "media.read" + ], + "configuration": { + "fields": [ + { + "name": "apiKey", + "type": "secret", + "label": "API Key", + "required": true + } + ] + }, + "contributes": { + "providers": [ + { + "id": "minimax-video", + "label": "MiniMax Hailuo Video V2 / H3", + "capabilities": [ + "video" + ], + "scopes": [ + "admin.system-channel", + "user.custom-channel", + "canvas", + "creation", + "agent" + ], + "baseUrl": "https://api.minimax.io", + "requiresPublicMediaUrls": true, + "auth": { + "type": "bearer", + "field": "apiKey" + }, + "parameters": [ + { + "name": "model", + "type": "string", + "required": true, + "mapping": "model", + "description": "MiniMax/Hailuo 视频模型 ID。" + }, + { + "name": "prompt", + "type": "string", + "required": true, + "mapping": "content[type=text].text", + "description": "仅写入 text content block,避免与顶层 prompt 重复。" + }, + { + "name": "images", + "type": "media[]", + "mapping": "content[type=image_url]", + "description": "保留 first_frame、last_frame、reference_image 等 role。" + }, + { + "name": "videos", + "type": "media[]", + "mapping": "content[type=video_url]", + "description": "映射为 reference_video。" + }, + { + "name": "audios", + "type": "media[]", + "mapping": "content[type=audio_url]", + "description": "映射为 reference_audio/reference_voice。" + }, + { + "name": "duration", + "type": "integer", + "required": true, + "values": [ + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15" + ], + "mapping": "duration", + "description": "4-15 秒。" + }, + { + "name": "resolution", + "type": "string", + "mapping": "resolution", + "description": "如 768P、1080P、2K;按模型能力限制。" + }, + { + "name": "aspectRatio", + "type": "string", + "mapping": "ratio", + "description": "首尾帧模式自动使用 adaptive;其他模式使用用户比例。" + }, + { + "name": "watermark", + "type": "boolean", + "mapping": "aigc_watermark", + "description": "AIGC 水印。" + }, + { + "name": "generateAudio", + "type": "boolean", + "mapping": "generate_audio", + "description": "模型支持时生成音频。" + }, + { + "name": "seed", + "type": "integer", + "mapping": "seed", + "description": "providerOptions.minimax-video.seed。" + }, + { + "name": "prompt_optimizer", + "type": "boolean", + "mapping": "prompt_optimizer", + "description": "providerOptions.minimax-video.prompt_optimizer。" + } + ], + "validations": [ + { + "assert": { + "$lte": [ + { + "$len": { + "$ref": "request.images" + } + }, + 9 + ] + }, + "message": "MiniMax H3 最多支持 9 张图片" + }, + { + "assert": { + "$lte": [ + { + "$len": { + "$ref": "request.videos" + } + }, + 3 + ] + }, + "message": "MiniMax H3 最多支持 3 个参考视频" + }, + { + "assert": { + "$lte": [ + { + "$len": { + "$ref": "request.audios" + } + }, + 3 + ] + }, + "message": "MiniMax H3 最多支持 3 个参考音频" + }, + { + "assert": { + "$and": [ + { + "$gte": [ + { + "$ref": "request.duration" + }, + 4 + ] + }, + { + "$lte": [ + { + "$ref": "request.duration" + }, + 15 + ] + } + ] + }, + "message": "MiniMax H3 duration 必须在 4-15 秒之间" + }, + { + "assert": { + "$lte": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$eq": [ + { + "$ref": "image.role" + }, + "first_frame" + ] + } + } + } + }, + 1 + ] + }, + "message": "MiniMax H3 最多只能有一个首帧" + }, + { + "assert": { + "$lte": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$eq": [ + { + "$ref": "image.role" + }, + "last_frame" + ] + } + } + } + }, + 1 + ] + }, + "message": "MiniMax H3 最多只能有一个尾帧" + }, + { + "assert": { + "$or": [ + { + "$eq": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$eq": [ + { + "$ref": "image.role" + }, + "last_frame" + ] + } + } + } + }, + 0 + ] + }, + { + "$eq": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$eq": [ + { + "$ref": "image.role" + }, + "first_frame" + ] + } + } + } + }, + 1 + ] + } + ] + }, + "message": "MiniMax H3 使用尾帧时必须同时提供首帧" + }, + { + "assert": { + "$not": { + "$and": [ + { + "$gt": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$in": [ + { + "$ref": "image.role" + }, + [ + "first_frame", + "last_frame" + ] + ] + } + } + } + }, + 0 + ] + }, + { + "$gt": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$in": [ + { + "$ref": "image.role" + }, + [ + "reference_image", + "subject_reference", + "style_reference" + ] + ] + } + } + } + }, + 0 + ] + } + ] + } + }, + "message": "MiniMax H3 首尾帧模式不能混入普通角色或风格参考图" + } + ], + "create": { + "method": "POST", + "path": "/v2/video_generation", + "contentType": "application/json", + "body": { + "model": { + "$ref": "request.model" + }, + "content": { + "$concatArrays": [ + [ + { + "type": "text", + "text": { + "$ref": "request.prompt" + } + } + ], + { + "$map": { + "from": { + "$sortByOrder": { + "$ref": "request.images" + } + }, + "as": "media", + "in": { + "type": "image_url", + "image_url": { + "url": { + "$ref": "media.value" + } + }, + "role": { + "$coalesce": [ + { + "$ref": "media.role" + }, + "reference_image" + ] + } + } + } + }, + { + "$map": { + "from": { + "$sortByOrder": { + "$ref": "request.videos" + } + }, + "as": "media", + "in": { + "type": "video_url", + "video_url": { + "url": { + "$ref": "media.value" + } + }, + "role": { + "$coalesce": [ + { + "$ref": "media.role" + }, + "reference_video" + ] + } + } + } + }, + { + "$map": { + "from": { + "$sortByOrder": { + "$ref": "request.audios" + } + }, + "as": "media", + "in": { + "type": "audio_url", + "audio_url": { + "url": { + "$ref": "media.value" + } + }, + "role": { + "$coalesce": [ + { + "$ref": "media.role" + }, + "reference_audio" + ] + } + } + } + } + ] + }, + "duration": { + "$ref": "request.duration" + }, + "resolution": { + "$coalesce": [ + { + "$ref": "request.resolution" + }, + "768P" + ] + }, + "ratio": { + "$if": { + "condition": { + "$gt": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$in": [ + { + "$ref": "image.role" + }, + [ + "first_frame", + "last_frame" + ] + ] + } + } + } + }, + 0 + ] + }, + "then": "adaptive", + "else": { + "$coalesce": [ + { + "$ref": "request.aspectRatio" + }, + "16:9" + ] + } + } + }, + "aigc_watermark": { + "$ref": "request.watermark" + }, + "generate_audio": { + "$ref": "request.generateAudio" + }, + "seed": { + "$omitEmpty": { + "$ref": "request.providerOptions.minimax-video.seed" + } + }, + "prompt_optimizer": { + "$omitEmpty": { + "$ref": "request.providerOptions.minimax-video.prompt_optimizer" + } + } + } + }, + "poll": { + "method": "GET", + "path": "/v2/query/video_generation/{{taskId}}" + }, + "response": { + "taskId": { + "$coalesce": [ + { + "$ref": "response.task.id" + }, + { + "$ref": "response.task_id" + }, + { + "$ref": "response.id" + }, + { + "$ref": "taskId" + } + ] + }, + "status": { + "$coalesce": [ + { + "$ref": "response.task.status" + }, + { + "$ref": "response.status" + }, + "pending" + ] + }, + "message": { + "$coalesce": [ + { + "$ref": "response.task.error.message" + }, + { + "$ref": "response.task.message" + }, + { + "$ref": "response.base_resp.status_msg" + }, + { + "$ref": "response.message" + }, + { + "$ref": "response.error.message" + } + ] + }, + "videos": { + "$coalesce": [ + { + "$ref": "response.task.content.url" + }, + { + "$ref": "response.content.url" + }, + { + "$ref": "response.video_url" + }, + { + "$ref": "response.file.url" + } + ] + }, + "errorPaths": [ + "task.error.code", + "base_resp.status_code", + "error.code" + ], + "resultEphemeral": true + } + } + ] + } +} +``` + diff --git a/plugin-packages/minimax-hailuo-video-v2/manifest.json b/plugin-packages/minimax-hailuo-video-v2/manifest.json new file mode 100644 index 000000000..d67a59634 --- /dev/null +++ b/plugin-packages/minimax-hailuo-video-v2/manifest.json @@ -0,0 +1,609 @@ +{ + "apiVersion": "yingce.plugin/v2", + "id": "minimax-hailuo-video-v2", + "name": "MiniMax Hailuo Video V2 / H3", + "version": "2.0.1", + "author": "MiniMax / 影策", + "description": "MiniMax / Hailuo V2 视频生成协议,保留首帧、尾帧、参考图、视频和音频角色语义。", + "documentation": "# MiniMax Hailuo Video V2 / H3 协议插件\n\n该包贡献兼容旧渠道 ID `minimax-video` 的声明式 provider。画布、创作页和短剧生产只提交统一 `GenerationRequest`;插件将图片、视频和音频按显式 role 转成 MiniMax `content[]`。\n\n重点修复:\n\n- `resolution` 直接来自统一分辨率,不再遗漏。\n- 比例写入上游实际使用的 `ratio`;首尾帧模式强制 `adaptive`。\n- `watermark` 映射为 `aigc_watermark`。\n- `generateAudio` 映射为 `generate_audio`。\n- prompt 只放入 text block,不再同时发送顶层 prompt。\n- 首帧、尾帧和 reference image 依据 role 转换,不按 `0/1` 猜测。\n\n完整字段见 [docs/interface.md](docs/interface.md)。\n\n---\n\n# MiniMax Hailuo Video V2 / H3 接口字段\n\n## 入口和生命周期\n\n- 创建:`POST /v2/video_generation`。\n- 查询:`GET /v2/query/video_generation/{taskId}`。\n- 鉴权:`Authorization: Bearer `。\n- 请求:JSON;结果:异步任务,完成后下载并持久化临时 URL。\n\n## 请求字段\n\n| 上游字段 | 类型 | 必填 | 统一来源 | 说明 |\n| --- | --- | --- | --- | --- |\n| `model` | string | 是 | `request.model` | Hailuo/MiniMax 视频模型 ID。 |\n| `content` | block[] | 是 | prompt + inputs | 第一个 block 为 text;媒体 block 依次按 order 输出。 |\n| `content[].type` | enum | 是 | media.kind | `text/image_url/video_url/audio_url`。 |\n| `content[].text` | string | 文本必填 | `request.prompt` | 不重复发送顶层 prompt。 |\n| `content[].image_url.url` | string | 图片 block 必填 | `media.value` | 由宿主保证公网 URL。 |\n| `content[].video_url.url` | string | 视频 block 必填 | `media.value` | 参考视频。 |\n| `content[].audio_url.url` | string | 音频 block 必填 | `media.value` | 参考音频/声音。 |\n| `content[].role` | string | 媒体必填 | `media.role` | `first_frame/last_frame/reference_image/reference_video/reference_audio/reference_voice`。 |\n| `images` | media[] | 否 | image content blocks | 图片按显式 role 与 order 映射,不使用数组下标猜测。 |\n| `videos` | media[] | 否 | video content blocks | 参考视频按 role 与 order 映射。 |\n| `audios` | media[] | 否 | audio content blocks | 参考音频或声音按 role 与 order 映射。 |\n| `duration` | integer | 是 | `request.duration` | 4-15 秒。 |\n| `resolution` | string | 否 | `request.resolution` | 默认 `768P`;实际枚举按模型 profile。 |\n| `ratio` | string | 否 | `request.aspectRatio` | 首尾帧模式为 `adaptive`,其余默认 `16:9`。 |\n| `aigc_watermark` | boolean | 否 | `request.watermark` | 是否带 AIGC 水印。 |\n| `generate_audio` | boolean | 否 | `request.generateAudio` | 模型支持时生成音频。 |\n| `seed` | integer | 否 | `providerOptions.minimax-video.seed` | 随机种子。 |\n| `prompt_optimizer` | boolean | 否 | `providerOptions.minimax-video.prompt_optimizer` | 提示词优化开关。 |\n\n## 强校验\n\n- 图片最多 9 张、视频最多 3 个、音频最多 3 个。\n- 首帧和尾帧各最多 1 张;尾帧不能脱离首帧。\n- 首尾帧模式不能混入 `reference_image/subject_reference/style_reference`。\n- duration 超出 4-15 秒直接失败,不静默裁剪。\n\n## 中转兼容边界\n\n本插件对应 MiniMax `/v2/video_generation` 原生线协议。NewAPI Channel 1、Channel 2 和自定义 OpenAI Videos 中转必须选择各自独立插件;不能只根据模型名包含 `H3` 自动切协议。\n\n## 响应映射\n\n- `task.id` / `task_id` / `id` 映射为统一任务 ID;查询响应缺省时保留已有任务 ID。\n- `task.status` / `status` 映射为 `pending/processing/succeeded/failed/cancelled`。\n- `task.content.url` / `content.url` / `video_url` / `file.url` 映射为统一视频结果。\n- 创建和查询响应优先读取 `task.error.message`,再读取 `task.message` / `base_resp.status_msg` / `message` / `error.message`;具体失败原因通过任务错误返回给用户。\n- `task.error.code` / `base_resp.status_code` / `error.code` 非成功值均判定为失败,即使响应 HTTP 状态为 200 或未提供任务状态。\n- 完成后的临时媒体地址由宿主立即下载并持久化。\n\n例如,参考图片尺寸不符合要求时,上游返回 `task.error.code: \"2013\"` 和 `task.error.message: \"content[1].image_url: media dimensions must be between 256 and 5760 pixels\"`。任务应展示这条具体消息;只有上游未提供可读取的错误原因时,才使用通用失败提示。\n\n\n## Manifest 完整接口定义\n\n以下 JSON 与插件包内实际 `manifest.json` 逐字段一致,覆盖插件身份、权限、配置、鉴权、参数、校验、创建、Agent、查询、取消、结果下载、响应和 Agent 响应映射。`documentation` 字段的值就是当前完整文档;为避免文档在自身内部无限递归,JSON 中仅用等义占位文本表示正文。\n\n```json\n{\n \"apiVersion\": \"yingce.plugin/v2\",\n \"id\": \"minimax-hailuo-video-v2\",\n \"name\": \"MiniMax Hailuo Video V2 / H3\",\n \"version\": \"2.0.1\",\n \"author\": \"MiniMax / 影策\",\n \"description\": \"MiniMax / Hailuo V2 视频生成协议,保留首帧、尾帧、参考图、视频和音频角色语义。\",\n \"documentation\": \"<当前插件的完整 documentation,由 README.md 与 docs/interface.md 拼接而成;为避免 JSON 递归,此处不重复展开正文。>\",\n \"permissions\": [\n \"generation.run\",\n \"media.read\"\n ],\n \"configuration\": {\n \"fields\": [\n {\n \"name\": \"apiKey\",\n \"type\": \"secret\",\n \"label\": \"API Key\",\n \"required\": true\n }\n ]\n },\n \"contributes\": {\n \"providers\": [\n {\n \"id\": \"minimax-video\",\n \"label\": \"MiniMax Hailuo Video V2 / H3\",\n \"capabilities\": [\n \"video\"\n ],\n \"scopes\": [\n \"admin.system-channel\",\n \"user.custom-channel\",\n \"canvas\",\n \"creation\",\n \"agent\"\n ],\n \"baseUrl\": \"https://api.minimax.io\",\n \"requiresPublicMediaUrls\": true,\n \"auth\": {\n \"type\": \"bearer\",\n \"field\": \"apiKey\"\n },\n \"parameters\": [\n {\n \"name\": \"model\",\n \"type\": \"string\",\n \"required\": true,\n \"mapping\": \"model\",\n \"description\": \"MiniMax/Hailuo 视频模型 ID。\"\n },\n {\n \"name\": \"prompt\",\n \"type\": \"string\",\n \"required\": true,\n \"mapping\": \"content[type=text].text\",\n \"description\": \"仅写入 text content block,避免与顶层 prompt 重复。\"\n },\n {\n \"name\": \"images\",\n \"type\": \"media[]\",\n \"mapping\": \"content[type=image_url]\",\n \"description\": \"保留 first_frame、last_frame、reference_image 等 role。\"\n },\n {\n \"name\": \"videos\",\n \"type\": \"media[]\",\n \"mapping\": \"content[type=video_url]\",\n \"description\": \"映射为 reference_video。\"\n },\n {\n \"name\": \"audios\",\n \"type\": \"media[]\",\n \"mapping\": \"content[type=audio_url]\",\n \"description\": \"映射为 reference_audio/reference_voice。\"\n },\n {\n \"name\": \"duration\",\n \"type\": \"integer\",\n \"required\": true,\n \"values\": [\n \"4\",\n \"5\",\n \"6\",\n \"7\",\n \"8\",\n \"9\",\n \"10\",\n \"11\",\n \"12\",\n \"13\",\n \"14\",\n \"15\"\n ],\n \"mapping\": \"duration\",\n \"description\": \"4-15 秒。\"\n },\n {\n \"name\": \"resolution\",\n \"type\": \"string\",\n \"mapping\": \"resolution\",\n \"description\": \"如 768P、1080P、2K;按模型能力限制。\"\n },\n {\n \"name\": \"aspectRatio\",\n \"type\": \"string\",\n \"mapping\": \"ratio\",\n \"description\": \"首尾帧模式自动使用 adaptive;其他模式使用用户比例。\"\n },\n {\n \"name\": \"watermark\",\n \"type\": \"boolean\",\n \"mapping\": \"aigc_watermark\",\n \"description\": \"AIGC 水印。\"\n },\n {\n \"name\": \"generateAudio\",\n \"type\": \"boolean\",\n \"mapping\": \"generate_audio\",\n \"description\": \"模型支持时生成音频。\"\n },\n {\n \"name\": \"seed\",\n \"type\": \"integer\",\n \"mapping\": \"seed\",\n \"description\": \"providerOptions.minimax-video.seed。\"\n },\n {\n \"name\": \"prompt_optimizer\",\n \"type\": \"boolean\",\n \"mapping\": \"prompt_optimizer\",\n \"description\": \"providerOptions.minimax-video.prompt_optimizer。\"\n }\n ],\n \"validations\": [\n {\n \"assert\": {\n \"$lte\": [\n {\n \"$len\": {\n \"$ref\": \"request.images\"\n }\n },\n 9\n ]\n },\n \"message\": \"MiniMax H3 最多支持 9 张图片\"\n },\n {\n \"assert\": {\n \"$lte\": [\n {\n \"$len\": {\n \"$ref\": \"request.videos\"\n }\n },\n 3\n ]\n },\n \"message\": \"MiniMax H3 最多支持 3 个参考视频\"\n },\n {\n \"assert\": {\n \"$lte\": [\n {\n \"$len\": {\n \"$ref\": \"request.audios\"\n }\n },\n 3\n ]\n },\n \"message\": \"MiniMax H3 最多支持 3 个参考音频\"\n },\n {\n \"assert\": {\n \"$and\": [\n {\n \"$gte\": [\n {\n \"$ref\": \"request.duration\"\n },\n 4\n ]\n },\n {\n \"$lte\": [\n {\n \"$ref\": \"request.duration\"\n },\n 15\n ]\n }\n ]\n },\n \"message\": \"MiniMax H3 duration 必须在 4-15 秒之间\"\n },\n {\n \"assert\": {\n \"$lte\": [\n {\n \"$len\": {\n \"$filter\": {\n \"from\": {\n \"$ref\": \"request.images\"\n },\n \"as\": \"image\",\n \"where\": {\n \"$eq\": [\n {\n \"$ref\": \"image.role\"\n },\n \"first_frame\"\n ]\n }\n }\n }\n },\n 1\n ]\n },\n \"message\": \"MiniMax H3 最多只能有一个首帧\"\n },\n {\n \"assert\": {\n \"$lte\": [\n {\n \"$len\": {\n \"$filter\": {\n \"from\": {\n \"$ref\": \"request.images\"\n },\n \"as\": \"image\",\n \"where\": {\n \"$eq\": [\n {\n \"$ref\": \"image.role\"\n },\n \"last_frame\"\n ]\n }\n }\n }\n },\n 1\n ]\n },\n \"message\": \"MiniMax H3 最多只能有一个尾帧\"\n },\n {\n \"assert\": {\n \"$or\": [\n {\n \"$eq\": [\n {\n \"$len\": {\n \"$filter\": {\n \"from\": {\n \"$ref\": \"request.images\"\n },\n \"as\": \"image\",\n \"where\": {\n \"$eq\": [\n {\n \"$ref\": \"image.role\"\n },\n \"last_frame\"\n ]\n }\n }\n }\n },\n 0\n ]\n },\n {\n \"$eq\": [\n {\n \"$len\": {\n \"$filter\": {\n \"from\": {\n \"$ref\": \"request.images\"\n },\n \"as\": \"image\",\n \"where\": {\n \"$eq\": [\n {\n \"$ref\": \"image.role\"\n },\n \"first_frame\"\n ]\n }\n }\n }\n },\n 1\n ]\n }\n ]\n },\n \"message\": \"MiniMax H3 使用尾帧时必须同时提供首帧\"\n },\n {\n \"assert\": {\n \"$not\": {\n \"$and\": [\n {\n \"$gt\": [\n {\n \"$len\": {\n \"$filter\": {\n \"from\": {\n \"$ref\": \"request.images\"\n },\n \"as\": \"image\",\n \"where\": {\n \"$in\": [\n {\n \"$ref\": \"image.role\"\n },\n [\n \"first_frame\",\n \"last_frame\"\n ]\n ]\n }\n }\n }\n },\n 0\n ]\n },\n {\n \"$gt\": [\n {\n \"$len\": {\n \"$filter\": {\n \"from\": {\n \"$ref\": \"request.images\"\n },\n \"as\": \"image\",\n \"where\": {\n \"$in\": [\n {\n \"$ref\": \"image.role\"\n },\n [\n \"reference_image\",\n \"subject_reference\",\n \"style_reference\"\n ]\n ]\n }\n }\n }\n },\n 0\n ]\n }\n ]\n }\n },\n \"message\": \"MiniMax H3 首尾帧模式不能混入普通角色或风格参考图\"\n }\n ],\n \"create\": {\n \"method\": \"POST\",\n \"path\": \"/v2/video_generation\",\n \"contentType\": \"application/json\",\n \"body\": {\n \"model\": {\n \"$ref\": \"request.model\"\n },\n \"content\": {\n \"$concatArrays\": [\n [\n {\n \"type\": \"text\",\n \"text\": {\n \"$ref\": \"request.prompt\"\n }\n }\n ],\n {\n \"$map\": {\n \"from\": {\n \"$sortByOrder\": {\n \"$ref\": \"request.images\"\n }\n },\n \"as\": \"media\",\n \"in\": {\n \"type\": \"image_url\",\n \"image_url\": {\n \"url\": {\n \"$ref\": \"media.value\"\n }\n },\n \"role\": {\n \"$coalesce\": [\n {\n \"$ref\": \"media.role\"\n },\n \"reference_image\"\n ]\n }\n }\n }\n },\n {\n \"$map\": {\n \"from\": {\n \"$sortByOrder\": {\n \"$ref\": \"request.videos\"\n }\n },\n \"as\": \"media\",\n \"in\": {\n \"type\": \"video_url\",\n \"video_url\": {\n \"url\": {\n \"$ref\": \"media.value\"\n }\n },\n \"role\": {\n \"$coalesce\": [\n {\n \"$ref\": \"media.role\"\n },\n \"reference_video\"\n ]\n }\n }\n }\n },\n {\n \"$map\": {\n \"from\": {\n \"$sortByOrder\": {\n \"$ref\": \"request.audios\"\n }\n },\n \"as\": \"media\",\n \"in\": {\n \"type\": \"audio_url\",\n \"audio_url\": {\n \"url\": {\n \"$ref\": \"media.value\"\n }\n },\n \"role\": {\n \"$coalesce\": [\n {\n \"$ref\": \"media.role\"\n },\n \"reference_audio\"\n ]\n }\n }\n }\n }\n ]\n },\n \"duration\": {\n \"$ref\": \"request.duration\"\n },\n \"resolution\": {\n \"$coalesce\": [\n {\n \"$ref\": \"request.resolution\"\n },\n \"768P\"\n ]\n },\n \"ratio\": {\n \"$if\": {\n \"condition\": {\n \"$gt\": [\n {\n \"$len\": {\n \"$filter\": {\n \"from\": {\n \"$ref\": \"request.images\"\n },\n \"as\": \"image\",\n \"where\": {\n \"$in\": [\n {\n \"$ref\": \"image.role\"\n },\n [\n \"first_frame\",\n \"last_frame\"\n ]\n ]\n }\n }\n }\n },\n 0\n ]\n },\n \"then\": \"adaptive\",\n \"else\": {\n \"$coalesce\": [\n {\n \"$ref\": \"request.aspectRatio\"\n },\n \"16:9\"\n ]\n }\n }\n },\n \"aigc_watermark\": {\n \"$ref\": \"request.watermark\"\n },\n \"generate_audio\": {\n \"$ref\": \"request.generateAudio\"\n },\n \"seed\": {\n \"$omitEmpty\": {\n \"$ref\": \"request.providerOptions.minimax-video.seed\"\n }\n },\n \"prompt_optimizer\": {\n \"$omitEmpty\": {\n \"$ref\": \"request.providerOptions.minimax-video.prompt_optimizer\"\n }\n }\n }\n },\n \"poll\": {\n \"method\": \"GET\",\n \"path\": \"/v2/query/video_generation/{{taskId}}\"\n },\n \"response\": {\n \"taskId\": {\n \"$coalesce\": [\n {\n \"$ref\": \"response.task.id\"\n },\n {\n \"$ref\": \"response.task_id\"\n },\n {\n \"$ref\": \"response.id\"\n },\n {\n \"$ref\": \"taskId\"\n }\n ]\n },\n \"status\": {\n \"$coalesce\": [\n {\n \"$ref\": \"response.task.status\"\n },\n {\n \"$ref\": \"response.status\"\n },\n \"pending\"\n ]\n },\n \"message\": {\n \"$coalesce\": [\n {\n \"$ref\": \"response.task.error.message\"\n },\n {\n \"$ref\": \"response.task.message\"\n },\n {\n \"$ref\": \"response.base_resp.status_msg\"\n },\n {\n \"$ref\": \"response.message\"\n },\n {\n \"$ref\": \"response.error.message\"\n }\n ]\n },\n \"videos\": {\n \"$coalesce\": [\n {\n \"$ref\": \"response.task.content.url\"\n },\n {\n \"$ref\": \"response.content.url\"\n },\n {\n \"$ref\": \"response.video_url\"\n },\n {\n \"$ref\": \"response.file.url\"\n }\n ]\n },\n \"errorPaths\": [\n \"task.error.code\",\n \"base_resp.status_code\",\n \"error.code\"\n ],\n \"resultEphemeral\": true\n }\n }\n ]\n }\n}\n```\n\n", + "permissions": [ + "generation.run", + "media.read" + ], + "configuration": { + "fields": [ + { + "name": "apiKey", + "type": "secret", + "label": "API Key", + "required": true + } + ] + }, + "contributes": { + "providers": [ + { + "id": "minimax-video", + "label": "MiniMax Hailuo Video V2 / H3", + "capabilities": [ + "video" + ], + "scopes": [ + "admin.system-channel", + "user.custom-channel", + "canvas", + "creation", + "agent" + ], + "baseUrl": "https://api.minimax.io", + "requiresPublicMediaUrls": true, + "auth": { + "type": "bearer", + "field": "apiKey" + }, + "parameters": [ + { + "name": "model", + "type": "string", + "required": true, + "mapping": "model", + "description": "MiniMax/Hailuo 视频模型 ID。" + }, + { + "name": "prompt", + "type": "string", + "required": true, + "mapping": "content[type=text].text", + "description": "仅写入 text content block,避免与顶层 prompt 重复。" + }, + { + "name": "images", + "type": "media[]", + "mapping": "content[type=image_url]", + "description": "保留 first_frame、last_frame、reference_image 等 role。" + }, + { + "name": "videos", + "type": "media[]", + "mapping": "content[type=video_url]", + "description": "映射为 reference_video。" + }, + { + "name": "audios", + "type": "media[]", + "mapping": "content[type=audio_url]", + "description": "映射为 reference_audio/reference_voice。" + }, + { + "name": "duration", + "type": "integer", + "required": true, + "values": [ + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15" + ], + "mapping": "duration", + "description": "4-15 秒。" + }, + { + "name": "resolution", + "type": "string", + "mapping": "resolution", + "description": "如 768P、1080P、2K;按模型能力限制。" + }, + { + "name": "aspectRatio", + "type": "string", + "mapping": "ratio", + "description": "首尾帧模式自动使用 adaptive;其他模式使用用户比例。" + }, + { + "name": "watermark", + "type": "boolean", + "mapping": "aigc_watermark", + "description": "AIGC 水印。" + }, + { + "name": "generateAudio", + "type": "boolean", + "mapping": "generate_audio", + "description": "模型支持时生成音频。" + }, + { + "name": "seed", + "type": "integer", + "mapping": "seed", + "description": "providerOptions.minimax-video.seed。" + }, + { + "name": "prompt_optimizer", + "type": "boolean", + "mapping": "prompt_optimizer", + "description": "providerOptions.minimax-video.prompt_optimizer。" + } + ], + "validations": [ + { + "assert": { + "$lte": [ + { + "$len": { + "$ref": "request.images" + } + }, + 9 + ] + }, + "message": "MiniMax H3 最多支持 9 张图片" + }, + { + "assert": { + "$lte": [ + { + "$len": { + "$ref": "request.videos" + } + }, + 3 + ] + }, + "message": "MiniMax H3 最多支持 3 个参考视频" + }, + { + "assert": { + "$lte": [ + { + "$len": { + "$ref": "request.audios" + } + }, + 3 + ] + }, + "message": "MiniMax H3 最多支持 3 个参考音频" + }, + { + "assert": { + "$and": [ + { + "$gte": [ + { + "$ref": "request.duration" + }, + 4 + ] + }, + { + "$lte": [ + { + "$ref": "request.duration" + }, + 15 + ] + } + ] + }, + "message": "MiniMax H3 duration 必须在 4-15 秒之间" + }, + { + "assert": { + "$lte": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$eq": [ + { + "$ref": "image.role" + }, + "first_frame" + ] + } + } + } + }, + 1 + ] + }, + "message": "MiniMax H3 最多只能有一个首帧" + }, + { + "assert": { + "$lte": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$eq": [ + { + "$ref": "image.role" + }, + "last_frame" + ] + } + } + } + }, + 1 + ] + }, + "message": "MiniMax H3 最多只能有一个尾帧" + }, + { + "assert": { + "$or": [ + { + "$eq": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$eq": [ + { + "$ref": "image.role" + }, + "last_frame" + ] + } + } + } + }, + 0 + ] + }, + { + "$eq": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$eq": [ + { + "$ref": "image.role" + }, + "first_frame" + ] + } + } + } + }, + 1 + ] + } + ] + }, + "message": "MiniMax H3 使用尾帧时必须同时提供首帧" + }, + { + "assert": { + "$not": { + "$and": [ + { + "$gt": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$in": [ + { + "$ref": "image.role" + }, + [ + "first_frame", + "last_frame" + ] + ] + } + } + } + }, + 0 + ] + }, + { + "$gt": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$in": [ + { + "$ref": "image.role" + }, + [ + "reference_image", + "subject_reference", + "style_reference" + ] + ] + } + } + } + }, + 0 + ] + } + ] + } + }, + "message": "MiniMax H3 首尾帧模式不能混入普通角色或风格参考图" + } + ], + "create": { + "method": "POST", + "path": "/v2/video_generation", + "contentType": "application/json", + "body": { + "model": { + "$ref": "request.model" + }, + "content": { + "$concatArrays": [ + [ + { + "type": "text", + "text": { + "$ref": "request.prompt" + } + } + ], + { + "$map": { + "from": { + "$sortByOrder": { + "$ref": "request.images" + } + }, + "as": "media", + "in": { + "type": "image_url", + "image_url": { + "url": { + "$ref": "media.value" + } + }, + "role": { + "$coalesce": [ + { + "$ref": "media.role" + }, + "reference_image" + ] + } + } + } + }, + { + "$map": { + "from": { + "$sortByOrder": { + "$ref": "request.videos" + } + }, + "as": "media", + "in": { + "type": "video_url", + "video_url": { + "url": { + "$ref": "media.value" + } + }, + "role": { + "$coalesce": [ + { + "$ref": "media.role" + }, + "reference_video" + ] + } + } + } + }, + { + "$map": { + "from": { + "$sortByOrder": { + "$ref": "request.audios" + } + }, + "as": "media", + "in": { + "type": "audio_url", + "audio_url": { + "url": { + "$ref": "media.value" + } + }, + "role": { + "$coalesce": [ + { + "$ref": "media.role" + }, + "reference_audio" + ] + } + } + } + } + ] + }, + "duration": { + "$ref": "request.duration" + }, + "resolution": { + "$coalesce": [ + { + "$ref": "request.resolution" + }, + "768P" + ] + }, + "ratio": { + "$if": { + "condition": { + "$gt": [ + { + "$len": { + "$filter": { + "from": { + "$ref": "request.images" + }, + "as": "image", + "where": { + "$in": [ + { + "$ref": "image.role" + }, + [ + "first_frame", + "last_frame" + ] + ] + } + } + } + }, + 0 + ] + }, + "then": "adaptive", + "else": { + "$coalesce": [ + { + "$ref": "request.aspectRatio" + }, + "16:9" + ] + } + } + }, + "aigc_watermark": { + "$ref": "request.watermark" + }, + "generate_audio": { + "$ref": "request.generateAudio" + }, + "seed": { + "$omitEmpty": { + "$ref": "request.providerOptions.minimax-video.seed" + } + }, + "prompt_optimizer": { + "$omitEmpty": { + "$ref": "request.providerOptions.minimax-video.prompt_optimizer" + } + } + } + }, + "poll": { + "method": "GET", + "path": "/v2/query/video_generation/{{taskId}}" + }, + "response": { + "taskId": { + "$coalesce": [ + { + "$ref": "response.task.id" + }, + { + "$ref": "response.task_id" + }, + { + "$ref": "response.id" + }, + { + "$ref": "taskId" + } + ] + }, + "status": { + "$coalesce": [ + { + "$ref": "response.task.status" + }, + { + "$ref": "response.status" + }, + "pending" + ] + }, + "message": { + "$coalesce": [ + { + "$ref": "response.task.error.message" + }, + { + "$ref": "response.task.message" + }, + { + "$ref": "response.base_resp.status_msg" + }, + { + "$ref": "response.message" + }, + { + "$ref": "response.error.message" + } + ] + }, + "videos": { + "$coalesce": [ + { + "$ref": "response.task.content.url" + }, + { + "$ref": "response.content.url" + }, + { + "$ref": "response.video_url" + }, + { + "$ref": "response.file.url" + } + ] + }, + "errorPaths": [ + "task.error.code", + "base_resp.status_code", + "error.code" + ], + "resultEphemeral": true + } + } + ] + } +}