feat: prompt entered once in SelvaFeatureExtractor, reused by SelvaSampler

SelvaFeatureExtractor now stores the prompt in SELVA_FEATURES (both in the
returned dict and the .npz cache). SelvaSampler's prompt is now optional —
when left empty it falls back to the prompt stored in features. A non-empty
override can still be passed when CLIP text should differ from the sync text.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 16:22:59 +02:00
parent 0e417f4078
commit 27b4424e1a
2 changed files with 21 additions and 6 deletions
+6 -1
View File
@@ -150,6 +150,7 @@ class SelvaFeatureExtractor:
clip_features=clip_features.cpu().float().numpy(),
sync_features=sync_features.cpu().float().numpy(),
duration=float(duration),
prompt=np.array(prompt),
)
print(f"[SelVA] Features cached: {cached_path}", flush=True)
@@ -157,13 +158,17 @@ class SelvaFeatureExtractor:
"clip_features": clip_features.cpu(),
"sync_features": sync_features.cpu(),
"duration": float(duration),
"prompt": prompt,
}, float(fps))
def _load_cached(path):
data = np.load(path, allow_pickle=False)
return {
features = {
"clip_features": torch.from_numpy(data["clip_features"]),
"sync_features": torch.from_numpy(data["sync_features"]),
"duration": float(data["duration"]),
}
if "prompt" in data:
features["prompt"] = str(data["prompt"])
return features
+15 -5
View File
@@ -11,10 +11,6 @@ class SelvaSampler:
"required": {
"model": ("SELVA_MODEL",),
"features": ("SELVA_FEATURES",),
"prompt": ("STRING", {
"default": "", "multiline": True,
"tooltip": "Should match the prompt used in SelvaFeatureExtractor.",
}),
"negative_prompt": ("STRING", {
"default": "", "multiline": True,
"tooltip": "Sounds to steer away from, e.g. 'wind noise, background music'.",
@@ -29,6 +25,12 @@ class SelvaSampler:
"tooltip": "CFG scale (SelVA default is 4.5)."}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFF}),
},
"optional": {
"prompt": ("STRING", {
"default": "", "multiline": True,
"tooltip": "CLIP text for audio generation. Leave empty to reuse the prompt from SelvaFeatureExtractor.",
}),
},
}
RETURN_TYPES = ("AUDIO",)
@@ -36,7 +38,7 @@ class SelvaSampler:
FUNCTION = "generate"
CATEGORY = PRISMAUDIO_CATEGORY
def generate(self, model, features, prompt, negative_prompt, duration, steps, cfg_strength, seed):
def generate(self, model, features, negative_prompt, duration, steps, cfg_strength, seed, prompt=None):
from selva_core.model.flow_matching import FlowMatching
from selva_core.model.sequence_config import SequenceConfig
@@ -47,6 +49,14 @@ class SelvaSampler:
feature_utils = model["feature_utils"]
mode = model["mode"]
# Resolve prompt: use override if given, otherwise fall back to features prompt
if not prompt or not prompt.strip():
prompt = features.get("prompt", "")
if prompt:
print(f"[SelVA] Using prompt from features: '{prompt[:60]}'", flush=True)
else:
print("[SelVA] Warning: no prompt in features or sampler — CLIP text conditioning will be empty.", flush=True)
# Resolve duration
if duration <= 0:
if "duration" not in features: