feat(telegram): paginated model picker with Next/Prev navigation
- Raise max_models from 8 to 50 so all curated models come through - Add _build_model_keyboard() helper with 8-per-page pagination - Next ▶ / ◀ Prev buttons with page counter (e.g. 2/4) - mg:<page> callback data for page navigation - Catch-all query.answer() for noop buttons
This commit is contained in:
@@ -1083,10 +1083,53 @@ class TelegramAdapter(BasePlatformAdapter):
|
|||||||
logger.warning("[%s] send_model_picker failed: %s", self.name, e)
|
logger.warning("[%s] send_model_picker failed: %s", self.name, e)
|
||||||
return SendResult(success=False, error=str(e))
|
return SendResult(success=False, error=str(e))
|
||||||
|
|
||||||
|
_MODEL_PAGE_SIZE = 8
|
||||||
|
|
||||||
|
def _build_model_keyboard(self, models: list, page: int) -> tuple:
|
||||||
|
"""Build paginated model buttons. Returns (keyboard, page_info_text)."""
|
||||||
|
page_size = self._MODEL_PAGE_SIZE
|
||||||
|
total = len(models)
|
||||||
|
total_pages = max(1, (total + page_size - 1) // page_size)
|
||||||
|
page = max(0, min(page, total_pages - 1))
|
||||||
|
|
||||||
|
start = page * page_size
|
||||||
|
end = min(start + page_size, total)
|
||||||
|
page_models = models[start:end]
|
||||||
|
|
||||||
|
buttons: list = []
|
||||||
|
for i, model_id in enumerate(page_models):
|
||||||
|
abs_idx = start + i
|
||||||
|
short = model_id.split("/")[-1] if "/" in model_id else model_id
|
||||||
|
if len(short) > 38:
|
||||||
|
short = short[:35] + "..."
|
||||||
|
buttons.append(
|
||||||
|
InlineKeyboardButton(short, callback_data=f"mm:{abs_idx}")
|
||||||
|
)
|
||||||
|
|
||||||
|
rows = [buttons[i : i + 2] for i in range(0, len(buttons), 2)]
|
||||||
|
|
||||||
|
# Pagination row (if needed)
|
||||||
|
if total_pages > 1:
|
||||||
|
nav: list = []
|
||||||
|
if page > 0:
|
||||||
|
nav.append(InlineKeyboardButton("◀ Prev", callback_data=f"mg:{page - 1}"))
|
||||||
|
nav.append(InlineKeyboardButton(f"{page + 1}/{total_pages}", callback_data="mx:noop"))
|
||||||
|
if page < total_pages - 1:
|
||||||
|
nav.append(InlineKeyboardButton("Next ▶", callback_data=f"mg:{page + 1}"))
|
||||||
|
rows.append(nav)
|
||||||
|
|
||||||
|
rows.append([
|
||||||
|
InlineKeyboardButton("◀ Back", callback_data="mb"),
|
||||||
|
InlineKeyboardButton("✗ Cancel", callback_data="mx"),
|
||||||
|
])
|
||||||
|
|
||||||
|
page_info = f" ({start + 1}–{end} of {total})" if total_pages > 1 else ""
|
||||||
|
return InlineKeyboardMarkup(rows), page_info
|
||||||
|
|
||||||
async def _handle_model_picker_callback(
|
async def _handle_model_picker_callback(
|
||||||
self, query, data: str, chat_id: str
|
self, query, data: str, chat_id: str
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Handle model picker inline keyboard callbacks (mp:/mm:/mb:/mx:)."""
|
"""Handle model picker inline keyboard callbacks (mp:/mm:/mb:/mx:/mg:)."""
|
||||||
state = self._model_picker_state.get(chat_id)
|
state = self._model_picker_state.get(chat_id)
|
||||||
if not state:
|
if not state:
|
||||||
await query.answer(text="Picker expired — use /model again.")
|
await query.answer(text="Picker expired — use /model again.")
|
||||||
@@ -1099,7 +1142,7 @@ class TelegramAdapter(BasePlatformAdapter):
|
|||||||
return slug
|
return slug
|
||||||
|
|
||||||
if data.startswith("mp:"):
|
if data.startswith("mp:"):
|
||||||
# --- Provider selected: show model buttons ---
|
# --- Provider selected: show model buttons (page 0) ---
|
||||||
provider_slug = data[3:]
|
provider_slug = data[3:]
|
||||||
provider = next(
|
provider = next(
|
||||||
(p for p in state["providers"] if p["slug"] == provider_slug),
|
(p for p in state["providers"] if p["slug"] == provider_slug),
|
||||||
@@ -1113,24 +1156,9 @@ class TelegramAdapter(BasePlatformAdapter):
|
|||||||
state["selected_provider"] = provider_slug
|
state["selected_provider"] = provider_slug
|
||||||
state["selected_provider_name"] = provider.get("name", provider_slug)
|
state["selected_provider_name"] = provider.get("name", provider_slug)
|
||||||
state["model_list"] = models
|
state["model_list"] = models
|
||||||
|
state["model_page"] = 0
|
||||||
|
|
||||||
buttons: list = []
|
keyboard, page_info = self._build_model_keyboard(models, 0)
|
||||||
for i, model_id in enumerate(models):
|
|
||||||
# Short display label: strip vendor prefix
|
|
||||||
short = model_id.split("/")[-1] if "/" in model_id else model_id
|
|
||||||
# Truncate long model names for button label (max ~40 chars)
|
|
||||||
if len(short) > 38:
|
|
||||||
short = short[:35] + "..."
|
|
||||||
buttons.append(
|
|
||||||
InlineKeyboardButton(short, callback_data=f"mm:{i}")
|
|
||||||
)
|
|
||||||
|
|
||||||
rows = [buttons[i : i + 2] for i in range(0, len(buttons), 2)]
|
|
||||||
rows.append([
|
|
||||||
InlineKeyboardButton("◀ Back", callback_data="mb"),
|
|
||||||
InlineKeyboardButton("✗ Cancel", callback_data="mx"),
|
|
||||||
])
|
|
||||||
keyboard = InlineKeyboardMarkup(rows)
|
|
||||||
|
|
||||||
pname = provider.get("name", provider_slug)
|
pname = provider.get("name", provider_slug)
|
||||||
total = provider.get("total_models", len(models))
|
total = provider.get("total_models", len(models))
|
||||||
@@ -1140,7 +1168,41 @@ class TelegramAdapter(BasePlatformAdapter):
|
|||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
text=(
|
text=(
|
||||||
f"⚙ *Model Configuration*\n\n"
|
f"⚙ *Model Configuration*\n\n"
|
||||||
f"Provider: *{pname}*\n"
|
f"Provider: *{pname}*{page_info}\n"
|
||||||
|
f"Select a model:{extra}"
|
||||||
|
),
|
||||||
|
parse_mode=ParseMode.MARKDOWN,
|
||||||
|
reply_markup=keyboard,
|
||||||
|
)
|
||||||
|
await query.answer()
|
||||||
|
|
||||||
|
elif data.startswith("mg:"):
|
||||||
|
# --- Page navigation ---
|
||||||
|
try:
|
||||||
|
page = int(data[3:])
|
||||||
|
except ValueError:
|
||||||
|
await query.answer(text="Invalid page.")
|
||||||
|
return
|
||||||
|
|
||||||
|
models = state.get("model_list", [])
|
||||||
|
state["model_page"] = page
|
||||||
|
|
||||||
|
keyboard, page_info = self._build_model_keyboard(models, page)
|
||||||
|
|
||||||
|
pname = state.get("selected_provider_name", "")
|
||||||
|
provider_slug = state.get("selected_provider", "")
|
||||||
|
provider = next(
|
||||||
|
(p for p in state["providers"] if p["slug"] == provider_slug),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
total = provider.get("total_models", len(models)) if provider else len(models)
|
||||||
|
shown = len(models)
|
||||||
|
extra = f"\n_{total - shown} more available — type `/model <name>` directly_" if total > shown else ""
|
||||||
|
|
||||||
|
await query.edit_message_text(
|
||||||
|
text=(
|
||||||
|
f"⚙ *Model Configuration*\n\n"
|
||||||
|
f"Provider: *{pname}*{page_info}\n"
|
||||||
f"Select a model:{extra}"
|
f"Select a model:{extra}"
|
||||||
),
|
),
|
||||||
parse_mode=ParseMode.MARKDOWN,
|
parse_mode=ParseMode.MARKDOWN,
|
||||||
@@ -1239,6 +1301,10 @@ class TelegramAdapter(BasePlatformAdapter):
|
|||||||
)
|
)
|
||||||
await query.answer()
|
await query.answer()
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Catch-all (e.g. page counter button "mx:noop")
|
||||||
|
await query.answer()
|
||||||
|
|
||||||
async def _handle_callback_query(
|
async def _handle_callback_query(
|
||||||
self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"
|
self, update: "Update", context: "ContextTypes.DEFAULT_TYPE"
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -1249,7 +1315,7 @@ class TelegramAdapter(BasePlatformAdapter):
|
|||||||
data = query.data
|
data = query.data
|
||||||
|
|
||||||
# --- Model picker callbacks ---
|
# --- Model picker callbacks ---
|
||||||
if data.startswith(("mp:", "mm:", "mb", "mx")):
|
if data.startswith(("mp:", "mm:", "mb", "mx", "mg:")):
|
||||||
chat_id = str(query.message.chat_id) if query.message else None
|
chat_id = str(query.message.chat_id) if query.message else None
|
||||||
if chat_id:
|
if chat_id:
|
||||||
await self._handle_model_picker_callback(query, data, chat_id)
|
await self._handle_model_picker_callback(query, data, chat_id)
|
||||||
|
|||||||
@@ -3530,7 +3530,7 @@ class GatewayRunner:
|
|||||||
providers = list_authenticated_providers(
|
providers = list_authenticated_providers(
|
||||||
current_provider=current_provider,
|
current_provider=current_provider,
|
||||||
user_providers=user_provs,
|
user_providers=user_provs,
|
||||||
max_models=8,
|
max_models=50,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
providers = []
|
providers = []
|
||||||
|
|||||||
Reference in New Issue
Block a user