mirror of
https://github.com/calesthio/OpenMontage.git
synced 2026-08-05 15:20:40 +08:00
Merge pull request #196 from An-idd/fix/provider-model-default-drift
fix(video): runway/higgsfield model defaults diverge from schema → wrong cost & silent model swap
This commit is contained in:
44
tests/tools/test_provider_model_defaults.py
Normal file
44
tests/tools/test_provider_model_defaults.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""Regression: a provider's code-level model default must match its schema's
|
||||
declared `model.default`.
|
||||
|
||||
Bug: runway_video and higgsfield_video hardcoded stale model defaults in
|
||||
estimate_cost/estimate_runtime/execute (`gen4_turbo` / `kling_3.0`) while the
|
||||
schema advertised `seedance_2.0` as the premium default. Omitting `model` then
|
||||
quoted the wrong (cheap) model's cost and silently generated a different model
|
||||
than the schema promised — a Decision-Communication / cost-accuracy violation.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import tools.video.higgsfield_video as higgsfield_video
|
||||
import tools.video.runway_video as runway_video
|
||||
from tools.video.higgsfield_video import HiggsFieldVideo
|
||||
from tools.video.runway_video import RunwayVideo
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"tool_cls, module",
|
||||
[(RunwayVideo, runway_video), (HiggsFieldVideo, higgsfield_video)],
|
||||
)
|
||||
def test_default_model_constant_matches_schema(tool_cls, module):
|
||||
# `execute()` reads `model` via `_DEFAULT_MODEL`; locking the constant to the
|
||||
# schema default guards the silent-model-swap path without a network call.
|
||||
schema_default = tool_cls().input_schema["properties"]["model"]["default"]
|
||||
assert module._DEFAULT_MODEL == schema_default
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tool_cls", [RunwayVideo, HiggsFieldVideo])
|
||||
def test_estimate_default_model_matches_schema(tool_cls):
|
||||
tool = tool_cls()
|
||||
schema_default = tool.input_schema["properties"]["model"]["default"]
|
||||
|
||||
# Cost/runtime with `model` omitted must equal the schema's declared default,
|
||||
# not some stale hardcoded fallback.
|
||||
assert tool.estimate_cost({}) == tool.estimate_cost({"model": schema_default}), (
|
||||
f"{tool.name}.estimate_cost default model diverges from schema default "
|
||||
f"{schema_default!r}"
|
||||
)
|
||||
assert tool.estimate_runtime({}) == tool.estimate_runtime({"model": schema_default}), (
|
||||
f"{tool.name}.estimate_runtime default model diverges from schema default "
|
||||
f"{schema_default!r}"
|
||||
)
|
||||
@@ -24,6 +24,11 @@ from tools.base_tool import (
|
||||
ToolTier,
|
||||
)
|
||||
|
||||
# Single source of truth for the default model. Referenced by both the input
|
||||
# schema and every code path that reads `model`, so estimate_cost / estimate_runtime
|
||||
# / execute can never silently diverge from the advertised default again.
|
||||
_DEFAULT_MODEL = "seedance_2.0"
|
||||
|
||||
|
||||
class HiggsFieldVideo(BaseTool):
|
||||
name = "higgsfield_video"
|
||||
@@ -89,7 +94,7 @@ class HiggsFieldVideo(BaseTool):
|
||||
"wan_2.5",
|
||||
"soul_cinema",
|
||||
],
|
||||
"default": "seedance_2.0",
|
||||
"default": _DEFAULT_MODEL,
|
||||
"description": "Underlying model. Defaults to Seedance 2.0 (preferred premium) — see .agents/skills/seedance-2-0/",
|
||||
},
|
||||
"duration": {
|
||||
@@ -134,7 +139,7 @@ class HiggsFieldVideo(BaseTool):
|
||||
return ToolStatus.UNAVAILABLE
|
||||
|
||||
def estimate_cost(self, inputs: dict[str, Any]) -> float:
|
||||
model = inputs.get("model", "seedance_2.0")
|
||||
model = inputs.get("model", _DEFAULT_MODEL)
|
||||
duration = int(inputs.get("duration", "5"))
|
||||
# Approximate per-clip costs based on Higgsfield credit pricing.
|
||||
# Seedance 2.0 on Higgsfield runs ~50-80 credits per 5s clip ≈ $0.50-$1.20.
|
||||
@@ -151,7 +156,7 @@ class HiggsFieldVideo(BaseTool):
|
||||
return base * (duration / 5)
|
||||
|
||||
def estimate_runtime(self, inputs: dict[str, Any]) -> float:
|
||||
model = inputs.get("model", "seedance_2.0")
|
||||
model = inputs.get("model", _DEFAULT_MODEL)
|
||||
if model in ("veo_3.1", "sora_2", "seedance_2.0"):
|
||||
return 120.0
|
||||
if model == "seedance_2.0_fast":
|
||||
@@ -171,7 +176,7 @@ class HiggsFieldVideo(BaseTool):
|
||||
api_key, api_secret = creds
|
||||
start = time.time()
|
||||
operation = inputs.get("operation", "text_to_video")
|
||||
model = inputs.get("model", "kling_3.0")
|
||||
model = inputs.get("model", _DEFAULT_MODEL)
|
||||
|
||||
payload: dict[str, Any] = {
|
||||
"prompt": inputs["prompt"],
|
||||
|
||||
@@ -47,6 +47,11 @@ _RUNTIME_SECONDS = {
|
||||
"seedance_2.0_fast": 60.0,
|
||||
}
|
||||
|
||||
# Single source of truth for the default model. Referenced by both the input
|
||||
# schema and every code path that reads `model`, so estimate_cost / estimate_runtime
|
||||
# / execute can never silently diverge from the advertised default again.
|
||||
_DEFAULT_MODEL = "seedance_2.0"
|
||||
|
||||
|
||||
class RunwayVideo(BaseTool):
|
||||
name = "runway_video"
|
||||
@@ -101,7 +106,7 @@ class RunwayVideo(BaseTool):
|
||||
"model": {
|
||||
"type": "string",
|
||||
"enum": ["seedance_2.0", "seedance_2.0_fast", "gen4_turbo", "gen4_aleph", "gen3a_turbo"],
|
||||
"default": "seedance_2.0",
|
||||
"default": _DEFAULT_MODEL,
|
||||
"description": (
|
||||
"seedance_2.0 = preferred premium default (single-pass synced audio, multi-shot, lip-sync — "
|
||||
"Runway Unlimited/Enterprise plan, non-US only). "
|
||||
@@ -149,12 +154,12 @@ class RunwayVideo(BaseTool):
|
||||
return os.environ.get("RUNWAY_API_KEY") or os.environ.get("RUNWAYML_API_SECRET")
|
||||
|
||||
def estimate_cost(self, inputs: dict[str, Any]) -> float:
|
||||
model = inputs.get("model", "gen4_turbo")
|
||||
model = inputs.get("model", _DEFAULT_MODEL)
|
||||
duration = inputs.get("duration", 5)
|
||||
return _COST_PER_SECOND.get(model, 0.05) * duration
|
||||
|
||||
def estimate_runtime(self, inputs: dict[str, Any]) -> float:
|
||||
model = inputs.get("model", "gen4_turbo")
|
||||
model = inputs.get("model", _DEFAULT_MODEL)
|
||||
return _RUNTIME_SECONDS.get(model, 30.0)
|
||||
|
||||
def execute(self, inputs: dict[str, Any]) -> ToolResult:
|
||||
@@ -168,7 +173,7 @@ class RunwayVideo(BaseTool):
|
||||
import requests
|
||||
|
||||
start = time.time()
|
||||
model = inputs.get("model", "gen4_turbo")
|
||||
model = inputs.get("model", _DEFAULT_MODEL)
|
||||
operation = inputs.get("operation", "text_to_video")
|
||||
ratio_friendly = inputs.get("ratio", "16:9")
|
||||
ratio_pixels = _RATIO_MAP.get(ratio_friendly, "1280:720")
|
||||
|
||||
Reference in New Issue
Block a user