ux: show Thinking placeholder immediately, edit with status and final reply
This commit is contained in:
parent
3c6cafd62d
commit
9316a38699
1 changed files with 28 additions and 10 deletions
38
main.py
38
main.py
|
|
@ -10,6 +10,7 @@ import pathlib
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
|
import telegram
|
||||||
from telegram import BotCommand, Update
|
from telegram import BotCommand, Update
|
||||||
from telegram.error import BadRequest, TimedOut
|
from telegram.error import BadRequest, TimedOut
|
||||||
from telegram.ext import (
|
from telegram.ext import (
|
||||||
|
|
@ -331,8 +332,8 @@ async def handle_message(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None
|
||||||
messages = get_messages(chat_id)
|
messages = get_messages(chat_id)
|
||||||
messages.append({"role": "user", "content": user_text})
|
messages.append({"role": "user", "content": user_text})
|
||||||
|
|
||||||
# Send "typing" indicator
|
# Send immediate "Thinking ..." placeholder so user knows the bot read their message
|
||||||
await update.message.chat.send_action("typing")
|
thinking = await _safe_reply(update, "Thinking ...")
|
||||||
|
|
||||||
# Run the LLM loop (with tool calls)
|
# Run the LLM loop (with tool calls)
|
||||||
max_rounds = 10
|
max_rounds = 10
|
||||||
|
|
@ -346,7 +347,7 @@ async def handle_message(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error("OpenAI API error: %s", e)
|
log.error("OpenAI API error: %s", e)
|
||||||
await _safe_reply(update, f"Sorry, I hit an error: {e}")
|
await _safe_edit(thinking, update, f"Sorry, I hit an error: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
choice = response.choices[0]
|
choice = response.choices[0]
|
||||||
|
|
@ -361,6 +362,9 @@ async def handle_message(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None
|
||||||
json.loads(tc.function.arguments) if tc.function.arguments else {}
|
json.loads(tc.function.arguments) if tc.function.arguments else {}
|
||||||
)
|
)
|
||||||
log.info("Tool call: %s(%s)", tc.function.name, list(args.keys()))
|
log.info("Tool call: %s(%s)", tc.function.name, list(args.keys()))
|
||||||
|
# Update placeholder with current tool status
|
||||||
|
status = f"Working ... ({tc.function.name})"
|
||||||
|
await _safe_edit(thinking, update, status)
|
||||||
try:
|
try:
|
||||||
result = handle_tool_call(tc.function.name, args)
|
result = handle_tool_call(tc.function.name, args)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -373,7 +377,6 @@ async def handle_message(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
# Continue the loop for the LLM to process tool results
|
# Continue the loop for the LLM to process tool results
|
||||||
await update.message.chat.send_action("typing")
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# No tool calls — we have a final text reply
|
# No tool calls — we have a final text reply
|
||||||
|
|
@ -382,21 +385,36 @@ async def handle_message(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None
|
||||||
if len(messages) > 60:
|
if len(messages) > 60:
|
||||||
messages[:] = messages[:1] + messages[-40:]
|
messages[:] = messages[:1] + messages[-40:]
|
||||||
|
|
||||||
await _safe_reply(update, msg.content)
|
await _safe_edit(thinking, update, msg.content)
|
||||||
return
|
return
|
||||||
|
|
||||||
await _safe_reply(
|
await _safe_edit(
|
||||||
update, "I ran out of steps — please try again with a simpler request."
|
thinking, update, "I ran out of steps — please try again with a simpler request."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _safe_reply(update: Update, text: str, **kwargs) -> None:
|
async def _safe_reply(update: Update, text: str, **kwargs) -> "telegram.Message | None":
|
||||||
"""Reply to a message, falling back to send_message if the original is gone."""
|
"""Reply to a message, falling back to send_message if the original is gone."""
|
||||||
try:
|
try:
|
||||||
await update.message.reply_text(text, **kwargs)
|
return await update.message.reply_text(text, **kwargs)
|
||||||
except BadRequest:
|
except BadRequest:
|
||||||
log.warning("Could not reply to message, sending without reply")
|
log.warning("Could not reply to message, sending without reply")
|
||||||
await update.effective_chat.send_message(text, **kwargs)
|
return await update.effective_chat.send_message(text, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
async def _safe_edit(
|
||||||
|
thinking: "telegram.Message | None",
|
||||||
|
update: Update,
|
||||||
|
text: str,
|
||||||
|
) -> None:
|
||||||
|
"""Edit the placeholder message, falling back to a new message if it fails."""
|
||||||
|
if thinking:
|
||||||
|
try:
|
||||||
|
await thinking.edit_text(text)
|
||||||
|
return
|
||||||
|
except BadRequest:
|
||||||
|
log.warning("Could not edit placeholder, sending new message")
|
||||||
|
await update.effective_chat.send_message(text)
|
||||||
|
|
||||||
|
|
||||||
async def _error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def _error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue