video-gen: adopt Seedance 2.0 as preferred premium default

Seedance 2.0 is now routed as the top-ranked video generator whenever a
premium gateway is available. Touches the tool layer, scoring engine,
cinematic pipeline, and both skill layers so discovery works from every
entry point.

- tools/video/seedance_video: BETA stability, quality_score=0.95, add
  reference_to_video operation plus 9 img + 3 vid + 3 audio ceilings,
  fix pre-existing upload_image_fal import
- tools/base_tool: surface optional quality_score / success_rate /
  latency fields in get_info so the scorer can read them
- lib/scoring: fix reliability enum-vs-string bug that was pinning every
  available tool to 0.0, switch to overlap coefficient so rich best_for
  descriptions aren't penalized, add premium-cinematic feature bonus
- pipeline_defs/cinematic + cinematic asset-director: add pixabay_music
  and freesound_music, restore pixabay-first music default
- cinematic compose-director: mandatory Remotion preflight at stage entry
- New Layer 3 .agents/skills/seedance-2-0/SKILL.md (8-part prompt
  structure, multi-shot, lip-sync, reference-to-video, provider landscape)
- New Layer 2 skills/creative/prompting/seedance-prompting.md
- Update ai-video-gen, video-gen-prompting, AGENT_GUIDE, INDEX to flag
  Seedance 2.0 as the preferred premium default and make the skill
  discoverable from every routing path
