Merge pull request #31 from living-ip/codex/leo-paid-research-standalone-ack-20260701
Some checks are pending
CI / lint-and-test (push) Waiting to run

Split Telegram paid research acknowledgement from answer
This commit is contained in:
twentyOne2x 2026-07-01 21:37:00 +02:00 committed by GitHub
commit c1eb4f5718
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 84 additions and 4 deletions

View file

@ -65,6 +65,7 @@ from http_chat_proxy import (
should_attach_structured_market_context,
smart_research_payment_fields_for_message,
smart_research_command_names,
split_smart_research_auto_resume_reply,
)
# ─── Config ─────────────────────────────────────────────────────────────
@ -859,11 +860,21 @@ async def _poll_smart_research_auto_resume(
if classification == "pending":
continue
if classification == "ready" and proxy_reply:
await _reply_text_native(msg, proxy_reply, do_quote=True)
ack_reply, answer_reply = split_smart_research_auto_resume_reply(
proxy_body,
proxy_reply,
)
if ack_reply:
await _reply_text_native(msg, ack_reply, do_quote=True)
if answer_reply:
await _reply_text_native(msg, answer_reply, do_quote=not ack_reply)
visible_reply = "\n\n".join(
part for part in (ack_reply, answer_reply) if part
) or proxy_reply
if msg.from_user:
entry = {
"user": research_goal[:500],
"bot": proxy_reply[:500],
"bot": visible_reply[:500],
"username": username or "anonymous",
}
user_key = (msg.chat_id, msg.from_user.id)
@ -879,9 +890,9 @@ async def _poll_smart_research_auto_resume(
user_response_times[msg.from_user.id].append(time.time())
_record_transcript(
msg,
proxy_reply,
visible_reply,
is_bot=True,
rio_response=proxy_reply,
rio_response=visible_reply,
internal={
"agent": AGENT_NAME.lower(),
"http_research_proxy_auto_resume": True,

View file

@ -7,7 +7,15 @@ from html import escape
from typing import Any
DEFAULT_SMART_RESEARCH_COMMAND_PREFIXES = ("/smart_research", "/paid_research")
SMART_RESEARCH_PAYMENT_ACK_TEXT = "Payment received. Research is starting now."
_TELEGRAM_COMMAND_NAME_RE = re.compile(r"^[A-Za-z0-9_]+$")
_SMART_RESEARCH_PAYMENT_ACK_PREFIX_RE = re.compile(
r"^\s*"
r"Payment received[.!]\s*"
r"(?:Research is starting now[.!]\s*)?"
r"(?:I will use the included source-tool budget and continue with the answer here[.!]\s*)?",
re.IGNORECASE,
)
_AUTO_SMART_RESEARCH_RE = re.compile(
r"\b("
r"research|source|sources|citation|citations|evidence|"
@ -319,6 +327,37 @@ def classify_smart_research_auto_resume_response(
return "ignore"
def split_smart_research_auto_resume_reply(
payload: dict[str, Any] | None,
reply: str | None,
) -> tuple[str | None, str | None]:
"""Split a paid auto-resume reply into receipt ack and answer text.
The HTTP route may return one combined reply starting with "Payment
received...". Telegram should show that as two messages: a quick payment
receipt first, then the research result or clean tool-failure message.
"""
if not isinstance(reply, str) or not reply.strip():
return None, None
text = reply.strip()
if not isinstance(payload, dict):
return None, text
auto_resume = payload.get("autoResume")
paid_work_order = payload.get("paidWorkOrder")
paid_resume = (
isinstance(auto_resume, dict)
and auto_resume.get("activated") is True
or isinstance(paid_work_order, dict)
and paid_work_order.get("status") == "settled"
)
if not paid_resume:
return None, text
answer = _SMART_RESEARCH_PAYMENT_ACK_PREFIX_RE.sub("", text, count=1).strip()
return SMART_RESEARCH_PAYMENT_ACK_TEXT, answer or None
def build_smart_research_proxy_payload(
*,
research_goal: str,

View file

@ -23,6 +23,7 @@ from http_chat_proxy import ( # noqa: E402
extract_chat_proxy_reply,
extract_smart_research_goal,
should_attach_structured_market_context,
split_smart_research_auto_resume_reply,
smart_research_payment_fields_for_message,
smart_research_command_names,
)
@ -227,6 +228,35 @@ def test_classify_smart_research_auto_resume_response(http_status, payload, expe
assert classify_smart_research_auto_resume_response(http_status, payload) == expected
def test_split_smart_research_auto_resume_reply_separates_payment_ack_from_answer():
payload = {
"autoResume": {"activated": True},
"paidWorkOrder": {"status": "settled"},
}
reply = (
"Payment received. Research is starting now. "
"I will use the included source-tool budget and continue with the answer here.\n\n"
"Based on the Twitter/X results I found, Ranger is being discussed as wound down."
)
ack, answer = split_smart_research_auto_resume_reply(payload, reply)
assert ack == "Payment received. Research is starting now."
assert answer == (
"Based on the Twitter/X results I found, Ranger is being discussed as wound down."
)
def test_split_smart_research_auto_resume_reply_keeps_non_paid_ready_reply_intact():
ack, answer = split_smart_research_auto_resume_reply(
{"status": "complete"},
"Here is the answer.",
)
assert ack is None
assert answer == "Here is the answer."
@pytest.mark.parametrize(
("message", "expected"),
[