Spaces:
Running
Running
File size: 6,288 Bytes
6303ae6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | """Static reference list of supported LLM providers and common model names.
This module is a *reference / helper* layer, **not** an enforcement layer. It
maps each provider the app supports (see :data:`app.config.PROVIDER_OPTIONS`)
to a curated set of plausible, commonly used model names — split into text and
vision-capable models — plus a short human-readable note per provider.
Why this exists
---------------
The Setup screen already lets operators pick any provider and type any model
name, and the runtime routes calls based on the resolved ``Settings`` object
(see ``app.config`` and ``app.services.llm_client``). **Nothing here changes
that flow.** This list is intended for *future* use by:
* the Setup dropdowns (offer suggested model names instead of a free-text box),
* documentation (the README "Supported Provider Model Reference" section), and
* optional, non-blocking validation / hints.
Suggestions, not rules
----------------------
These model names are *suggestions*. A model that is not listed here is **not**
rejected — if the operator's provider account supports it, the app will still
use it. Strict validation (hard-blocking unknown models) is intentionally not
implemented; a caller that wants it can layer it on top of the
``is_supported_*`` helpers below.
Vision support
--------------
``vision_models`` lists models that can accept image input (used to read job
screenshots). Groq is treated as text-only in this app — its ``vision_models``
list is empty — mirroring ``app.config._SUPPORTED_VISION_PROVIDERS``.
"""
from __future__ import annotations
# Curated, commonly-used model names per provider. Reference data only — see
# the module docstring. Keys are the lowercase provider identifiers used
# everywhere else in the app (matching ``app.config.PROVIDER_OPTIONS``).
SUPPORTED_PROVIDER_MODELS: dict[str, dict[str, object]] = {
"openai": {
"text_models": [
"gpt-4.1",
"gpt-4.1-mini",
"gpt-4o",
"gpt-4o-mini",
],
"vision_models": [
"gpt-4o",
"gpt-4.1",
"gpt-4o-mini",
],
"notes": (
"OpenAI offers both text and vision-capable models. The 4o and "
"4.1 families accept image input and can read job screenshots."
),
},
"anthropic": {
"text_models": [
"claude-sonnet-4-6",
"claude-3-5-sonnet-latest",
"claude-3-5-haiku-latest",
],
"vision_models": [
"claude-sonnet-4-6",
"claude-3-5-sonnet-latest",
],
"notes": (
"Anthropic Claude models support both text and vision. "
"Sonnet-class models are recommended for screenshot extraction."
),
},
"groq": {
"text_models": [
"llama-3.3-70b-versatile",
"llama-3.1-8b-instant",
"mixtral-8x7b-32768",
"gemma2-9b-it",
],
"vision_models": [],
"notes": (
"Groq is treated as text-only in this app unless vision support "
"is added later."
),
},
"gemini": {
"text_models": [
"gemini-1.5-pro",
"gemini-1.5-flash",
"gemini-2.0-flash",
"gemini-2.0-flash-lite",
],
"vision_models": [
"gemini-1.5-pro",
"gemini-1.5-flash",
"gemini-2.0-flash",
],
"notes": (
"Gemini models are multimodal and support both text and vision "
"(image) input."
),
},
}
def _normalize_provider(provider: str | None) -> str:
"""Lowercase, whitespace-trimmed provider key (``""`` when not a string)."""
if not isinstance(provider, str):
return ""
return provider.strip().lower()
def _normalize_model(model: str | None) -> str:
"""Whitespace-trimmed model name (``""`` when not a string)."""
if not isinstance(model, str):
return ""
return model.strip()
def get_supported_providers() -> list[str]:
"""Return the provider identifiers in the reference list, in display order."""
return list(SUPPORTED_PROVIDER_MODELS.keys())
def is_supported_provider(provider: str) -> bool:
"""True when ``provider`` (case-insensitive) is in the reference list."""
return _normalize_provider(provider) in SUPPORTED_PROVIDER_MODELS
def get_text_models(provider: str) -> list[str]:
"""Suggested text model names for ``provider`` (``[]`` when unknown).
Returns a fresh copy so callers cannot mutate the reference data.
"""
entry = SUPPORTED_PROVIDER_MODELS.get(_normalize_provider(provider))
if not entry:
return []
return list(entry.get("text_models", []))
def get_vision_models(provider: str) -> list[str]:
"""Suggested vision model names for ``provider``.
Returns ``[]`` when the provider is unknown *or* text-only (e.g. Groq).
Returns a fresh copy so callers cannot mutate the reference data.
"""
entry = SUPPORTED_PROVIDER_MODELS.get(_normalize_provider(provider))
if not entry:
return []
return list(entry.get("vision_models", []))
def provider_supports_vision(provider: str) -> bool:
"""True when ``provider`` has at least one suggested vision model."""
return bool(get_vision_models(provider))
def is_supported_text_model(provider: str, model: str) -> bool:
"""True when ``model`` is a suggested *text* model for ``provider``.
Matching is case-insensitive and whitespace-tolerant. This is a reference
check only — a ``False`` result does **not** mean the model is unusable,
only that it is not in the curated suggestion list.
"""
target = _normalize_model(model).lower()
if not target:
return False
return any(target == m.lower() for m in get_text_models(provider))
def is_supported_vision_model(provider: str, model: str) -> bool:
"""True when ``model`` is a suggested *vision* model for ``provider``.
Same matching and "reference only" semantics as
:func:`is_supported_text_model`.
"""
target = _normalize_model(model).lower()
if not target:
return False
return any(target == m.lower() for m in get_vision_models(provider))
|