refactor(webui): derive provider model catalog kind
This commit is contained in:
@@ -32,6 +32,7 @@ class ProviderSpec:
|
|||||||
keywords: tuple[str, ...] # model-name keywords for matching (lowercase)
|
keywords: tuple[str, ...] # model-name keywords for matching (lowercase)
|
||||||
env_key: str # env var for API key, e.g. "DASHSCOPE_API_KEY"
|
env_key: str # env var for API key, e.g. "DASHSCOPE_API_KEY"
|
||||||
display_name: str = "" # shown in `nanobot status`
|
display_name: str = "" # shown in `nanobot status`
|
||||||
|
model_catalog: str = "auto" # WebUI model-list source
|
||||||
|
|
||||||
# which provider implementation to use
|
# which provider implementation to use
|
||||||
# "openai_compat" | "anthropic" | "azure_openai" | "openai_codex" | "github_copilot" | "bedrock"
|
# "openai_compat" | "anthropic" | "azure_openai" | "openai_codex" | "github_copilot" | "bedrock"
|
||||||
@@ -221,6 +222,7 @@ PROVIDERS: tuple[ProviderSpec, ...] = (
|
|||||||
keywords=("skywork", "skyclaw", "apifree"),
|
keywords=("skywork", "skyclaw", "apifree"),
|
||||||
env_key="SKYWORK_API_KEY",
|
env_key="SKYWORK_API_KEY",
|
||||||
display_name="Skywork",
|
display_name="Skywork",
|
||||||
|
model_catalog="official",
|
||||||
backend="openai_compat",
|
backend="openai_compat",
|
||||||
env_extras=(("APIFREE_API_KEY", "{api_key}"),),
|
env_extras=(("APIFREE_API_KEY", "{api_key}"),),
|
||||||
is_gateway=True,
|
is_gateway=True,
|
||||||
|
|||||||
@@ -99,47 +99,6 @@ _CONTEXT_WINDOW_TOKEN_OPTIONS = {65_536, 200_000, 262_144}
|
|||||||
_MODEL_CONFIGURATION_SLUG_RE = re.compile(r"[^a-z0-9_-]+")
|
_MODEL_CONFIGURATION_SLUG_RE = re.compile(r"[^a-z0-9_-]+")
|
||||||
_ENV_REF_RE = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}")
|
_ENV_REF_RE = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}")
|
||||||
|
|
||||||
_MODEL_LIST_UNSUPPORTED_BACKENDS = {
|
|
||||||
"anthropic",
|
|
||||||
"azure_openai",
|
|
||||||
"bedrock",
|
|
||||||
"github_copilot",
|
|
||||||
"openai_codex",
|
|
||||||
}
|
|
||||||
|
|
||||||
_MODEL_LIST_CATALOG_PROVIDERS = {
|
|
||||||
"aihubmix",
|
|
||||||
"byteplus",
|
|
||||||
"byteplus_coding_plan",
|
|
||||||
"huggingface",
|
|
||||||
"novita",
|
|
||||||
"openrouter",
|
|
||||||
"siliconflow",
|
|
||||||
"volcengine",
|
|
||||||
"volcengine_coding_plan",
|
|
||||||
}
|
|
||||||
|
|
||||||
_MODEL_LIST_OFFICIAL_PROVIDERS = {
|
|
||||||
"ant_ling",
|
|
||||||
"dashscope",
|
|
||||||
"deepseek",
|
|
||||||
"gemini",
|
|
||||||
"groq",
|
|
||||||
"longcat",
|
|
||||||
"minimax",
|
|
||||||
"minimax_anthropic",
|
|
||||||
"mistral",
|
|
||||||
"moonshot",
|
|
||||||
"nvidia",
|
|
||||||
"openai",
|
|
||||||
"qianfan",
|
|
||||||
"skywork",
|
|
||||||
"stepfun",
|
|
||||||
"xiaomi_mimo",
|
|
||||||
"zhipu",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class WebUISettingsError(ValueError):
|
class WebUISettingsError(ValueError):
|
||||||
"""User-facing settings validation failure."""
|
"""User-facing settings validation failure."""
|
||||||
|
|
||||||
@@ -394,10 +353,13 @@ def _provider_settings_row(
|
|||||||
|
|
||||||
|
|
||||||
def _model_catalog_kind(spec: Any) -> str:
|
def _model_catalog_kind(spec: Any) -> str:
|
||||||
if spec.name in _MODEL_LIST_CATALOG_PROVIDERS:
|
catalog = getattr(spec, "model_catalog", "auto")
|
||||||
return "catalog"
|
if catalog != "auto":
|
||||||
if spec.name in _MODEL_LIST_OFFICIAL_PROVIDERS:
|
return catalog
|
||||||
return "official"
|
if spec.is_transcription_only or spec.is_oauth:
|
||||||
|
return "unsupported"
|
||||||
|
if spec.backend != "openai_compat" and spec.name != "minimax_anthropic":
|
||||||
|
return "unsupported"
|
||||||
if spec.is_local:
|
if spec.is_local:
|
||||||
return "local"
|
return "local"
|
||||||
if spec.is_direct:
|
if spec.is_direct:
|
||||||
@@ -490,27 +452,20 @@ def provider_models_payload(query: QueryParams) -> dict[str, Any]:
|
|||||||
raise WebUISettingsError("unknown provider")
|
raise WebUISettingsError("unknown provider")
|
||||||
spec, provider_key, provider_config = resolved_provider
|
spec, provider_key, provider_config = resolved_provider
|
||||||
|
|
||||||
|
catalog_kind = _model_catalog_kind(spec)
|
||||||
base_payload: dict[str, Any] = {
|
base_payload: dict[str, Any] = {
|
||||||
"provider": provider_key,
|
"provider": provider_key,
|
||||||
"label": spec.label,
|
"label": spec.label,
|
||||||
"catalog_kind": _model_catalog_kind(spec),
|
"catalog_kind": catalog_kind,
|
||||||
"models": [],
|
"models": [],
|
||||||
"model_count": 0,
|
"model_count": 0,
|
||||||
"message": None,
|
"message": None,
|
||||||
"fetched_at": time.time(),
|
"fetched_at": time.time(),
|
||||||
}
|
}
|
||||||
if (
|
if catalog_kind == "unsupported":
|
||||||
spec.is_transcription_only
|
|
||||||
or (
|
|
||||||
spec.backend in _MODEL_LIST_UNSUPPORTED_BACKENDS
|
|
||||||
and spec.name != "minimax_anthropic"
|
|
||||||
)
|
|
||||||
or spec.is_oauth
|
|
||||||
):
|
|
||||||
return {
|
return {
|
||||||
**base_payload,
|
**base_payload,
|
||||||
"status": "unsupported",
|
"status": "unsupported",
|
||||||
"catalog_kind": "unsupported",
|
|
||||||
"message": "Model list is not available for this provider. Type a model ID manually.",
|
"message": "Model list is not available for this provider. Type a model ID manually.",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from nanobot.config.schema import Config, ModelPresetConfig
|
|||||||
from nanobot.providers.registry import find_by_name
|
from nanobot.providers.registry import find_by_name
|
||||||
from nanobot.webui.settings_api import (
|
from nanobot.webui.settings_api import (
|
||||||
WebUISettingsError,
|
WebUISettingsError,
|
||||||
|
_model_catalog_kind,
|
||||||
_oauth_provider_status,
|
_oauth_provider_status,
|
||||||
create_model_configuration,
|
create_model_configuration,
|
||||||
login_oauth_provider,
|
login_oauth_provider,
|
||||||
@@ -998,9 +999,16 @@ def test_provider_models_payload_requires_gateway_key(
|
|||||||
payload = provider_models_payload({"provider": ["openrouter"]})
|
payload = provider_models_payload({"provider": ["openrouter"]})
|
||||||
|
|
||||||
assert payload["status"] == "not_configured"
|
assert payload["status"] == "not_configured"
|
||||||
|
assert payload["catalog_kind"] == "catalog"
|
||||||
assert payload["models"] == []
|
assert payload["models"] == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_model_catalog_kind_uses_provider_spec_metadata() -> None:
|
||||||
|
assert _model_catalog_kind(find_by_name("skywork")) == "official"
|
||||||
|
assert _model_catalog_kind(find_by_name("anthropic")) == "unsupported"
|
||||||
|
assert _model_catalog_kind(find_by_name("openrouter")) == "catalog"
|
||||||
|
|
||||||
|
|
||||||
def test_create_model_configuration_accepts_configured_oauth_provider(
|
def test_create_model_configuration_accepts_configured_oauth_provider(
|
||||||
tmp_path,
|
tmp_path,
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
|||||||
Reference in New Issue
Block a user