diff --git a/studio_api/meta.py b/studio_api/meta.py index d238512d..3308a6ca 100644 --- a/studio_api/meta.py +++ b/studio_api/meta.py @@ -78,7 +78,6 @@ def role_card_preview(stem: str) -> dict[str, Any]: """Return a text preview of one Role Card (path-safe).""" if not _SAFE_STEM.match(stem or ""): raise ValueError("Invalid role card name") - # Disallow traversal fragments even if regex passes if ".." in stem or "/" in stem or "\\" in stem: raise ValueError("Invalid role card name") @@ -87,7 +86,6 @@ def role_card_preview(stem: str) -> dict[str, Any]: agents_dir = AGENTS_DIR.resolve() path = (agents_dir / f"{stem}.md").resolve() if not str(path).startswith(str(agents_dir)) or not path.is_file(): - # Allow exact match from listed cards only allowed = {p.stem: p for p in list_role_card_files()} path = allowed.get(stem) if path is None: @@ -132,7 +130,6 @@ def production_options() -> dict[str, Any]: image_models = [ "grok-imagine-image", "grok-imagine-image-2.0", - "grok-imagine-image-quality", ] surfaces = [ "grok_build_tools", diff --git a/tests/test_models_image.py b/tests/test_models_image.py index 4eaef77f..53e34077 100644 --- a/tests/test_models_image.py +++ b/tests/test_models_image.py @@ -18,6 +18,7 @@ imagine_surface_catalog, ordered_image_model_slugs, resolve_image_model, + serve_image_model, verify_model_compatibility, ) @@ -53,6 +54,8 @@ def test_image_2_0_pricing_tiers() -> None: assert image_usd_per_image("grok-imagine-image-2.0") == 0.04 assert image_usd_per_image("2.0", resolution="1k", quality="low") == 0.04 assert image_usd_per_image("2.0", resolution="2k", quality="low") == 0.06 + assert image_usd_per_image("2.0", resolution="1k", quality="medium") == 0.06 + assert image_usd_per_image("2.0", resolution="2k", quality="medium") == 0.08 catalog = imagine_surface_catalog() assert catalog["routing"]["image_hero"] == HERO_IMAGINE_IMAGE_MODEL assert any(s["id"] == "xai_responses_tool" for s in catalog["agent_mode_surfaces"]) @@ -66,6 +69,34 @@ def test_pricing_table_matches_registry() -> None: assert rates["usd_per_image"] == IMAGINE_IMAGE_MODELS[slug]["usd_per_image"] +def test_serve_image_model_remaps_legacy() -> None: + assert serve_image_model("grok-imagine-image-quality") == ( + "grok-imagine-image-2.0", + "low", + ) + assert serve_image_model("quality", role="hero") == ( + "grok-imagine-image-2.0", + "medium", + ) + assert serve_image_model("grok-imagine-image") == ("grok-imagine-image", None) + assert serve_image_model("2.0", quality="high") == ( + "grok-imagine-image-2.0", + "low", + ) + assert serve_image_model("2.0", quality="medium") == ( + "grok-imagine-image-2.0", + "medium", + ) + assert serve_image_model("2.0", quality="") == ( + "grok-imagine-image-2.0", + "low", + ) + assert resolve_image_model("pro") == LEGACY_QUALITY_IMAGE_MODEL + info = IMAGINE_IMAGE_MODELS[LEGACY_QUALITY_IMAGE_MODEL] + assert info.get("retired") is True + assert info.get("retire_date") == "2026-11-02" + + def test_model_compatibility() -> None: result = verify_model_compatibility() assert result["compatible"], result["issues"] @@ -74,6 +105,9 @@ def test_model_compatibility() -> None: if __name__ == "__main__": test_default_image_model() test_xai_api_aliases_resolve() + test_hero_image_is_2_0() + test_image_2_0_pricing_tiers() + test_serve_image_model_remaps_legacy() test_pricing_table_matches_registry() test_model_compatibility() - print("All image model tests passed") \ No newline at end of file + print("All image model tests passed") diff --git a/tools/batch_runner.py b/tools/batch_runner.py index 8c213238..ab424353 100644 --- a/tools/batch_runner.py +++ b/tools/batch_runner.py @@ -21,6 +21,7 @@ submit_video_generation, ) from imagine_jobs import create_job, get_reference_asset, transition_job +from models import HERO_IMAGINE_IMAGE_MODEL from quota_sync import record_generation_spend @@ -105,11 +106,13 @@ def execute_shot( vid_model = shot.get("video_model", "grok-imagine-video-1.5") model = vid_model + image_quality_pin: str | None = None if mode == "image_prompt": job_type = "image" model = img_model if shot.get("image_quality"): - model = "grok-imagine-image-quality" + model = HERO_IMAGINE_IMAGE_MODEL + image_quality_pin = "medium" elif mode == "image_to_video": job_type = "video" model = vid_model @@ -138,7 +141,12 @@ def execute_shot( try: if mode == "image_prompt": - resp = generate_image(prompt, model=model, dry_run=use_dry) + resp = generate_image( + prompt, + model=model, + quality=image_quality_pin, + dry_run=use_dry, + ) result_url = extract_image_url(resp) transition_job(job["job_id"], "qa_pending", result_url=result_url) shot["result_url"] = result_url @@ -232,4 +240,4 @@ def execute_sfw_shot(batch: dict[str, Any], shot_id: str, **kwargs: Any) -> dict def execute_nsfw_shot(batch: dict[str, Any], shot_id: str, **kwargs: Any) -> dict[str, Any]: - return execute_shot(batch, shot_id, pipeline=nsfw_pipeline(), **kwargs) \ No newline at end of file + return execute_shot(batch, shot_id, pipeline=nsfw_pipeline(), **kwargs)