From 2b0b142f95b50f843a9039b1f1c45e2e1adbcb33 Mon Sep 17 00:00:00 2001 From: Apple Date: Sat, 21 Feb 2026 00:05:09 -0800 Subject: [PATCH] gateway: fix greeting UX and reduce false photo-intent fallbacks --- gateway-bot/http_api.py | 70 +++++++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/gateway-bot/http_api.py b/gateway-bot/http_api.py index e644470f..fbdc2a17 100644 --- a/gateway-bot/http_api.py +++ b/gateway-bot/http_api.py @@ -138,17 +138,6 @@ def _looks_like_photo_followup(text: str) -> bool: if any(m in t for m in direct_markers): return True - # If user is correcting previous visual interpretation, route to vision again. - correction_markers = [ - "неправильна відповідь", "не правильна відповідь", "не видумуй", "це не так", - "ти помилився", "ти помилилась", "неправильно визначив", - "wrong answer", "you are wrong", "that is incorrect", - "неправильный ответ", "это не так", "ты ошибся", - ] - photo_topic_markers = ["фото", "зображ", "рослин", "image", "photo", "plant", "растен"] - if any(c in t for c in correction_markers) and any(p in t for p in photo_topic_markers): - return True - # Flexible forms: "що на ... фото/зображенні/світлині" if re.search(r"(що|what|что)\s+на\s+.*(фото|зображ|світлин|image|photo)", t): # Exclude common meta-questions @@ -158,6 +147,37 @@ def _looks_like_photo_followup(text: str) -> bool: return False +def _needs_photo_only_response(text: str) -> bool: + """ + Return True only for explicit requests to analyze/describe image content. + Do not trigger on meta-dialogue about previous mistakes. + """ + t = (text or "").strip().lower() + if not t: + return False + explicit_patterns = [ + r"(що|what|что).{0,24}(на|in).{0,24}(фото|зображ|світлин|image|photo)", + r"(опиши|describe|проаналізуй|analyz|анализируй).{0,32}(фото|зображ|image|photo)", + r"(яка|какая|what).{0,28}(рослин|plant|культура).{0,28}(на|in).{0,28}(фото|image|photo)", + ] + return any(re.search(p, t) for p in explicit_patterns) + + +def _is_simple_greeting(text: str) -> bool: + t = (text or "").strip().lower() + if not t: + return False + compact = re.sub(r"[^a-zа-яіїєґ0-9 ]+", "", t).strip() + greetings = { + "привіт", "вітаю", "добрий день", "доброго дня", "доброго вечора", + "hello", "hi", "hey", "good morning", "good evening", + } + if compact in greetings: + return True + # Short greeting variants like "привіт!" / "hi!" + return len(compact.split()) <= 3 and any(g in compact for g in greetings) + + def _extract_unanswered_user_messages( memory_context: Dict[str, Any], current_user_id: str, @@ -2536,14 +2556,33 @@ async def handle_telegram_webhook( text = update.message.get("text", "") caption = update.message.get("caption", "") + # Friendly greeting fast-path for better UX and less mechanical replies. + if _is_simple_greeting(text): + greeting_reply = ( + f"Привіт, {username or 'друже'}! Я {agent_config.name}. " + "Можу допомогти з фото рослин, Excel-звітами та короткими практичними порадами." + ) + await send_telegram_message(chat_id, greeting_reply, telegram_token) + await memory_client.save_chat_turn( + agent_id=agent_config.agent_id, + team_id=dao_id, + user_id=f"tg:{user_id}", + message=text, + response=greeting_reply, + channel_id=chat_id, + scope="short_term", + save_agent_response=True, + agent_metadata={"greeting_fast_path": True}, + username=username, + ) + return {"ok": True, "agent": agent_config.agent_id, "mode": "greeting_fast_path"} + # Photo/image intent guard: # if text references a photo/image, try to resolve latest file_id and route to vision. photo_intent = False if text: tl = text.lower() - photo_intent = _looks_like_photo_followup(text) or any( - k in tl for k in ("фото", "зображ", "світлин", "image", "photo") - ) + photo_intent = _looks_like_photo_followup(text) if not photo_intent: # Robust fallback for common formulations like "що на цьому фото?" photo_intent = bool( @@ -2590,8 +2629,7 @@ async def handle_telegram_webhook( return followup_result # Hard guard: don't send photo-related requests to text LLM path when image context is missing. - is_question_like = ("?" in text) or any(k in tl for k in ("що", "опиши", "проанал", "what", "describe", "analy", "что")) - if is_question_like: + if _needs_photo_only_response(text): await send_telegram_message( chat_id, "Бачу питання про фото, але не знайшов зображення в історії сесії. Надішли фото ще раз з коротким питанням, і я одразу проаналізую.",