Merge pull request #24 from living-ip/codex/leo-telegram-x402-quote-resume-20260630
Some checks are pending
CI / lint-and-test (push) Waiting to run

Enable quote-first Leo Telegram research
This commit is contained in:
twentyOne2x 2026-06-30 19:13:33 +02:00 committed by GitHub
commit 06a8b547ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 66 additions and 2 deletions

View file

@ -19,6 +19,11 @@ domain_expertise: >
# ─── Hosted Leo Runtime ──────────────────────────────────────────────────
http_chat_proxy_url: "https://leo.livingip.xyz/api/agents/leo/chat"
http_research_proxy_url: "https://leo.livingip.xyz/api/agents/leo/research"
smart_research_command_prefixes:
- "/smart_research"
- "/paid_research"
auto_smart_research_from_chat: true
respond_to_private_chats: true
# ─── KB Scope ────────────────────────────────────────────────────────────
@ -44,6 +49,10 @@ voice_definition: |
answer from retained Living IP runtime evidence and current route state.
Do not claim payment execution unless the HTTP route returns retained
payment/readback evidence.
When addressed or used in private chat, clear requests for fresh sourced
research should go through Leo's hosted research route. First return a paid
research quote and checkout link. Only resume paid execution after a
work_order_id or payment receipt is present.
# ─── Learnings ───────────────────────────────────────────────────────────
learnings_file: agents/leo/learnings.md

View file

@ -61,6 +61,7 @@ from http_chat_proxy import (
extract_smart_research_goal,
post_chat_proxy,
should_attach_structured_market_context,
smart_research_payment_fields_for_message,
smart_research_command_names,
)
@ -1128,6 +1129,10 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
if len(text) < MIN_MESSAGE_LENGTH:
return
if AGENT_HTTP_RESEARCH_PROXY_URL and extract_paid_work_order_id(text):
await handle_tagged(update, context)
return
# Conversation window behavior depends on chat type (Rio: DMs vs groups)
# DMs: auto-respond (always 1-on-1, no false positives)
# Groups: silent context only (reply-to is the only follow-up trigger)
@ -1207,7 +1212,10 @@ async def handle_tagged(update: Update, context: ContextTypes.DEFAULT_TYPE):
tuple(AGENT_MENTION_ALIASES),
)
if AGENT_HTTP_RESEARCH_PROXY_URL and smart_research_goal:
payment_gate = _smart_research_payment_gate(msg.chat_id)
payment_gate = smart_research_payment_fields_for_message(
paid_work_order_id=paid_work_order_id,
configured_payment_gate=_smart_research_payment_gate(msg.chat_id),
)
proxy_research_goal = smart_research_goal
if should_attach_structured_market_context(smart_research_goal):
market_context, market_data_audit, market_duration, market_tokens = await _market_context_for_message(

View file

@ -184,6 +184,19 @@ def should_attach_structured_market_context(message: str) -> bool:
return bool(_MARKET_CONTEXT_RE.search(text))
def smart_research_payment_fields_for_message(
*,
paid_work_order_id: str | None,
configured_payment_gate: dict[str, Any],
) -> dict[str, Any]:
"""Return paid-execution fields only for a settled work-order resume message."""
if not paid_work_order_id:
return {"allow_paid_execution": False}
if configured_payment_gate.get("allow_paid_execution") is True:
return configured_payment_gate
return {"allow_paid_execution": False}
def build_smart_research_proxy_payload(
*,
research_goal: str,

View file

@ -19,6 +19,7 @@ from http_chat_proxy import ( # noqa: E402
extract_chat_proxy_reply,
extract_smart_research_goal,
should_attach_structured_market_context,
smart_research_payment_fields_for_message,
smart_research_command_names,
)
from market_data import extract_market_data_tokens, format_price_context # noqa: E402
@ -31,6 +32,9 @@ def test_leo_config_opts_into_http_chat_proxy_without_changing_default_agents():
assert leo.name == "Leo"
assert leo.http_chat_proxy_url == "https://leo.livingip.xyz/api/agents/leo/chat"
assert leo.http_research_proxy_url == "https://leo.livingip.xyz/api/agents/leo/research"
assert "/smart_research" in leo.smart_research_command_prefixes
assert leo.auto_smart_research_from_chat is True
assert leo.respond_to_private_chats is True
assert "@teLEOhuman" in leo.mention_aliases
assert leo_wallet_test.name == "Leo Wallet Test"
@ -43,7 +47,6 @@ def test_leo_config_opts_into_http_chat_proxy_without_changing_default_agents():
assert "@lipleowallet0622183538bot" in leo_wallet_test.mention_aliases
assert rio.http_chat_proxy_url is None
assert rio.respond_to_private_chats is False
assert leo.auto_smart_research_from_chat is False
def test_invalid_http_chat_proxy_url_fails_closed(tmp_path):
@ -135,6 +138,37 @@ def test_smart_research_payload_can_resume_paid_work_order_without_secret_materi
assert "secret" not in str(payload).lower()
def test_smart_research_payment_fields_quote_first_even_when_gate_enabled():
configured_gate = {
"allow_paid_execution": True,
"approval_ref": "approval_ref_livingip_x402_20260622",
"max_amount_usd": 0.01,
}
payment_fields = smart_research_payment_fields_for_message(
paid_work_order_id=None,
configured_payment_gate=configured_gate,
)
assert payment_fields == {"allow_paid_execution": False}
assert "approval_ref" not in payment_fields
def test_smart_research_payment_fields_resume_uses_capped_gate():
configured_gate = {
"allow_paid_execution": True,
"approval_ref": "approval_ref_livingip_x402_20260622",
"max_amount_usd": 0.01,
}
payment_fields = smart_research_payment_fields_for_message(
paid_work_order_id="sponsored_work_orders:f951ccc6c7762ecba6f76cf6",
configured_payment_gate=configured_gate,
)
assert payment_fields == configured_gate
@pytest.mark.parametrize(
("message", "expected"),
[