182 lines
5.7 KiB
Python
182 lines
5.7 KiB
Python
"""
|
||
Video generation cost estimator - multi-model pricing and cost prediction.
|
||
|
||
Estimates cost for generating scene videos using different models:
|
||
- Wan2.2 (local GPU, fast, lower cost)
|
||
- Wan2.7 (local GPU, higher quality, medium cost)
|
||
- Vidu 2.0 (cloud API, highest quality, premium cost)
|
||
|
||
Cost factors:
|
||
- Scene count from storyboard
|
||
- Duration per scene (frames)
|
||
- Model pricing (per frame or per second)
|
||
- GPU compute cost (for local models)
|
||
"""
|
||
|
||
import logging
|
||
from typing import List, Dict, Any
|
||
|
||
logger = logging.getLogger("pipeline.cost_estimator")
|
||
|
||
|
||
# Model pricing configuration — llmage v1 video models
|
||
MODEL_PRICING = {
|
||
"wan2.7-t2v": {
|
||
"name": "通义万象 WAN 2.7 (T2V) ⭐推荐",
|
||
"price_per_second": 0.05,
|
||
"quality_score": 8.5,
|
||
"generation_speed": "medium",
|
||
"description": "最新版文生视频,质量更高",
|
||
"features": ["T2V", "I2V", "Ref2V"],
|
||
"recommended_for": ["quality", "professional"],
|
||
},
|
||
"happyhorse-1.0-t2v": {
|
||
"name": "快乐马 HappyHorse 1.0 (T2V)",
|
||
"price_per_second": 0.03,
|
||
"quality_score": 7.5,
|
||
"generation_speed": "fast",
|
||
"description": "快速文生视频,经济实惠",
|
||
"features": ["T2V", "I2V"],
|
||
"recommended_for": ["standard", "batch"],
|
||
},
|
||
"doubao-seedance-2-0-260128": {
|
||
"name": "豆包 Seedance 2.0 (T2V)",
|
||
"price_per_second": 0.08,
|
||
"quality_score": 9.0,
|
||
"generation_speed": "slow",
|
||
"description": "字节跳动旗舰视频生成,支持音效同步",
|
||
"features": ["T2V", "I2V", "Ref2V", "音效"],
|
||
"recommended_for": ["premium", "cinematic"],
|
||
},
|
||
}
|
||
|
||
|
||
def estimate_scene_cost(storyboard: List[Dict[str, Any]], fps: int = 24) -> int:
|
||
"""
|
||
Estimate total frames needed for all scenes.
|
||
|
||
Args:
|
||
storyboard: List of scene dicts with start_time/end_time
|
||
fps: Frames per second (default 24)
|
||
|
||
Returns:
|
||
Total frame count
|
||
"""
|
||
total_frames = 0
|
||
for scene in storyboard:
|
||
duration = scene.get("end_time", 10) - scene.get("start_time", 0)
|
||
frames = int(duration * fps)
|
||
total_frames += frames
|
||
return total_frames
|
||
|
||
|
||
def estimate_model_cost(model_id: str, total_frames: int, fps: int = 24) -> Dict[str, Any]:
|
||
"""
|
||
Estimate cost for a specific model.
|
||
|
||
Args:
|
||
model_id: Model identifier (e.g., wan2.7-t2v, doubao-seedance-2-0-260128)
|
||
total_frames: Total frames to generate
|
||
|
||
Returns:
|
||
Cost estimate dict with breakdown
|
||
"""
|
||
if model_id not in MODEL_PRICING:
|
||
raise ValueError(f"Unknown model: {model_id}")
|
||
|
||
model = MODEL_PRICING[model_id]
|
||
|
||
return {
|
||
"model_id": model_id,
|
||
"model_name": model["name"],
|
||
"total_frames": total_frames,
|
||
"fps": fps,
|
||
"price_per_second": model["price_per_second"],
|
||
"total_cost": round(total_frames * model["price_per_second"] / fps, 4),
|
||
"quality_score": model["quality_score"],
|
||
"generation_speed": model["generation_speed"],
|
||
"description": model["description"],
|
||
"features": model["features"],
|
||
"recommended_for": model["recommended_for"],
|
||
}
|
||
|
||
|
||
def estimate_all_models(storyboard: List[Dict[str, Any]], fps: int = 24) -> List[Dict[str, Any]]:
|
||
"""
|
||
Estimate cost for all available models.
|
||
|
||
Args:
|
||
storyboard: List of scene dicts
|
||
fps: Frames per second
|
||
|
||
Returns:
|
||
List of cost estimates sorted by total_cost (ascending)
|
||
"""
|
||
total_frames = estimate_scene_cost(storyboard, fps)
|
||
scene_count = len(storyboard)
|
||
|
||
estimates = []
|
||
for model_id in MODEL_PRICING.keys():
|
||
try:
|
||
estimate = estimate_model_cost(model_id, total_frames, fps)
|
||
estimate["scene_count"] = scene_count
|
||
estimate["fps"] = fps
|
||
estimates.append(estimate)
|
||
except Exception as e:
|
||
logger.error(f"Failed to estimate cost for {model_id}: {e}")
|
||
|
||
# Sort by total_cost (cheapest first)
|
||
estimates.sort(key=lambda x: x["total_cost"])
|
||
|
||
# Add recommendation flag
|
||
if estimates:
|
||
# Recommend middle option (best value)
|
||
mid_idx = len(estimates) // 2
|
||
estimates[mid_idx]["recommended"] = True
|
||
for i, est in enumerate(estimates):
|
||
if i != mid_idx:
|
||
est["recommended"] = False
|
||
|
||
return estimates
|
||
|
||
|
||
def format_cost_summary(estimates: List[Dict[str, Any]]) -> str:
|
||
"""
|
||
Format cost estimates as human-readable summary.
|
||
|
||
Args:
|
||
estimates: List of cost estimate dicts
|
||
|
||
Returns:
|
||
Formatted string summary
|
||
"""
|
||
lines = []
|
||
lines.append("=" * 70)
|
||
lines.append("视频生成模型费用预估")
|
||
lines.append("=" * 70)
|
||
|
||
if not estimates:
|
||
lines.append("无可用模型")
|
||
return "\n".join(lines)
|
||
|
||
scene_count = estimates[0].get("scene_count", 0)
|
||
total_frames = estimates[0].get("total_frames", 0)
|
||
fps = estimates[0].get("fps", 24)
|
||
|
||
lines.append(f"分镜数量: {scene_count}")
|
||
lines.append(f"总帧数: {total_frames} (at {fps}fps)")
|
||
lines.append("")
|
||
|
||
for est in estimates:
|
||
rec = " ⭐ 推荐" if est.get("recommended") else ""
|
||
lines.append(f"【{est['model_name']}】{rec}")
|
||
lines.append(f" 质量评分: {est['quality_score']}/10")
|
||
lines.append(f" 生成速度: {est['generation_speed']}")
|
||
lines.append(f" 功能: {', '.join(est['features'])}")
|
||
lines.append(f" 描述: {est['description']}")
|
||
lines.append(f" 费用: 每秒 ${est['price_per_second']:.4f} × {est['total_frames']}帧@{est['fps']}fps = ${est['total_cost']:.4f}")
|
||
lines.append("")
|
||
|
||
lines.append("=" * 70)
|
||
return "\n".join(lines)
|