`image_gen.provider: nous` fronts three gateways; the stored model id now says which one serves a request (tools/image_generation_managed.py): FAL catalog ids → FAL gateway, Krea plugin ids → Krea gateway, anything else → the Nous Portal image plugin. Before, a Portal model id under the managed selection fell through to FAL, which warned about an unknown model and billed a FLUX default instead. The same module builds the managed row's union catalog with each model listed once (FAL wins identical/cross-id duplicates; Krea 2 appears natively, never as the FAL or Portal copy), so the picker and the runtime agree on one mapping.
85 lines
3.5 KiB
Python
85 lines
3.5 KiB
Python
"""Managed ("Nous Subscription") image generation: one picker row, one model catalog.
|
|
|
|
Three gateways sit behind the single stored selection ``image_gen.provider: nous``; the stored
|
|
``image_gen.model`` decides which one serves a request:
|
|
|
|
* ``fal`` — the FAL managed gateway, every id in the in-tree ``FAL_MODELS`` catalog;
|
|
* ``krea`` — the Krea managed gateway, the ``plugins/image_gen/krea`` model ids;
|
|
* ``portal`` — Nous Portal chat-completions image models (``plugins/image_gen/openrouter``'s
|
|
``nous`` provider), anything else.
|
|
|
|
Before this module the second and third gateways each had their own picker row that also
|
|
wrote ``provider: nous`` — every managed row read "active" at once and picking the Portal row
|
|
silently generated on FAL.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Dict, Optional, Tuple
|
|
|
|
FAL, KREA, PORTAL = "fal", "krea", "portal"
|
|
|
|
# Portal ids that are the same model as a FAL or Krea catalog entry. FAL wins (it is the
|
|
# gateway the free tool pool funds); Krea 2 goes through its native gateway, never FAL or the
|
|
# Portal. Only cross-id duplicates live here — identical ids collapse by key.
|
|
_PORTAL_DUPLICATE_OF = {
|
|
"openai/gpt-image-2": "fal-ai/gpt-image-2",
|
|
"google/gemini-3-pro-image": "fal-ai/nano-banana-pro",
|
|
"google/gemini-3.1-flash-image": "fal-ai/nano-banana-2",
|
|
"google/gemini-3.1-flash-lite-image": "google/nano-banana-2-lite",
|
|
"krea/krea-2-medium": "krea-2-medium",
|
|
"krea/krea-2-medium-turbo": "krea-2-medium-turbo",
|
|
"krea/krea-2-large": "krea-2-large",
|
|
}
|
|
_FAL_KREA_PREFIX = "fal-ai/krea/"
|
|
|
|
|
|
def managed_backend_for_model(model_id: Optional[str]) -> str:
|
|
"""Gateway that serves ``model_id`` under the managed selection (unset → FAL default)."""
|
|
from plugins.image_gen.krea import KREA_MODEL_IDS
|
|
from tools.image_generation_catalog import FAL_MODELS
|
|
|
|
candidate = model_id.strip() if isinstance(model_id, str) else ""
|
|
if not candidate or candidate in FAL_MODELS:
|
|
return FAL
|
|
if candidate in KREA_MODEL_IDS:
|
|
return KREA
|
|
return PORTAL
|
|
|
|
|
|
def _plugin_rows(name: str) -> list:
|
|
"""``list_models()`` of a registered image gen plugin; ``[]`` when unavailable."""
|
|
from tools.image_generation_tool import _get_plugin_provider
|
|
|
|
try:
|
|
provider = _get_plugin_provider(name)
|
|
return list(provider.list_models() or []) if provider is not None else []
|
|
except Exception: # noqa: BLE001 - a broken plugin must not empty the whole picker
|
|
return []
|
|
|
|
|
|
def managed_image_catalog(
|
|
*, include_krea: bool = True, include_portal: bool = True,
|
|
) -> Tuple[Dict[str, Dict[str, Any]], str]:
|
|
"""``({model_id: metadata}, default_model)`` for the managed row's model picker.
|
|
|
|
FAL catalog first (minus the Krea-on-FAL entries), then native Krea, then Portal models
|
|
that are not another entry in disguise. Every row carries ``backend`` so callers can hide
|
|
the gateways an account is not entitled to.
|
|
"""
|
|
from tools.image_generation_catalog import DEFAULT_MODEL, FAL_MODELS
|
|
|
|
catalog: Dict[str, Dict[str, Any]] = {
|
|
mid: {**meta, "backend": FAL} for mid, meta in FAL_MODELS.items()
|
|
if not mid.startswith(_FAL_KREA_PREFIX)}
|
|
if include_krea:
|
|
for row in _plugin_rows("krea"):
|
|
catalog[row["id"]] = {**row, "backend": KREA}
|
|
if include_portal:
|
|
for row in _plugin_rows("nous"):
|
|
mid = row["id"]
|
|
if mid in catalog or _PORTAL_DUPLICATE_OF.get(mid) in catalog:
|
|
continue
|
|
catalog[mid] = {**row, "backend": PORTAL}
|
|
return catalog, DEFAULT_MODEL
|