This commit is contained in:
calesthio
2026-04-17 21:51:54 -07:00
parent 4822454e77
commit 16791a3a80
13 changed files with 524 additions and 33 deletions
+11
View File
@@ -181,6 +181,14 @@ class BaseTool(ABC):
# --- Verification ---
user_visible_verification: list[str] = []
# --- Optional telemetry / quality hints for the scoring engine ---
# If set (0.0-1.0), lib/scoring.py uses these directly instead of falling
# back to stability-based heuristics. Leave unset unless the tool has a
# real measured or well-calibrated value.
quality_score: Optional[float] = None
historical_success_rate: Optional[float] = None
latency_p50_seconds: Optional[float] = None
# ---- Status reporting ----
def get_status(self) -> ToolStatus:
@@ -255,6 +263,9 @@ class BaseTool(ABC):
"agent_skills": self.agent_skills,
"related_skills": self.agent_skills,
"user_visible_verification": self.user_visible_verification,
"quality_score": self.quality_score,
"historical_success_rate": self.historical_success_rate,
"latency_p50_seconds": self.latency_p50_seconds,
}
# ---- Cost estimation ----
+75 -10
View File
@@ -27,11 +27,11 @@ from tools.base_tool import (
class SeedanceVideo(BaseTool):
name = "seedance_video"
version = "0.1.0"
version = "0.2.0"
tier = ToolTier.GENERATE
capability = "video_generation"
provider = "seedance"
stability = ToolStability.EXPERIMENTAL
stability = ToolStability.BETA
execution_mode = ExecutionMode.SYNC
determinism = Determinism.STOCHASTIC
runtime = ToolRuntime.API
@@ -41,25 +41,36 @@ class SeedanceVideo(BaseTool):
"Set FAL_KEY to your fal.ai API key.\n"
" Get one at https://fal.ai/dashboard/keys"
)
agent_skills = ["ai-video-gen"]
agent_skills = ["seedance-2-0", "ai-video-gen"]
capabilities = ["text_to_video", "image_to_video"]
capabilities = ["text_to_video", "image_to_video", "reference_to_video"]
supports = {
"text_to_video": True,
"image_to_video": True,
"reference_to_video": True,
"multiple_reference_images": True,
"reference_image": True,
"native_audio": True,
"cinematic_quality": True,
"camera_direction": True,
"lip_sync": True,
"multi_shot": True,
"aspect_ratio": True,
"seed": True,
}
best_for = [
"cinematic clips with native synchronized audio",
"director-level camera control and multi-shot editing",
"preferred premium video gen when FAL_KEY is available",
"cinematic trailers, teasers, and high-fidelity clips with native synchronized audio",
"director-level camera control and multi-shot editing in a single generation",
"lip-sync from quoted dialogue in prompts",
"high-fidelity motion with real-world physics",
"reference-conditioned generation (up to 9 images + 3 video clips + 3 audio clips)",
"consistent character identity across shots",
]
not_good_for = ["offline generation", "budget-constrained projects"]
fallback_tools = ["kling_video", "minimax_video", "veo_video"]
fallback_tools = ["veo_video", "kling_video", "minimax_video"]
# Premium model — beat out "experimental stability" baseline. The scoring
# engine reads quality_score directly when present (see lib/scoring.py).
quality_score = 0.95
input_schema = {
"type": "object",
@@ -68,7 +79,7 @@ class SeedanceVideo(BaseTool):
"prompt": {"type": "string"},
"operation": {
"type": "string",
"enum": ["text_to_video", "image_to_video"],
"enum": ["text_to_video", "image_to_video", "reference_to_video"],
"default": "text_to_video",
},
"model_variant": {
@@ -102,10 +113,34 @@ class SeedanceVideo(BaseTool):
"type": "string",
"description": "Start frame image URL for image_to_video (jpg, png, webp)",
},
"image_path": {
"type": "string",
"description": "Local start-frame path for image_to_video. Auto-uploaded to fal.ai storage.",
},
"end_image_url": {
"type": "string",
"description": "Optional end frame URL for image_to_video",
},
"reference_image_urls": {
"type": "array",
"items": {"type": "string"},
"description": "Up to 9 reference image URLs for reference_to_video (identity / wardrobe / setting / style anchors).",
},
"reference_image_paths": {
"type": "array",
"items": {"type": "string"},
"description": "Local reference image paths for reference_to_video. Auto-uploaded to fal.ai storage.",
},
"reference_video_urls": {
"type": "array",
"items": {"type": "string"},
"description": "Up to 3 reference video clip URLs for reference_to_video (motion / camera / pacing anchors).",
},
"reference_audio_urls": {
"type": "array",
"items": {"type": "string"},
"description": "Up to 3 reference audio clip URLs for reference_to_video (voice / music / ambience anchors).",
},
"seed": {
"type": "integer",
"description": "Optional seed for reproducibility",
@@ -180,11 +215,41 @@ class SeedanceVideo(BaseTool):
if inputs.get("image_url"):
payload["image_url"] = inputs["image_url"]
elif inputs.get("image_path"):
from tools.video._shared import upload_image_to_fal
from tools.video._shared import upload_image_fal
payload["image_url"] = upload_image_to_fal(inputs["image_path"])
if inputs.get("end_image_url"):
payload["end_image_url"] = inputs["end_image_url"]
if operation == "reference_to_video":
ref_image_urls = list(inputs.get("reference_image_urls") or [])
for local_path in inputs.get("reference_image_paths") or []:
from tools.video._shared import upload_image_fal
ref_image_urls.append(upload_image_to_fal(local_path))
# Seedance 2.0 reference-to-video ceilings: 9 images + 3 video + 3 audio.
if len(ref_image_urls) > 9:
return ToolResult(
success=False,
error=f"Seedance 2.0 reference_to_video accepts at most 9 reference images; got {len(ref_image_urls)}",
)
ref_video_urls = list(inputs.get("reference_video_urls") or [])
if len(ref_video_urls) > 3:
return ToolResult(
success=False,
error=f"Seedance 2.0 reference_to_video accepts at most 3 reference videos; got {len(ref_video_urls)}",
)
ref_audio_urls = list(inputs.get("reference_audio_urls") or [])
if len(ref_audio_urls) > 3:
return ToolResult(
success=False,
error=f"Seedance 2.0 reference_to_video accepts at most 3 reference audio clips; got {len(ref_audio_urls)}",
)
if ref_image_urls:
payload["reference_image_urls"] = ref_image_urls
if ref_video_urls:
payload["reference_video_urls"] = ref_video_urls
if ref_audio_urls:
payload["reference_audio_urls"] = ref_audio_urls
headers = {
"Authorization": f"Key {api_key}",
"Content-Type": "application/json",