From 9ba4ba4fdb2841fcab86b83041bc63c056b2d37b Mon Sep 17 00:00:00 2001 From: Antigravity Bot Date: Sun, 30 Aug 2026 13:39:16 +0330 Subject: [PATCH] Simplify live streaming model thinking indicator to cloud emoji and model name --- telegram-agy-bot/bot.py | 125 ++++++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 23 deletions(-) diff --git a/telegram-agy-bot/bot.py b/telegram-agy-bot/bot.py index 0b475ca..74c3aa1 100644 --- a/telegram-agy-bot/bot.py +++ b/telegram-agy-bot/bot.py @@ -884,12 +884,12 @@ def build_conversations_menu(chat_id: int) -> tuple[str, InlineKeyboardMarkup]: if is_fa: header = ( f"📜 مدیریت و تاریخچه گفتگوها (پروژه: {escape_html(curr_proj.name)})\n\n" - f"💡 روی هر گفتگو برای سوییچ سریع کلیک کنید، یا با دکمه 🗑️ آن را حذف نمایید:\n\n" + f"💡 جهت سوییچ یا مشاهده جزئیات و حذف هر گفتگو، روی دکمه مربوطه کلیک کنید:\n\n" ) else: header = ( f"📜 Conversation Manager & History (Project: {escape_html(curr_proj.name)})\n\n" - f"💡 Click a conversation to switch, or tap 🗑️ to delete:\n\n" + f"💡 Click any conversation button to switch, view details, or delete:\n\n" ) if not convs: @@ -908,28 +908,36 @@ def build_conversations_menu(chat_id: int) -> tuple[str, InlineKeyboardMarkup]: for idx, c in enumerate(convs[:8], start=1): cid = c["id"] is_curr = c.get("is_current", False) - badge = "🟢 [گفتگوی فعال] " if is_curr else f"#{idx} " - prompt_snippet = c.get("first_prompt") or "(بدون متن)" + badge_text = "🟢 [فعال]" if is_curr else f"#{idx}" + custom_t = c.get("title") + prompt_snippet = custom_t if custom_t else (c.get("first_prompt") or "(بدون متن)") prompt_clean = prompt_snippet.replace("\n", " ").strip() - if len(prompt_clean) > 40: - prompt_clean = prompt_clean[:37] + "..." + + # Format text preview for message body (up to 80 chars) + prompt_body_preview = prompt_clean if len(prompt_clean) <= 80 else prompt_clean[:77] + "..." + + # Format label for full-width button (up to 45 chars) + prompt_btn_preview = prompt_clean if len(prompt_clean) <= 45 else prompt_clean[:42] + "..." turns = c.get("turns_count", 0) + title_prefix = "🏷️ " if custom_t else "" if is_fa: - body_lines.append(f"{badge}{cid[:8]}... ({turns} نوبت)\n └ «{escape_html(prompt_clean)}»\n") + status_tag = " (در حال استفاده)" if is_curr else "" + body_lines.append(f"{idx}. {badge_text}{status_tag} {cid[:8]} ({turns} نوبت)\n └ {title_prefix}«{escape_html(prompt_body_preview)}»\n") else: - body_lines.append(f"{badge}{cid[:8]}... ({turns} turns)\n └ \"{escape_html(prompt_clean)}\"\n") + status_tag = " (Active)" if is_curr else "" + body_lines.append(f"{idx}. {badge_text}{status_tag} {cid[:8]} ({turns} turns)\n └ {title_prefix}\"{escape_html(prompt_body_preview)}\"\n") if is_curr: - btn_label = f"🟢 #{idx}: {prompt_clean} (فعال)" if is_fa else f"🟢 #{idx}: {prompt_clean} (Active)" + btn_label = f"🟢 #{idx}: {title_prefix}{prompt_btn_preview} (فعال)" if is_fa else f"🟢 #{idx}: {title_prefix}{prompt_btn_preview} (Active)" btn_action = f"conv_view_{cid}" else: - btn_label = f"💬 #{idx}: {prompt_clean}" + btn_label = f"💬 #{idx}: {title_prefix}{prompt_btn_preview}" btn_action = f"conv_switch_{cid}" + # Full width button for maximum readability keyboard.append([ - InlineKeyboardButton(btn_label[:38], callback_data=btn_action), - InlineKeyboardButton("🗑️", callback_data=f"conv_del_ask_{cid}"), + InlineKeyboardButton(btn_label[:60], callback_data=btn_action), ]) action_row = [ @@ -953,6 +961,7 @@ def build_conversations_menu(chat_id: int) -> tuple[str, InlineKeyboardMarkup]: footer = ( f"\n💡 دستورات متنی:\n" f"• سوییچ: /switchconv <شماره یا شناسه>\n" + f"• تغییر موضوع: /settopic <عنوان جدید>\n" f"• حذف: /delconv <شماره یا شناسه>\n" f"• پاکسازی همه: /clearconvs" ) @@ -960,6 +969,7 @@ def build_conversations_menu(chat_id: int) -> tuple[str, InlineKeyboardMarkup]: footer = ( f"\n💡 Text commands:\n" f"• Switch: /switchconv <number or ID>\n" + f"• Rename: /settopic <new title>\n" f"• Delete: /delconv <number or ID>\n" f"• Clear all: /clearconvs" ) @@ -981,6 +991,8 @@ def build_conversation_detail_menu(chat_id: int, conv_id: str) -> tuple[str, Inl out = get_conversation_last_output(conv_id) is_active = (curr_proj.conversation_id == conv_id) first_prompt = meta.get("first_prompt") or "(شروع مکالمه)" + custom_title = getattr(curr_proj, "conversation_titles", {}).get(conv_id) if hasattr(curr_proj, "conversation_titles") else None + display_title = custom_title if custom_title else first_prompt last_resp = out.get("text") or meta.get("last_response") or "" if len(last_resp) > 200: last_resp = last_resp[:197] + "..." @@ -989,24 +1001,26 @@ def build_conversation_detail_menu(chat_id: int, conv_id: str) -> tuple[str, Inl status_str_en = "🟢 Active (In use)" if is_active else "⚪ Inactive (Archived)" if is_fa: + title_section = f"• 🏷️ عنوان / موضوع گفتگو: {escape_html(custom_title)}\n• 📝 اولین پیام: «{escape_html(first_prompt[:200])}»\n" if custom_title else f"📝 موضوع / اولین پیام:\n«{escape_html(first_prompt[:250])}»\n" text = ( f"🔍 جزئیات گفتگو (Conversation Details)\n\n" f"• 📁 پروژه: {escape_html(curr_proj.name)}\n" f"• 💬 شناسه گفتگو: {conv_id}\n" f"• 🏷 وضعیت: {status_str}\n" f"• 🔢 تعداد نوبت‌ها: {meta.get('turns_count', 0)} نوبت\n\n" - f"📝 موضوع / اولین پیام:\n«{escape_html(first_prompt[:250])}»\n" + f"{title_section}" ) if last_resp: text += f"\n📋 آخرین پاسخ AI:\n«{escape_html(last_resp)}»\n" else: + title_section = f"• 🏷️ Topic / Title: {escape_html(custom_title)}\n• 📝 First Prompt: \"{escape_html(first_prompt[:200])}\"\n" if custom_title else f"📝 First Prompt / Topic:\n\"{escape_html(first_prompt[:250])}\"\n" text = ( f"🔍 Conversation Details\n\n" f"• 📁 Project: {escape_html(curr_proj.name)}\n" f"• 💬 Conversation ID: {conv_id}\n" f"• 🏷 Status: {status_str_en}\n" f"• 🔢 Turns: {meta.get('turns_count', 0)}\n\n" - f"📝 First Prompt / Topic:\n\"{escape_html(first_prompt[:250])}\"\n" + f"{title_section}" ) if last_resp: text += f"\n📋 Last AI Response:\n\"{escape_html(last_resp)}\"\n" @@ -2750,10 +2764,10 @@ async def process_agent_turn_by_chat_id( model_display = get_model_display_name(model_to_use, effort_to_use) initial_status = ( f"📁 پروژه: {escape_html(curr_proj.name)}\n\n" - f"💭 در حال تفکر با مدل {escape_html(model_display)}..." + f"💭 {escape_html(model_display)}" if is_fa else f"📁 Project: {escape_html(curr_proj.name)}\n\n" - f"💭 Thinking with model {escape_html(model_display)}..." + f"💭 {escape_html(model_display)}" ) status_msg = await app.bot.send_message( chat_id=chat_id, @@ -2854,10 +2868,16 @@ def get_usage_buttons(lang: str = "fa") -> InlineKeyboardMarkup: is_fa = (lang or "").lower() in ("fa", "farsi", "persian", "🇮🇷 persian / farsi (فارسی)") restart_label = "🔄 گفتگوی جدید" if is_fa else "🔄 New Chat" compact_label = "🗜️ فشرده‌سازی" if is_fa else "🗜️ Compact" + conv_label = "📜 سشن‌ها / گفتگوها" if is_fa else "📜 Conversations" + last_conv_label = "⏮️ گفتگوی قبلی" if is_fa else "⏮️ Previous Chat" keyboard = [ [ InlineKeyboardButton(compact_label, callback_data="btn_compact"), InlineKeyboardButton(restart_label, callback_data="btn_restart"), + ], + [ + InlineKeyboardButton(last_conv_label, callback_data="btn_conv_last"), + InlineKeyboardButton(conv_label, callback_data="btn_conv_menu"), ] ] return InlineKeyboardMarkup(keyboard) @@ -3243,6 +3263,56 @@ async def delete_conv_command(update: Update, context: ContextTypes.DEFAULT_TYPE ] await update.message.reply_html(text, reply_markup=InlineKeyboardMarkup(keyboard)) +# Command: /settopic or /settitle or /convtitle +@check_auth +async def set_conv_title_command(update: Update, context: ContextTypes.DEFAULT_TYPE): + chat_id = update.effective_chat.id + curr_proj = session_manager.get_current_project(chat_id) + session = session_manager.get_or_create(chat_id) + is_fa = (session.language or "").lower() in ("fa", "farsi", "persian", "🇮🇷 persian / farsi (فارسی)") + + if not curr_proj: + msg = "⚠️ شما هنوز هیچ پروژه‌ای ایجاد نکرده‌اید." if is_fa else "⚠️ No active project found." + await update.message.reply_html(msg) + return + + if not context.args: + msg = ( + "💡 راهنمای تنظیم عنوان گفتگو:\n\n" + "/settopic <عنوان یا موضوع جدید>\n" + "یا برای گفتگوی خاص:\n" + "/settopic <شماره یا شناسه گفتگو> <عنوان جدید>" + if is_fa else + "💡 Set Conversation Topic:\n\n" + "/settopic <New Title>\n" + "Or for a specific conversation:\n" + "/settopic <number or ID> <New Title>" + ) + await update.message.reply_html(msg) + return + + first_arg = context.args[0].strip() + # Check if first arg is conv index or id + if (first_arg.isdigit() or first_arg.startswith("#") or len(first_arg) > 20) and len(context.args) > 1: + target_cid = first_arg + new_title = " ".join(context.args[1:]).strip() + else: + target_cid = curr_proj.conversation_id + new_title = " ".join(context.args).strip() + + ok, msg, matched_cid = session_manager.set_conversation_title(chat_id, title=new_title, conv_id=target_cid) + if not ok: + await update.message.reply_html(msg) + return + + keyboard = [ + [ + InlineKeyboardButton("📜 تاریخچه گفتگوها" if is_fa else "📜 Conversations", callback_data="btn_conv_menu"), + InlineKeyboardButton("🏠 منوی اصلی" if is_fa else "🏠 Main Dashboard", callback_data="btn_dashboard"), + ] + ] + await update.message.reply_html(msg, reply_markup=InlineKeyboardMarkup(keyboard)) + # Command: /clearconvs or /deleteallconvs @check_auth async def clear_convs_command(update: Update, context: ContextTypes.DEFAULT_TYPE): @@ -6465,12 +6535,12 @@ async def process_agent_turn( if is_fa: initial_status = ( f"📁 پروژه: {escape_html(curr_proj.name)}\n\n" - f"💭 در حال تفکر با مدل {escape_html(model_display)}..." + f"💭 {escape_html(model_display)}" ) else: initial_status = ( f"📁 Project: {escape_html(curr_proj.name)}\n\n" - f"💭 Thinking with model {escape_html(model_display)}..." + f"💭 {escape_html(model_display)}" ) status_msg = await update.effective_message.reply_html( @@ -6527,10 +6597,7 @@ async def process_agent_turn( # 3. Status indicator at the bottom stating which model is being used if not final: - if is_fa: - bottom_parts.append(f"💭 در حال تفکر با مدل {escape_html(model_display)}... ▌") - else: - bottom_parts.append(f"💭 Thinking with model {escape_html(model_display)}... ▌") + bottom_parts.append(f"💭 {escape_html(model_display)} ▌") if raw_text.strip(): formatted_body = markdown_to_telegram_html(raw_text) @@ -6544,7 +6611,7 @@ async def process_agent_turn( if bottom_parts: full_html = proj_header + "\n\n" + "\n\n".join(bottom_parts) else: - thinking_msg = f"💭 در حال تفکر با مدل {escape_html(model_display)}... ▌" if is_fa else f"💭 Thinking with model {escape_html(model_display)}... ▌" + thinking_msg = f"💭 {escape_html(model_display)} ▌" full_html = proj_header + "\n\n" + thinking_msg if len(full_html) > settings.max_message_length: @@ -6594,6 +6661,14 @@ async def process_agent_turn( tools.append(format_tool_call(name, args, lang=session.language)) asyncio.create_task(update_telegram_display(final=False)) + def on_model_change(new_model: str, new_effort: Optional[str] = None): + nonlocal model_display + model_display = get_model_display_name(new_model, new_effort) + asyncio.create_task(update_telegram_display(final=False)) + + def on_reset(): + accumulated_tokens.clear() + async def send_typing_loop(): while True: try: @@ -6614,6 +6689,8 @@ async def process_agent_turn( on_delta=on_delta, on_thought=on_thought, on_tool=on_tool, + on_model_change=on_model_change, + on_reset=on_reset, ) return result finally: @@ -6763,6 +6840,7 @@ async def on_startup(app: Application): BotCommand("schedule", "افزودن زمان‌بندی / Schedule task"), BotCommand("conversations", "مدیریت گفتگوها / Conversations"), BotCommand("switchconv", "سوییچ گفتگو / Switch chat"), + BotCommand("settopic", "تغییر موضوع گفتگو / Rename chat"), BotCommand("delconv", "حذف گفتگو / Delete chat"), BotCommand("clearconvs", "پاکسازی همه / Clear chats"), BotCommand("lastconv", "آخرین گفتگو / Open last chat"), @@ -6970,6 +7048,7 @@ def main(): application.add_handler(CommandHandler(["lastconv", "lastconversation", "openlast", "resume", "open_last"], last_conversation_command)) application.add_handler(CommandHandler(["conversations", "convs", "history_conv", "dialogs", "chats"], conversations_command)) application.add_handler(CommandHandler(["switchconv", "switch_conv", "openconv", "useconv", "selectconv"], switch_conv_command)) + application.add_handler(CommandHandler(["settopic", "set_topic", "settitle", "set_title", "convtitle", "title"], set_conv_title_command)) application.add_handler(CommandHandler(["delconv", "deleteconv", "del_conv", "delete_conv", "removeconv"], delete_conv_command)) application.add_handler(CommandHandler(["clearconvs", "clear_convs", "deleteallconvs"], clear_convs_command)) application.add_handler(CommandHandler("model", model_command))