From b5a26f5e80cf9694132aba5040a73af4b23f7c75 Mon Sep 17 00:00:00 2001 From: Thor Whalen <1906276+thorwhalen@users.noreply.github.com> Date: Tue, 13 Oct 2026 14:13:11 +0000 Subject: [PATCH 1/3] Catalogue the fal models reelee-web prices: ltx-video, mochi, kling v1.5, flux-pro (#82) Priced from fal's public model pages (read 2026-10-13); kling v1.5 standard has no published price and is flagged approximate. New entries carry quality_tier 'unranked' so no default pick changes. Co-Authored-By: Claude Sonnet 5.5 --- .claude/skills/falaw/SKILL.md | 7 ++ falaw/base.py | 2 +- falaw/data/models.json | 134 +++++++++++++++++++++++++++++ falaw/data/skills/falaw/SKILL.md | 7 ++ schema/constants.json | 2 +- schema/fixtures/plans.json | 30 +++---- schema/models.json | 134 +++++++++++++++++++++++++++++ tests/test_reelee_web_catalogue.py | 74 ++++++++++++++++ 8 files changed, 373 insertions(+), 17 deletions(-) create mode 100644 tests/test_reelee_web_catalogue.py diff --git a/.claude/skills/falaw/SKILL.md b/.claude/skills/falaw/SKILL.md index 6c8a940d..d1f26647 100644 --- a/.claude/skills/falaw/SKILL.md +++ b/.claude/skills/falaw/SKILL.md @@ -570,6 +570,7 @@ The model registry lives at `falaw/data/models.json`. Refresh it from image high fal-ai/flux-pro/v1.1 image ultra fal-ai/flux-pro/v1.1-ultra image ultra fal-ai/nano-banana-pro + image unranked fal-ai/flux-pro image_edit balanced fal-ai/flux-kontext/dev image_edit balanced fal-ai/omnigen-v2 image_edit high fal-ai/bytedance/seededit/v3/edit-image @@ -578,6 +579,9 @@ The model registry lives at `falaw/data/models.json`. Refresh it from image_to_video balanced fal-ai/pixverse/v4.5/image-to-video image_to_video high fal-ai/bytedance/seedance/v1/pro/image-to-video image_to_video ultra fal-ai/kling-video/v2.1/master/image-to-video + image_to_video unranked fal-ai/ltx-video/image-to-video + image_to_video unranked fal-ai/kling-video/v1.5/pro/image-to-video + image_to_video unranked fal-ai/kling-video/v1.5/standard lipsync balanced fal-ai/ai-avatar/multi lipsync balanced fal-ai/kling-video/lipsync/audio-to-video lipsync high fal-ai/sync-lipsync/v2 @@ -588,6 +592,9 @@ The model registry lives at `falaw/data/models.json`. Refresh it from music high fal-ai/lyria2 text_to_video high fal-ai/bytedance/seedance/v1/pro/text-to-video text_to_video ultra fal-ai/veo3 + text_to_video unranked fal-ai/ltx-video + text_to_video unranked fal-ai/mochi-v1 + text_to_video unranked fal-ai/kling-video/v1.5/pro/text-to-video training balanced fal-ai/flux-lora-portrait-trainer training fast fal-ai/flux-lora-fast-training training high fal-ai/flux-pro-trainer diff --git a/falaw/base.py b/falaw/base.py index 4f7c3332..0f950d9c 100644 --- a/falaw/base.py +++ b/falaw/base.py @@ -89,7 +89,7 @@ class ModelRecord: category: str description: str = "" aliases: tuple[str, ...] = () - quality_tier: str = "" # "fast" | "balanced" | "high" | "ultra" + quality_tier: str = "" # "fast" | "balanced" | "high" | "ultra" | "unranked" cost_hint: str = "" # legacy free-form text; kept for back-compat cost_estimate: Optional[CostEstimate] = None # quantitative; preferred docs_url: str = "" diff --git a/falaw/data/models.json b/falaw/data/models.json index 9f7de53f..218ed4db 100644 --- a/falaw/data/models.json +++ b/falaw/data/models.json @@ -780,5 +780,139 @@ "quality_tier": "high", "cost_hint": "medium", "docs_url": "https://fal.ai/models/fal-ai/minimax/voice-clone" + }, + { + "id": "fal-ai/ltx-video", + "category": "text_to_video", + "description": "LTX Video (preview): cheap open-weights text-to-video, reelee-web's default video model.", + "aliases": [ + "ltx-video" + ], + "quality_tier": "unranked", + "cost_hint": "very low", + "docs_url": "https://fal.ai/models/fal-ai/ltx-video", + "cost_estimate": { + "kind": "per_call", + "amount": 0.02, + "currency": "USD", + "notes": "fal model page: '$0.02 per video', read 2026-10-13. Flat per video; the schema exposes no length knob.", + "source": "docs" + }, + "max_clip_seconds": 5.0, + "single_character_recommended": true + }, + { + "id": "fal-ai/ltx-video/image-to-video", + "category": "image_to_video", + "description": "LTX Video (preview) image-to-video; same price as the text endpoint.", + "aliases": [ + "ltx-video-i2v" + ], + "quality_tier": "unranked", + "cost_hint": "very low", + "docs_url": "https://fal.ai/models/fal-ai/ltx-video/image-to-video", + "cost_estimate": { + "kind": "per_call", + "amount": 0.02, + "currency": "USD", + "notes": "fal model page: '$0.02 per video', read 2026-10-13. Flat per video.", + "source": "docs" + }, + "max_clip_seconds": 5.0, + "single_character_recommended": true + }, + { + "id": "fal-ai/mochi-v1", + "category": "text_to_video", + "description": "Mochi 1 preview text-to-video. Deprecated by fal.", + "aliases": [ + "mochi" + ], + "quality_tier": "unranked", + "cost_hint": "low", + "docs_url": "https://fal.ai/models/fal-ai/mochi-v1", + "cost_estimate": { + "kind": "per_call", + "amount": 0.4, + "currency": "USD", + "notes": "fal model page: '$0.4 per video', read 2026-10-13; endpoint marked deprecated. Default 163 frames at 30 fps (~5.4 s).", + "source": "docs" + }, + "max_clip_seconds": 5.4, + "single_character_recommended": true + }, + { + "id": "fal-ai/kling-video/v1.5/pro/image-to-video", + "category": "image_to_video", + "description": "Kling 1.5 (pro) image-to-video. Deprecated by fal. reelee-web's bare 'fal-ai/kling-video/v1.5/pro' id resolves here.", + "aliases": [ + "kling-video/v1.5/pro", + "fal-ai/kling-video/v1.5/pro" + ], + "quality_tier": "unranked", + "cost_hint": "medium", + "docs_url": "https://fal.ai/models/fal-ai/kling-video/v1.5/pro/image-to-video", + "cost_estimate": { + "kind": "per_second", + "amount": 0.1, + "currency": "USD", + "notes": "fal model page: '$0.1 per second', read 2026-10-13; endpoint marked deprecated.", + "source": "docs" + }, + "max_clip_seconds": 10.0 + }, + { + "id": "fal-ai/kling-video/v1.5/pro/text-to-video", + "category": "text_to_video", + "description": "Kling 1.5 (pro) text-to-video. Deprecated by fal.", + "aliases": [], + "quality_tier": "unranked", + "cost_hint": "medium", + "docs_url": "https://fal.ai/models/fal-ai/kling-video/v1.5/pro/text-to-video", + "cost_estimate": { + "kind": "per_second", + "amount": 0.1, + "currency": "USD", + "notes": "fal model page: '$0.1 per second', read 2026-10-13; endpoint marked deprecated.", + "source": "docs" + }, + "max_clip_seconds": 10.0 + }, + { + "id": "fal-ai/kling-video/v1.5/standard", + "category": "image_to_video", + "description": "Kling 1.5 (standard). fal publishes no endpoint metadata or price for it; reelee-web's id.", + "aliases": [ + "kling-video/v1.5/standard" + ], + "quality_tier": "unranked", + "cost_hint": "low", + "docs_url": "https://fal.ai/models/fal-ai/kling-video/v1.5/standard", + "cost_estimate": { + "kind": "per_second", + "amount": 0.045, + "currency": "USD", + "notes": "NO published price: fal's pages for v1.5 standard carry none (read 2026-10-13). Uses the published Kling 1.0 standard rate ($0.045 per second, deprecated endpoint) as the nearest tier. Replace when fal publishes one.", + "source": "approximate" + }, + "max_clip_seconds": 10.0 + }, + { + "id": "fal-ai/flux-pro", + "category": "image", + "description": "FLUX.1 [pro] (original endpoint). Deprecated by fal; fal-ai/flux-pro/new and v1.1 replace it.", + "aliases": [ + "flux-pro" + ], + "quality_tier": "unranked", + "cost_hint": "low", + "docs_url": "https://fal.ai/models/fal-ai/flux-pro", + "cost_estimate": { + "kind": "per_megapixel", + "amount": 0.05, + "currency": "USD", + "notes": "fal model page: '$0.05 per megapixel', rounded up to the next megapixel, read 2026-10-13; endpoint marked deprecated.", + "source": "docs" + } } ] diff --git a/falaw/data/skills/falaw/SKILL.md b/falaw/data/skills/falaw/SKILL.md index 6c8a940d..d1f26647 100644 --- a/falaw/data/skills/falaw/SKILL.md +++ b/falaw/data/skills/falaw/SKILL.md @@ -570,6 +570,7 @@ The model registry lives at `falaw/data/models.json`. Refresh it from image high fal-ai/flux-pro/v1.1 image ultra fal-ai/flux-pro/v1.1-ultra image ultra fal-ai/nano-banana-pro + image unranked fal-ai/flux-pro image_edit balanced fal-ai/flux-kontext/dev image_edit balanced fal-ai/omnigen-v2 image_edit high fal-ai/bytedance/seededit/v3/edit-image @@ -578,6 +579,9 @@ The model registry lives at `falaw/data/models.json`. Refresh it from image_to_video balanced fal-ai/pixverse/v4.5/image-to-video image_to_video high fal-ai/bytedance/seedance/v1/pro/image-to-video image_to_video ultra fal-ai/kling-video/v2.1/master/image-to-video + image_to_video unranked fal-ai/ltx-video/image-to-video + image_to_video unranked fal-ai/kling-video/v1.5/pro/image-to-video + image_to_video unranked fal-ai/kling-video/v1.5/standard lipsync balanced fal-ai/ai-avatar/multi lipsync balanced fal-ai/kling-video/lipsync/audio-to-video lipsync high fal-ai/sync-lipsync/v2 @@ -588,6 +592,9 @@ The model registry lives at `falaw/data/models.json`. Refresh it from music high fal-ai/lyria2 text_to_video high fal-ai/bytedance/seedance/v1/pro/text-to-video text_to_video ultra fal-ai/veo3 + text_to_video unranked fal-ai/ltx-video + text_to_video unranked fal-ai/mochi-v1 + text_to_video unranked fal-ai/kling-video/v1.5/pro/text-to-video training balanced fal-ai/flux-lora-portrait-trainer training fast fal-ai/flux-lora-fast-training training high fal-ai/flux-pro-trainer diff --git a/schema/constants.json b/schema/constants.json index 63711ca7..665407e0 100644 --- a/schema/constants.json +++ b/schema/constants.json @@ -40,7 +40,7 @@ ] ], "model_catalogue_table": "falaw/data/models.json", - "models_table_version": "70b26c6bfd7b", + "models_table_version": "06897dfcc04a", "output_kinds": [ "image", "video", diff --git a/schema/fixtures/plans.json b/schema/fixtures/plans.json index f40c3307..5f221743 100644 --- a/schema/fixtures/plans.json +++ b/schema/fixtures/plans.json @@ -14,7 +14,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": 0.015, "expected_duration_s": null, @@ -43,7 +43,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": 0.0018, "expected_duration_s": null, @@ -73,7 +73,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": 0.06, "expected_duration_s": null, @@ -116,7 +116,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": 0.015, "expected_duration_s": null, @@ -160,7 +160,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": 0.015, "expected_duration_s": null, @@ -195,7 +195,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": null, "expected_duration_s": null, @@ -226,7 +226,7 @@ "seconds": 6 }, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": 1.2000000000000002, "expected_duration_s": null, @@ -258,7 +258,7 @@ "seconds": 5.5 }, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": 1.54, "expected_duration_s": null, @@ -290,7 +290,7 @@ "seconds": 6 }, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": 0.48, "expected_duration_s": null, @@ -329,7 +329,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": 0.015, "expected_duration_s": null, @@ -350,7 +350,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": null, "expected_duration_s": null, @@ -398,7 +398,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": 0.015, "expected_duration_s": null, @@ -421,7 +421,7 @@ "seconds": 6 }, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": 0.48, "expected_duration_s": null, @@ -457,7 +457,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": 0.0018, "expected_duration_s": null, @@ -479,7 +479,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "70b26c6bfd7b" + "table_version": "06897dfcc04a" }, "estimated_cost_usd": 0.0018, "expected_duration_s": null, diff --git a/schema/models.json b/schema/models.json index 9f7de53f..218ed4db 100644 --- a/schema/models.json +++ b/schema/models.json @@ -780,5 +780,139 @@ "quality_tier": "high", "cost_hint": "medium", "docs_url": "https://fal.ai/models/fal-ai/minimax/voice-clone" + }, + { + "id": "fal-ai/ltx-video", + "category": "text_to_video", + "description": "LTX Video (preview): cheap open-weights text-to-video, reelee-web's default video model.", + "aliases": [ + "ltx-video" + ], + "quality_tier": "unranked", + "cost_hint": "very low", + "docs_url": "https://fal.ai/models/fal-ai/ltx-video", + "cost_estimate": { + "kind": "per_call", + "amount": 0.02, + "currency": "USD", + "notes": "fal model page: '$0.02 per video', read 2026-10-13. Flat per video; the schema exposes no length knob.", + "source": "docs" + }, + "max_clip_seconds": 5.0, + "single_character_recommended": true + }, + { + "id": "fal-ai/ltx-video/image-to-video", + "category": "image_to_video", + "description": "LTX Video (preview) image-to-video; same price as the text endpoint.", + "aliases": [ + "ltx-video-i2v" + ], + "quality_tier": "unranked", + "cost_hint": "very low", + "docs_url": "https://fal.ai/models/fal-ai/ltx-video/image-to-video", + "cost_estimate": { + "kind": "per_call", + "amount": 0.02, + "currency": "USD", + "notes": "fal model page: '$0.02 per video', read 2026-10-13. Flat per video.", + "source": "docs" + }, + "max_clip_seconds": 5.0, + "single_character_recommended": true + }, + { + "id": "fal-ai/mochi-v1", + "category": "text_to_video", + "description": "Mochi 1 preview text-to-video. Deprecated by fal.", + "aliases": [ + "mochi" + ], + "quality_tier": "unranked", + "cost_hint": "low", + "docs_url": "https://fal.ai/models/fal-ai/mochi-v1", + "cost_estimate": { + "kind": "per_call", + "amount": 0.4, + "currency": "USD", + "notes": "fal model page: '$0.4 per video', read 2026-10-13; endpoint marked deprecated. Default 163 frames at 30 fps (~5.4 s).", + "source": "docs" + }, + "max_clip_seconds": 5.4, + "single_character_recommended": true + }, + { + "id": "fal-ai/kling-video/v1.5/pro/image-to-video", + "category": "image_to_video", + "description": "Kling 1.5 (pro) image-to-video. Deprecated by fal. reelee-web's bare 'fal-ai/kling-video/v1.5/pro' id resolves here.", + "aliases": [ + "kling-video/v1.5/pro", + "fal-ai/kling-video/v1.5/pro" + ], + "quality_tier": "unranked", + "cost_hint": "medium", + "docs_url": "https://fal.ai/models/fal-ai/kling-video/v1.5/pro/image-to-video", + "cost_estimate": { + "kind": "per_second", + "amount": 0.1, + "currency": "USD", + "notes": "fal model page: '$0.1 per second', read 2026-10-13; endpoint marked deprecated.", + "source": "docs" + }, + "max_clip_seconds": 10.0 + }, + { + "id": "fal-ai/kling-video/v1.5/pro/text-to-video", + "category": "text_to_video", + "description": "Kling 1.5 (pro) text-to-video. Deprecated by fal.", + "aliases": [], + "quality_tier": "unranked", + "cost_hint": "medium", + "docs_url": "https://fal.ai/models/fal-ai/kling-video/v1.5/pro/text-to-video", + "cost_estimate": { + "kind": "per_second", + "amount": 0.1, + "currency": "USD", + "notes": "fal model page: '$0.1 per second', read 2026-10-13; endpoint marked deprecated.", + "source": "docs" + }, + "max_clip_seconds": 10.0 + }, + { + "id": "fal-ai/kling-video/v1.5/standard", + "category": "image_to_video", + "description": "Kling 1.5 (standard). fal publishes no endpoint metadata or price for it; reelee-web's id.", + "aliases": [ + "kling-video/v1.5/standard" + ], + "quality_tier": "unranked", + "cost_hint": "low", + "docs_url": "https://fal.ai/models/fal-ai/kling-video/v1.5/standard", + "cost_estimate": { + "kind": "per_second", + "amount": 0.045, + "currency": "USD", + "notes": "NO published price: fal's pages for v1.5 standard carry none (read 2026-10-13). Uses the published Kling 1.0 standard rate ($0.045 per second, deprecated endpoint) as the nearest tier. Replace when fal publishes one.", + "source": "approximate" + }, + "max_clip_seconds": 10.0 + }, + { + "id": "fal-ai/flux-pro", + "category": "image", + "description": "FLUX.1 [pro] (original endpoint). Deprecated by fal; fal-ai/flux-pro/new and v1.1 replace it.", + "aliases": [ + "flux-pro" + ], + "quality_tier": "unranked", + "cost_hint": "low", + "docs_url": "https://fal.ai/models/fal-ai/flux-pro", + "cost_estimate": { + "kind": "per_megapixel", + "amount": 0.05, + "currency": "USD", + "notes": "fal model page: '$0.05 per megapixel', rounded up to the next megapixel, read 2026-10-13; endpoint marked deprecated.", + "source": "docs" + } } ] diff --git a/tests/test_reelee_web_catalogue.py b/tests/test_reelee_web_catalogue.py new file mode 100644 index 00000000..b4a79292 --- /dev/null +++ b/tests/test_reelee_web_catalogue.py @@ -0,0 +1,74 @@ +"""Every fal model reelee-web sends through reelee's proxy is catalogued and priced (falaw#82). + +reelee prices each proxied submission with ``get_model`` + ``estimate_call_cost``; +a model missing here is recorded as an unknown $0.00 and never moves a spend cap. +No network: this reads the committed catalogue only. +""" + +import pytest + +from falaw import estimate_call_cost, get_model + +# The ids in reelee-web's ``FAL_PRICE_TABLE_USD`` (src/ai/image-providers/fal-pricing.ts). +REELEE_WEB_IDS = ( + "fal-ai/flux/schnell", + "fal-ai/flux/dev", + "fal-ai/flux-pro/v1.1", + "fal-ai/flux-pro", + "fal-ai/flux-pro/v1.1-ultra", + "fal-ai/ltx-video", + "fal-ai/ltx-video/image-to-video", + "fal-ai/mochi-v1", + "fal-ai/kling-video/v1.5/pro", + "fal-ai/kling-video/v1.5/standard", +) +VIDEO_IDS = REELEE_WEB_IDS[5:] +CLIP_SECONDS = 5.0 + + +@pytest.mark.parametrize("model_id", REELEE_WEB_IDS) +def test_every_reelee_web_model_is_priced(model_id): + record = get_model(model_id) + assert record.cost_estimate is not None + assert estimate_call_cost(record, seconds=CLIP_SECONDS) > 0 + + +@pytest.mark.parametrize("model_id", VIDEO_IDS) +def test_video_models_declare_a_clip_limit(model_id): + record = get_model(model_id) + assert record.max_clip_seconds is not None + assert record.max_clip_seconds >= CLIP_SECONDS + + +@pytest.mark.parametrize( + "model_id, expected_usd", + [ + ("fal-ai/ltx-video", 0.02), + ("fal-ai/ltx-video/image-to-video", 0.02), + ("fal-ai/mochi-v1", 0.4), + ("fal-ai/kling-video/v1.5/pro", 0.5), # $0.1/s + ], +) +def test_five_second_clip_prices_from_fal_pages(model_id, expected_usd): + cost = estimate_call_cost(get_model(model_id), seconds=CLIP_SECONDS) + assert cost == pytest.approx(expected_usd) + + +def test_bare_kling_pro_id_resolves_to_a_real_endpoint(): + assert get_model("fal-ai/kling-video/v1.5/pro").id.endswith("/pro/image-to-video") + + +def test_unpublished_price_is_flagged_approximate(): + ce = get_model("fal-ai/kling-video/v1.5/standard").cost_estimate + assert ce.source == "approximate" + + +def test_catalogued_only_models_never_become_a_default_pick(): + """Deprecated / research-licence entries are priced, not recommended (``unranked``).""" + import falaw + + unranked = {m.id for m in falaw.list_models(quality_tier="unranked")} + assert unranked, "the reelee-web-only models should carry the 'unranked' tier" + for category in {m.category for m in falaw.list_models()}: + for tier in ("fast", "balanced", "high", "ultra"): + assert falaw.pick_model(category=category, quality_tier=tier).id not in unranked From 19804d731e11d9fe73806e0f4d8766b717328800 Mon Sep 17 00:00:00 2001 From: Thor Whalen <1906276+thorwhalen@users.noreply.github.com> Date: Tue, 13 Oct 2026 14:15:48 +0000 Subject: [PATCH 2/3] Regenerate the TS catalogue (npm codegen) for the new models (#82) Co-Authored-By: Claude Sonnet 5.5 --- ts/src/generated/constants.ts | 2 +- ts/src/generated/models.ts | 136 +++++++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 2 deletions(-) diff --git a/ts/src/generated/constants.ts b/ts/src/generated/constants.ts index 0e71dcfd..bb4ab7f2 100644 --- a/ts/src/generated/constants.ts +++ b/ts/src/generated/constants.ts @@ -43,7 +43,7 @@ export const CONSTANTS = { ] ], "model_catalogue_table": "falaw/data/models.json", - "models_table_version": "70b26c6bfd7b", + "models_table_version": "06897dfcc04a", "output_kinds": [ "image", "video", diff --git a/ts/src/generated/models.ts b/ts/src/generated/models.ts index 936a824e..eeb22464 100644 --- a/ts/src/generated/models.ts +++ b/ts/src/generated/models.ts @@ -5,7 +5,7 @@ import { z } from 'zod'; import { type ModelRecord, modelRecordSchema } from './model-record'; /** Digest of the committed catalogue bytes — what a CostBasis records as `table_version`. */ -export const MODELS_TABLE_VERSION = "70b26c6bfd7b"; +export const MODELS_TABLE_VERSION = "06897dfcc04a"; /** The fal model catalogue, in catalogue order (curated entries first). Parsed through the * schema at import so every record carries its defaults, as the Python dataclass does. */ @@ -791,5 +791,139 @@ export const MODELS: readonly ModelRecord[] = z.array(modelRecordSchema).parse([ "quality_tier": "high", "cost_hint": "medium", "docs_url": "https://fal.ai/models/fal-ai/minimax/voice-clone" + }, + { + "id": "fal-ai/ltx-video", + "category": "text_to_video", + "description": "LTX Video (preview): cheap open-weights text-to-video, reelee-web's default video model.", + "aliases": [ + "ltx-video" + ], + "quality_tier": "unranked", + "cost_hint": "very low", + "docs_url": "https://fal.ai/models/fal-ai/ltx-video", + "cost_estimate": { + "kind": "per_call", + "amount": 0.02, + "currency": "USD", + "notes": "fal model page: '$0.02 per video', read 2026-10-13. Flat per video; the schema exposes no length knob.", + "source": "docs" + }, + "max_clip_seconds": 5, + "single_character_recommended": true + }, + { + "id": "fal-ai/ltx-video/image-to-video", + "category": "image_to_video", + "description": "LTX Video (preview) image-to-video; same price as the text endpoint.", + "aliases": [ + "ltx-video-i2v" + ], + "quality_tier": "unranked", + "cost_hint": "very low", + "docs_url": "https://fal.ai/models/fal-ai/ltx-video/image-to-video", + "cost_estimate": { + "kind": "per_call", + "amount": 0.02, + "currency": "USD", + "notes": "fal model page: '$0.02 per video', read 2026-10-13. Flat per video.", + "source": "docs" + }, + "max_clip_seconds": 5, + "single_character_recommended": true + }, + { + "id": "fal-ai/mochi-v1", + "category": "text_to_video", + "description": "Mochi 1 preview text-to-video. Deprecated by fal.", + "aliases": [ + "mochi" + ], + "quality_tier": "unranked", + "cost_hint": "low", + "docs_url": "https://fal.ai/models/fal-ai/mochi-v1", + "cost_estimate": { + "kind": "per_call", + "amount": 0.4, + "currency": "USD", + "notes": "fal model page: '$0.4 per video', read 2026-10-13; endpoint marked deprecated. Default 163 frames at 30 fps (~5.4 s).", + "source": "docs" + }, + "max_clip_seconds": 5.4, + "single_character_recommended": true + }, + { + "id": "fal-ai/kling-video/v1.5/pro/image-to-video", + "category": "image_to_video", + "description": "Kling 1.5 (pro) image-to-video. Deprecated by fal. reelee-web's bare 'fal-ai/kling-video/v1.5/pro' id resolves here.", + "aliases": [ + "kling-video/v1.5/pro", + "fal-ai/kling-video/v1.5/pro" + ], + "quality_tier": "unranked", + "cost_hint": "medium", + "docs_url": "https://fal.ai/models/fal-ai/kling-video/v1.5/pro/image-to-video", + "cost_estimate": { + "kind": "per_second", + "amount": 0.1, + "currency": "USD", + "notes": "fal model page: '$0.1 per second', read 2026-10-13; endpoint marked deprecated.", + "source": "docs" + }, + "max_clip_seconds": 10 + }, + { + "id": "fal-ai/kling-video/v1.5/pro/text-to-video", + "category": "text_to_video", + "description": "Kling 1.5 (pro) text-to-video. Deprecated by fal.", + "aliases": [], + "quality_tier": "unranked", + "cost_hint": "medium", + "docs_url": "https://fal.ai/models/fal-ai/kling-video/v1.5/pro/text-to-video", + "cost_estimate": { + "kind": "per_second", + "amount": 0.1, + "currency": "USD", + "notes": "fal model page: '$0.1 per second', read 2026-10-13; endpoint marked deprecated.", + "source": "docs" + }, + "max_clip_seconds": 10 + }, + { + "id": "fal-ai/kling-video/v1.5/standard", + "category": "image_to_video", + "description": "Kling 1.5 (standard). fal publishes no endpoint metadata or price for it; reelee-web's id.", + "aliases": [ + "kling-video/v1.5/standard" + ], + "quality_tier": "unranked", + "cost_hint": "low", + "docs_url": "https://fal.ai/models/fal-ai/kling-video/v1.5/standard", + "cost_estimate": { + "kind": "per_second", + "amount": 0.045, + "currency": "USD", + "notes": "NO published price: fal's pages for v1.5 standard carry none (read 2026-10-13). Uses the published Kling 1.0 standard rate ($0.045 per second, deprecated endpoint) as the nearest tier. Replace when fal publishes one.", + "source": "approximate" + }, + "max_clip_seconds": 10 + }, + { + "id": "fal-ai/flux-pro", + "category": "image", + "description": "FLUX.1 [pro] (original endpoint). Deprecated by fal; fal-ai/flux-pro/new and v1.1 replace it.", + "aliases": [ + "flux-pro" + ], + "quality_tier": "unranked", + "cost_hint": "low", + "docs_url": "https://fal.ai/models/fal-ai/flux-pro", + "cost_estimate": { + "kind": "per_megapixel", + "amount": 0.05, + "currency": "USD", + "notes": "fal model page: '$0.05 per megapixel', rounded up to the next megapixel, read 2026-10-13; endpoint marked deprecated.", + "source": "docs" + } } ]); From 268ad20fac337423f72db2d39f4c41771ec37533 Mon Sep 17 00:00:00 2001 From: Thor Whalen <1906276+thorwhalen@users.noreply.github.com> Date: Tue, 13 Oct 2026 14:16:34 +0000 Subject: [PATCH 3/3] Drop the duplicate flux-pro alias, unevidenced single-character flag; test alias uniqueness (#82) Co-Authored-By: Claude Sonnet 5.5 --- falaw/data/models.json | 10 ++++------ schema/constants.json | 2 +- schema/fixtures/plans.json | 30 +++++++++++++++--------------- schema/models.json | 10 ++++------ tests/test_reelee_web_catalogue.py | 11 +++++++++++ ts/src/generated/constants.ts | 2 +- ts/src/generated/models.ts | 12 +++++------- 7 files changed, 41 insertions(+), 36 deletions(-) diff --git a/falaw/data/models.json b/falaw/data/models.json index 218ed4db..1dce580a 100644 --- a/falaw/data/models.json +++ b/falaw/data/models.json @@ -799,7 +799,7 @@ "source": "docs" }, "max_clip_seconds": 5.0, - "single_character_recommended": true + "single_character_recommended": false }, { "id": "fal-ai/ltx-video/image-to-video", @@ -819,7 +819,7 @@ "source": "docs" }, "max_clip_seconds": 5.0, - "single_character_recommended": true + "single_character_recommended": false }, { "id": "fal-ai/mochi-v1", @@ -839,7 +839,7 @@ "source": "docs" }, "max_clip_seconds": 5.4, - "single_character_recommended": true + "single_character_recommended": false }, { "id": "fal-ai/kling-video/v1.5/pro/image-to-video", @@ -901,9 +901,7 @@ "id": "fal-ai/flux-pro", "category": "image", "description": "FLUX.1 [pro] (original endpoint). Deprecated by fal; fal-ai/flux-pro/new and v1.1 replace it.", - "aliases": [ - "flux-pro" - ], + "aliases": [], "quality_tier": "unranked", "cost_hint": "low", "docs_url": "https://fal.ai/models/fal-ai/flux-pro", diff --git a/schema/constants.json b/schema/constants.json index 665407e0..1194a3fa 100644 --- a/schema/constants.json +++ b/schema/constants.json @@ -40,7 +40,7 @@ ] ], "model_catalogue_table": "falaw/data/models.json", - "models_table_version": "06897dfcc04a", + "models_table_version": "5b7be5d7b10e", "output_kinds": [ "image", "video", diff --git a/schema/fixtures/plans.json b/schema/fixtures/plans.json index 5f221743..49e79be9 100644 --- a/schema/fixtures/plans.json +++ b/schema/fixtures/plans.json @@ -14,7 +14,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": 0.015, "expected_duration_s": null, @@ -43,7 +43,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": 0.0018, "expected_duration_s": null, @@ -73,7 +73,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": 0.06, "expected_duration_s": null, @@ -116,7 +116,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": 0.015, "expected_duration_s": null, @@ -160,7 +160,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": 0.015, "expected_duration_s": null, @@ -195,7 +195,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": null, "expected_duration_s": null, @@ -226,7 +226,7 @@ "seconds": 6 }, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": 1.2000000000000002, "expected_duration_s": null, @@ -258,7 +258,7 @@ "seconds": 5.5 }, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": 1.54, "expected_duration_s": null, @@ -290,7 +290,7 @@ "seconds": 6 }, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": 0.48, "expected_duration_s": null, @@ -329,7 +329,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": 0.015, "expected_duration_s": null, @@ -350,7 +350,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": null, "expected_duration_s": null, @@ -398,7 +398,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": 0.015, "expected_duration_s": null, @@ -421,7 +421,7 @@ "seconds": 6 }, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": 0.48, "expected_duration_s": null, @@ -457,7 +457,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": 0.0018, "expected_duration_s": null, @@ -479,7 +479,7 @@ "pricer": "model_catalogue", "quantities": {}, "table": "falaw/data/models.json", - "table_version": "06897dfcc04a" + "table_version": "5b7be5d7b10e" }, "estimated_cost_usd": 0.0018, "expected_duration_s": null, diff --git a/schema/models.json b/schema/models.json index 218ed4db..1dce580a 100644 --- a/schema/models.json +++ b/schema/models.json @@ -799,7 +799,7 @@ "source": "docs" }, "max_clip_seconds": 5.0, - "single_character_recommended": true + "single_character_recommended": false }, { "id": "fal-ai/ltx-video/image-to-video", @@ -819,7 +819,7 @@ "source": "docs" }, "max_clip_seconds": 5.0, - "single_character_recommended": true + "single_character_recommended": false }, { "id": "fal-ai/mochi-v1", @@ -839,7 +839,7 @@ "source": "docs" }, "max_clip_seconds": 5.4, - "single_character_recommended": true + "single_character_recommended": false }, { "id": "fal-ai/kling-video/v1.5/pro/image-to-video", @@ -901,9 +901,7 @@ "id": "fal-ai/flux-pro", "category": "image", "description": "FLUX.1 [pro] (original endpoint). Deprecated by fal; fal-ai/flux-pro/new and v1.1 replace it.", - "aliases": [ - "flux-pro" - ], + "aliases": [], "quality_tier": "unranked", "cost_hint": "low", "docs_url": "https://fal.ai/models/fal-ai/flux-pro", diff --git a/tests/test_reelee_web_catalogue.py b/tests/test_reelee_web_catalogue.py index b4a79292..305f613d 100644 --- a/tests/test_reelee_web_catalogue.py +++ b/tests/test_reelee_web_catalogue.py @@ -72,3 +72,14 @@ def test_catalogued_only_models_never_become_a_default_pick(): for category in {m.category for m in falaw.list_models()}: for tier in ("fast", "balanced", "high", "ultra"): assert falaw.pick_model(category=category, quality_tier=tier).id not in unranked + + +def test_no_alias_names_two_models(): + from collections import Counter + + import falaw + + names = Counter( + n for m in falaw.list_models() for n in (m.id, *m.aliases) + ) + assert {n: c for n, c in names.items() if c > 1} == {} diff --git a/ts/src/generated/constants.ts b/ts/src/generated/constants.ts index bb4ab7f2..472ccebd 100644 --- a/ts/src/generated/constants.ts +++ b/ts/src/generated/constants.ts @@ -43,7 +43,7 @@ export const CONSTANTS = { ] ], "model_catalogue_table": "falaw/data/models.json", - "models_table_version": "06897dfcc04a", + "models_table_version": "5b7be5d7b10e", "output_kinds": [ "image", "video", diff --git a/ts/src/generated/models.ts b/ts/src/generated/models.ts index eeb22464..3c44f953 100644 --- a/ts/src/generated/models.ts +++ b/ts/src/generated/models.ts @@ -5,7 +5,7 @@ import { z } from 'zod'; import { type ModelRecord, modelRecordSchema } from './model-record'; /** Digest of the committed catalogue bytes — what a CostBasis records as `table_version`. */ -export const MODELS_TABLE_VERSION = "06897dfcc04a"; +export const MODELS_TABLE_VERSION = "5b7be5d7b10e"; /** The fal model catalogue, in catalogue order (curated entries first). Parsed through the * schema at import so every record carries its defaults, as the Python dataclass does. */ @@ -810,7 +810,7 @@ export const MODELS: readonly ModelRecord[] = z.array(modelRecordSchema).parse([ "source": "docs" }, "max_clip_seconds": 5, - "single_character_recommended": true + "single_character_recommended": false }, { "id": "fal-ai/ltx-video/image-to-video", @@ -830,7 +830,7 @@ export const MODELS: readonly ModelRecord[] = z.array(modelRecordSchema).parse([ "source": "docs" }, "max_clip_seconds": 5, - "single_character_recommended": true + "single_character_recommended": false }, { "id": "fal-ai/mochi-v1", @@ -850,7 +850,7 @@ export const MODELS: readonly ModelRecord[] = z.array(modelRecordSchema).parse([ "source": "docs" }, "max_clip_seconds": 5.4, - "single_character_recommended": true + "single_character_recommended": false }, { "id": "fal-ai/kling-video/v1.5/pro/image-to-video", @@ -912,9 +912,7 @@ export const MODELS: readonly ModelRecord[] = z.array(modelRecordSchema).parse([ "id": "fal-ai/flux-pro", "category": "image", "description": "FLUX.1 [pro] (original endpoint). Deprecated by fal; fal-ai/flux-pro/new and v1.1 replace it.", - "aliases": [ - "flux-pro" - ], + "aliases": [], "quality_tier": "unranked", "cost_hint": "low", "docs_url": "https://fal.ai/models/fal-ai/flux-pro",