Spaces:
Running
Running
Upload bot.py
Browse files
bot.py
CHANGED
|
@@ -528,7 +528,11 @@ GEMINI_MODELS: dict[str, dict[str, Any]] = {
|
|
| 528 |
"gemma-4-31b-it": {
|
| 529 |
"name": "Gemma 4 31B", "badge": "Открытая модель",
|
| 530 |
"desc": "Флагманская открытая модель Google на 31 млрд параметров — топ-3 среди открытых моделей мира по качеству рассуждений, кода и работы с изображениями.", "no_system": True, "no_search": True, "stream": True,
|
| 531 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 532 |
}
|
| 533 |
DEFAULT_GEMINI_MODEL = "gemini-3.5-flash"
|
| 534 |
|
|
@@ -849,35 +853,38 @@ HF_IMAGE_MODEL_PAGE_SIZE = 8
|
|
| 849 |
HF_IMAGE_MODEL_CALLBACKS: dict[str, str] = {}
|
| 850 |
|
| 851 |
HF_IMAGE_MODELS: dict[str, dict[str, Any]] = {
|
| 852 |
-
"
|
| 853 |
-
"name": "FLUX
|
| 854 |
-
"desc": "
|
| 855 |
},
|
| 856 |
-
"
|
| 857 |
-
"name": "FLUX
|
| 858 |
-
"desc": "
|
| 859 |
},
|
| 860 |
-
"
|
| 861 |
-
"name": "
|
| 862 |
-
"desc": "
|
| 863 |
},
|
| 864 |
-
"
|
| 865 |
-
"name": "
|
| 866 |
-
"desc": "
|
| 867 |
},
|
| 868 |
-
"
|
| 869 |
-
"name": "
|
| 870 |
-
"desc": "
|
| 871 |
},
|
| 872 |
-
"
|
| 873 |
-
"name": "
|
| 874 |
-
"desc": "
|
| 875 |
},
|
| 876 |
}
|
| 877 |
|
| 878 |
|
| 879 |
def _hf_image_headers() -> dict[str, str]:
|
| 880 |
-
headers = {
|
|
|
|
|
|
|
|
|
|
| 881 |
if HF_TOKEN:
|
| 882 |
headers["Authorization"] = f"Bearer {HF_TOKEN}"
|
| 883 |
return headers
|
|
@@ -894,9 +901,7 @@ async def _hf_fetch_model_catalog(force: bool = False) -> list[dict[str, Any]]:
|
|
| 894 |
|
| 895 |
def _imgmodel_button_text(model: dict[str, Any], current: str) -> str:
|
| 896 |
prefix = "• " if model["id"] == current else ""
|
| 897 |
-
|
| 898 |
-
suffix = f" [{badge}]" if badge else ""
|
| 899 |
-
return f"{prefix}{model['name']}{suffix}"
|
| 900 |
|
| 901 |
|
| 902 |
def _imgmodel_keyboard(current: str, page: int = 0) -> InlineKeyboardMarkup:
|
|
@@ -925,7 +930,31 @@ def _imgmodel_keyboard(current: str, page: int = 0) -> InlineKeyboardMarkup:
|
|
| 925 |
return InlineKeyboardMarkup(inline_keyboard=rows)
|
| 926 |
|
| 927 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 928 |
async def _hf_text_to_image(model_id: str, prompt: str) -> bytes:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 929 |
session = await _get_http_session()
|
| 930 |
url = f"{HF_IMAGE_API_BASE}/models/{urllib.parse.quote(model_id, safe='')}"
|
| 931 |
payload = {
|
|
@@ -1889,6 +1918,7 @@ async def ask_gemini(chat_id: int, user_text: str, media: list[tuple[bytes, str]
|
|
| 1889 |
"gemini-2.5-flash",
|
| 1890 |
"gemini-2.5-flash-lite",
|
| 1891 |
"gemma-4-31b-it",
|
|
|
|
| 1892 |
]
|
| 1893 |
|
| 1894 |
loop_guard = 0
|
|
@@ -2215,13 +2245,18 @@ def gemini_kb(current: str) -> InlineKeyboardMarkup:
|
|
| 2215 |
return InlineKeyboardMarkup(inline_keyboard=grid)
|
| 2216 |
|
| 2217 |
def _get_gemini_model_limit_info(model_id: str) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2218 |
limits_map = {
|
| 2219 |
-
"gemini-3.5-flash":
|
| 2220 |
-
"gemini-3-flash-preview":
|
| 2221 |
"gemini-3.1-flash-lite": 500,
|
| 2222 |
-
"gemini-2.5-flash":
|
| 2223 |
-
"gemini-2.5-flash-lite":
|
| 2224 |
"gemma-4-31b-it": 1500,
|
|
|
|
| 2225 |
}
|
| 2226 |
limit = limits_map.get(model_id, 20)
|
| 2227 |
e = _quota_entry(None, "gemini", model_id)
|
|
|
|
| 528 |
"gemma-4-31b-it": {
|
| 529 |
"name": "Gemma 4 31B", "badge": "Открытая модель",
|
| 530 |
"desc": "Флагманская открытая модель Google на 31 млрд параметров — топ-3 среди открытых моделей мира по качеству рассуждений, кода и работы с изображениями.", "no_system": True, "no_search": True, "stream": True,
|
| 531 |
+
},
|
| 532 |
+
"gemma-4-26b-a4b-it": {
|
| 533 |
+
"name": "Gemma 4 26B", "badge": "Открытая модель",
|
| 534 |
+
"desc": "Компактная открытая модель Google на 26 млрд параметров с поддержкой расширенного мышления (thinking). Хороший баланс качества и скорости среди Gemma-семейства.", "no_system": True, "stream": True,
|
| 535 |
+
},
|
| 536 |
}
|
| 537 |
DEFAULT_GEMINI_MODEL = "gemini-3.5-flash"
|
| 538 |
|
|
|
|
| 853 |
HF_IMAGE_MODEL_CALLBACKS: dict[str, str] = {}
|
| 854 |
|
| 855 |
HF_IMAGE_MODELS: dict[str, dict[str, Any]] = {
|
| 856 |
+
"pollinations:flux": {
|
| 857 |
+
"name": "FLUX Pro",
|
| 858 |
+
"desc": "Высококачественный FLUX через Pollinations.ai. Фотореализм, точное следование промпту, богатая детализация.",
|
| 859 |
},
|
| 860 |
+
"pollinations:flux-realism": {
|
| 861 |
+
"name": "FLUX Realism",
|
| 862 |
+
"desc": "FLUX с акцентом на гиперреализм — детализированные текстуры, естественное освещение, кинематографичность.",
|
| 863 |
},
|
| 864 |
+
"pollinations:flux-anime": {
|
| 865 |
+
"name": "FLUX Anime",
|
| 866 |
+
"desc": "FLUX для аниме и иллюстраций — характерные пропорции, яркие цвета, стилизация под японскую графику.",
|
| 867 |
},
|
| 868 |
+
"pollinations:turbo": {
|
| 869 |
+
"name": "Turbo",
|
| 870 |
+
"desc": "Быстрая дистиллированная модель. Результат за несколько секунд — для черновиков и быстрых итераций.",
|
| 871 |
},
|
| 872 |
+
"pollinations:dreamshaper": {
|
| 873 |
+
"name": "DreamShaper",
|
| 874 |
+
"desc": "Художественная модель для фэнтези, концепт-арта и стилизованных иллюстраций.",
|
| 875 |
},
|
| 876 |
+
"black-forest-labs/FLUX.1-schnell": {
|
| 877 |
+
"name": "FLUX.1 Schnell (HF)",
|
| 878 |
+
"desc": "Оригинальная FLUX.1 Schnell от Black Forest Labs через Hugging Face Inference API. Быстрая, Apache 2.0.",
|
| 879 |
},
|
| 880 |
}
|
| 881 |
|
| 882 |
|
| 883 |
def _hf_image_headers() -> dict[str, str]:
|
| 884 |
+
headers = {
|
| 885 |
+
"Accept": "image/png, image/jpeg, image/webp, application/json",
|
| 886 |
+
"User-Agent": "IlvorBot/8.2"
|
| 887 |
+
}
|
| 888 |
if HF_TOKEN:
|
| 889 |
headers["Authorization"] = f"Bearer {HF_TOKEN}"
|
| 890 |
return headers
|
|
|
|
| 901 |
|
| 902 |
def _imgmodel_button_text(model: dict[str, Any], current: str) -> str:
|
| 903 |
prefix = "• " if model["id"] == current else ""
|
| 904 |
+
return f"{prefix}{model['name']}"
|
|
|
|
|
|
|
| 905 |
|
| 906 |
|
| 907 |
def _imgmodel_keyboard(current: str, page: int = 0) -> InlineKeyboardMarkup:
|
|
|
|
| 930 |
return InlineKeyboardMarkup(inline_keyboard=rows)
|
| 931 |
|
| 932 |
|
| 933 |
+
async def _pollinations_generate(model_name: str, prompt: str) -> bytes:
|
| 934 |
+
"""Бесплатная генерация через Pollinations.ai — не требует авторизации."""
|
| 935 |
+
session = await _get_http_session()
|
| 936 |
+
encoded = urllib.parse.quote(prompt[:600], safe="")
|
| 937 |
+
url = (
|
| 938 |
+
f"https://image.pollinations.ai/prompt/{encoded}"
|
| 939 |
+
f"?width=1024&height=1024&model={model_name}&nologo=true&enhance=false"
|
| 940 |
+
)
|
| 941 |
+
async with session.get(url, timeout=aiohttp.ClientTimeout(total=90)) as resp:
|
| 942 |
+
if resp.status == 200:
|
| 943 |
+
ctype = (resp.headers.get("Content-Type") or "").lower()
|
| 944 |
+
body = await resp.read()
|
| 945 |
+
if body and (ctype.startswith("image/") or body[:4] in (b"\x89PNG", b"\xff\xd8\xff", b"RIFF", b"GIF8")):
|
| 946 |
+
return body
|
| 947 |
+
raise RuntimeError(f"Pollinations вернул не-изображение: {ctype}")
|
| 948 |
+
raise RuntimeError(f"Pollinations.ai HTTP {resp.status}")
|
| 949 |
+
|
| 950 |
+
|
| 951 |
async def _hf_text_to_image(model_id: str, prompt: str) -> bytes:
|
| 952 |
+
# Pollinations.ai — бесплатный провайдер без авторизации
|
| 953 |
+
if model_id.startswith("pollinations:"):
|
| 954 |
+
poll_model = model_id.split(":", 1)[1]
|
| 955 |
+
return await _pollinations_generate(poll_model, prompt)
|
| 956 |
+
|
| 957 |
+
# HF Inference API (router.huggingface.co/hf-inference)
|
| 958 |
session = await _get_http_session()
|
| 959 |
url = f"{HF_IMAGE_API_BASE}/models/{urllib.parse.quote(model_id, safe='')}"
|
| 960 |
payload = {
|
|
|
|
| 1918 |
"gemini-2.5-flash",
|
| 1919 |
"gemini-2.5-flash-lite",
|
| 1920 |
"gemma-4-31b-it",
|
| 1921 |
+
"gemma-4-26b-a4b-it",
|
| 1922 |
]
|
| 1923 |
|
| 1924 |
loop_guard = 0
|
|
|
|
| 2245 |
return InlineKeyboardMarkup(inline_keyboard=grid)
|
| 2246 |
|
| 2247 |
def _get_gemini_model_limit_info(model_id: str) -> str:
|
| 2248 |
+
# Реальные лимиты по официальному дашборду AI Studio (June 2026).
|
| 2249 |
+
# Строка вида "32 / 20" означает: пик использования / лимит.
|
| 2250 |
+
# 3.x Flash/Preview = 20-25 RPD; 3.1 Flash-Lite = 500 RPD;
|
| 2251 |
+
# 2.5 Flash = 20 RPD; 2.5 Flash-Lite = 20 RPD; Gemma 4 = 1500 RPD.
|
| 2252 |
limits_map = {
|
| 2253 |
+
"gemini-3.5-flash": 20,
|
| 2254 |
+
"gemini-3-flash-preview": 25,
|
| 2255 |
"gemini-3.1-flash-lite": 500,
|
| 2256 |
+
"gemini-2.5-flash": 20,
|
| 2257 |
+
"gemini-2.5-flash-lite": 20,
|
| 2258 |
"gemma-4-31b-it": 1500,
|
| 2259 |
+
"gemma-4-26b-a4b-it": 1500,
|
| 2260 |
}
|
| 2261 |
limit = limits_map.get(model_id, 20)
|
| 2262 |
e = _quota_entry(None, "gemini", model_id)
|