diff --git a/src/BuiltinExtensions/ComfyUIBackend/WorkflowGenerator.cs b/src/BuiltinExtensions/ComfyUIBackend/WorkflowGenerator.cs
index 1dc087dbc..778631ab4 100644
--- a/src/BuiltinExtensions/ComfyUIBackend/WorkflowGenerator.cs
+++ b/src/BuiltinExtensions/ComfyUIBackend/WorkflowGenerator.cs
@@ -1014,6 +1014,11 @@ public string CreateKSampler(JArray model, JArray pos, JArray neg, JArray latent
defsampler ??= "res_multistep";
defscheduler ??= "simple";
}
+ else if (IsYue2())
+ {
+ defsampler ??= "dpm_2";
+ defscheduler ??= "sgm_uniform";
+ }
else if (IsAnima())
{
defsampler ??= "er_sde";
@@ -2560,6 +2565,36 @@ public JArray CreateConditioningDirect(string prompt, JArray clip, T2IModel mode
["min_p"] = 0
}, id);
}
+ else if (IsYue2())
+ {
+ if (!isPositive)
+ {
+ return FinalPrompt;
+ }
+ string abc = CreateNode("YuE2GenerateABC", new JObject()
+ {
+ ["clip"] = clip,
+ ["style"] = UserInput.Get(T2IParamTypes.Text2AudioStyle, ""),
+ ["lyrics"] = prompt,
+ ["seed"] = UserInput.Get(T2IParamTypes.Seed, 0) + 10,
+ ["mode"] = "full",
+ ["max_abc_tokens"] = 1024
+ });
+ node = CreateNode("YuE2GenerateMusic", new JObject()
+ {
+ ["clip"] = clip,
+ ["style"] = UserInput.Get(T2IParamTypes.Text2AudioStyle, ""),
+ ["lyrics"] = prompt,
+ ["abc"] = NodePath(abc, 0),
+ ["seed"] = UserInput.Get(T2IParamTypes.Seed, 0) + 20,
+ ["mode"] = "full",
+ ["max_duration"] = Math.Clamp(UserInput.Get(T2IParamTypes.Text2AudioDuration, 120), 0.04, 900),
+ ["temperature"] = 1,
+ ["top_p"] = 0.95,
+ ["top_k"] = 100,
+ ["repetition_penalty"] = 1.2
+ }, id);
+ }
else if (IsMiniMaxMusic3())
{
node = CreateNode("MiniMaxMusic3TextEncode", new JObject()
diff --git a/src/BuiltinExtensions/ComfyUIBackend/WorkflowGeneratorModelSupport.cs b/src/BuiltinExtensions/ComfyUIBackend/WorkflowGeneratorModelSupport.cs
index 2871b0ed3..b6bcd71df 100644
--- a/src/BuiltinExtensions/ComfyUIBackend/WorkflowGeneratorModelSupport.cs
+++ b/src/BuiltinExtensions/ComfyUIBackend/WorkflowGeneratorModelSupport.cs
@@ -278,6 +278,12 @@ public bool IsMiniMaxMusic3()
return IsModelCompatClass(T2IModelClassSorter.CompatMiniMaxMusic3);
}
+ /// Returns true if the current model is YuE2.
+ public bool IsYue2()
+ {
+ return IsModelCompatClass(T2IModelClassSorter.CompatYue2);
+ }
+
/// Returns true if the current model primarily operates on audio.
public bool IsAudioModel()
{
@@ -419,9 +425,9 @@ public WGNodeData EmptyImage(int width, int height, int batchSize, string id = n
["seconds"] = UserInput.Get(T2IParamTypes.Text2AudioDuration, 120)
}, id));
}
- else if (IsMiniMaxMusic3())
+ else if (IsMiniMaxMusic3() || IsYue2())
{
- JProperty encodedNode = NodesOfClass("MiniMaxMusic3TextEncode").FirstOrDefault();
+ JProperty encodedNode = NodesOfClass(IsYue2() ? "YuE2GenerateMusic" : "MiniMaxMusic3TextEncode").FirstOrDefault();
JToken targetSeconds;
if (encodedNode is not null)
{
@@ -431,7 +437,7 @@ public WGNodeData EmptyImage(int width, int height, int batchSize, string id = n
{
targetSeconds = NodePath("6", 1); // TODO: This is a very wrong hack hardcoding the prompt path. This will break in many practical edge cases. Need to figure out the special routing that applies here.
}
- return resultAudio(CreateNode("EmptyMiniMaxMusic3LatentAudio", new JObject()
+ return resultAudio(CreateNode(IsYue2() ? "EmptyYuE2LatentAudio" : "EmptyMiniMaxMusic3LatentAudio", new JObject()
{
["batch_size"] = batchSize,
["seconds"] = targetSeconds
@@ -1529,6 +1535,10 @@ public void LoadClip3(string type, string modelA, string modelB, string modelC)
helpers.DoVaeLoader(null, T2IModelClassSorter.CompatMiniMaxMusic3, "minimax-music-3-vae");
CurrentAudioVae = new WGNodeData([LoadingVAE, 0], this, WGNodeData.DT_AUDIOVAE, CurrentCompat());
}
+ else if (IsYue2())
+ {
+ CurrentAudioVae = new WGNodeData([LoadingVAE, 0], this, WGNodeData.DT_AUDIOVAE, CurrentCompat());
+ }
else if (IsAceStep15())
{
// TODO: WTF? these twin qwen tencs are wacky.
diff --git a/src/Text2Image/T2IModelClassSorter.cs b/src/Text2Image/T2IModelClassSorter.cs
index c8d981d6f..e39d8ce49 100644
--- a/src/Text2Image/T2IModelClassSorter.cs
+++ b/src/Text2Image/T2IModelClassSorter.cs
@@ -102,6 +102,7 @@ public static T2IModelCompatClass
// Audio models
CompatAceStep15 = RegisterCompat(new() { ID = "ace-step-1_5", ShortCode = "Ace15", IsAudioModel = true }),
CompatMiniMaxMusic3 = RegisterCompat(new() { ID = "minimax-music-3", ShortCode = "MMM3", IsAudioModel = true }),
+ CompatYue2 = RegisterCompat(new() { ID = "yue-2", ShortCode = "YuE2", IsAudioModel = true }),
// Obscure old random ones
CompatAuraFlow = RegisterCompat(new() { ID = "auraflow-v1", ShortCode = "Aura", VaeFamily = VaeSdxl }),
CompatHiDreamI1 = RegisterCompat(new() { ID = "hidream-i1", ShortCode = "HiDrm", LorasTargetTextEnc = false, VaeFamily = VaeFlux1 }),
@@ -286,6 +287,7 @@ bool isAnimaLora(JObject h) => (hasLoraKey(h, "llm_adapter.blocks.5.self_attn.v_
// Audio models
bool isAceStep15(JObject h) => hasKey(h, "encoder.lyric_encoder.layers.0.post_attention_layernorm.weight");
bool isMiniMaxMusic3(JObject h) => hasKey(h, "cond_layer_logits") && hasKey(h, "latent_conditioners.0.weight") && hasKey(h, "diffusion_transformer.transformer.layers.0.self_attn.to_qkv.weight");
+ bool isYue2(JObject h) => hasKey(h, "vae2llm.weight") && hasKey(h, "llm2vae.weight") && hasKey(h, "latent_pos_embed.pe") && hasKey(h, "model.layers.0.self_attn.qkv_proj.weight") && hasKey(h, "time_embedder.mlp.0.weight");
// ====================== Stable Diffusion v1 ======================
Register(new() { ID = "stable-diffusion-v1", CompatClass = CompatSdv1, Name = "Stable Diffusion v1", StandardWidth = 512, StandardHeight = 512, IsThisModelOfClass = (m, h) =>
{
@@ -956,6 +958,10 @@ JToken GetEmbeddingKey(JObject h)
{
return isMiniMaxMusic3(h);
}});
+ Register(new() { ID = "yue-2", CompatClass = CompatYue2, Name = "YuE2", IsThisModelOfClass = (m, h) =>
+ {
+ return isYue2(h);
+ }});
// ====================== Everything below this point does not autodetect, it must match through ModelSpec or be manually set ======================
// General Stable Diffusion variants
Register(new() { ID = "stable-diffusion-v1/vae", CompatClass = CompatSdv1, Name = "Stable Diffusion v1 VAE", StandardWidth = 512, StandardHeight = 512, IsThisModelOfClass = (m, h) => { return false; } });
diff --git a/src/wwwroot/js/genpage/main.js b/src/wwwroot/js/genpage/main.js
index 1ad66df9d..93826981b 100644
--- a/src/wwwroot/js/genpage/main.js
+++ b/src/wwwroot/js/genpage/main.js
@@ -209,7 +209,7 @@ function reviseBackendFeatureSet() {
doAnyCompatFeature(['stable-diffusion-v1', 'stable-diffusion-v2', 'stable-diffusion-xl-v1'], 'supports_reference_only');
doAnyCompatFeature(['stable-diffusion-v1', 'stable-diffusion-v2', 'stable-diffusion-xl-v1'], 'supports_hypertile');
doAnyCompatFeature(['genmo-mochi-1', 'lightricks-ltx-video', 'hunyuan-video', 'nvidia-cosmos-1', `wan-21`, `wan-22`, 'kandinsky5-vidlite', 'kandinsky5-vidpro', 'minimax-h3'], 'text2video');
- doAnyCompatFeature(['ace-step-1_5', 'minimax-music-3'], 'text2audio');
+ doAnyCompatFeature(['ace-step-1_5', 'minimax-music-3', 'yue-2'], 'text2audio');
for (let changer of featureSetChangers) {
let [add, remove] = changer();
addMe.push(...add);