149 lines
4.9 KiB
Python
149 lines
4.9 KiB
Python
"""HTTP chat proxy helpers for opt-in Telegram agent routing."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from typing import Any
|
|
|
|
DEFAULT_SMART_RESEARCH_COMMAND_PREFIXES = ("/smart_research", "/paid_research")
|
|
|
|
|
|
def build_chat_proxy_payload(
|
|
*,
|
|
message: str,
|
|
source: str,
|
|
agent: str,
|
|
chat_id: int | None = None,
|
|
message_id: int | None = None,
|
|
username: str | None = None,
|
|
) -> dict[str, Any]:
|
|
"""Build the no-secret payload sent from Telegram to an agent HTTP chat route."""
|
|
metadata = {
|
|
"source": source,
|
|
"agent": agent,
|
|
"chat_id": chat_id,
|
|
"message_id": message_id,
|
|
"username": username,
|
|
}
|
|
return {
|
|
"message": message,
|
|
"metadata": {k: v for k, v in metadata.items() if v is not None},
|
|
}
|
|
|
|
|
|
def extract_smart_research_goal(
|
|
message: str,
|
|
command_prefixes: tuple[str, ...] | list[str] = DEFAULT_SMART_RESEARCH_COMMAND_PREFIXES,
|
|
) -> str | None:
|
|
"""Return the research goal when a Telegram message opts into smart research."""
|
|
text = message.strip()
|
|
for prefix in command_prefixes:
|
|
command = re.escape(prefix.lstrip("/"))
|
|
match = re.match(rf"^(?:@\w+\s+)?/{command}(?:@\w+)?(?:\s+(?P<goal>.+))?$", text, re.IGNORECASE)
|
|
if match:
|
|
goal = (match.group("goal") or "").strip()
|
|
return goal or None
|
|
return None
|
|
|
|
|
|
def build_smart_research_proxy_payload(
|
|
*,
|
|
research_goal: str,
|
|
source: str,
|
|
agent: str,
|
|
chat_id: int | None = None,
|
|
message_id: int | None = None,
|
|
username: str | None = None,
|
|
allow_paid_execution: bool = False,
|
|
approval_ref: str | None = None,
|
|
max_amount_usd: float | None = None,
|
|
include_synthesis: bool = True,
|
|
) -> dict[str, Any]:
|
|
"""Build the no-secret Telegram payload for Leo smart research."""
|
|
payload = build_chat_proxy_payload(
|
|
message=research_goal,
|
|
source=source,
|
|
agent=agent,
|
|
chat_id=chat_id,
|
|
message_id=message_id,
|
|
username=username,
|
|
)
|
|
payload.update(
|
|
{
|
|
"research_goal": research_goal,
|
|
"allow_paid_execution": bool(allow_paid_execution),
|
|
"include_synthesis": bool(include_synthesis),
|
|
}
|
|
)
|
|
if max_amount_usd is not None:
|
|
payload["max_amount_usd"] = max_amount_usd
|
|
if allow_paid_execution and approval_ref:
|
|
payload["approval_ref"] = approval_ref
|
|
return payload
|
|
|
|
|
|
def extract_chat_proxy_reply(payload: dict[str, Any]) -> str | None:
|
|
"""Extract a reply from supported Living IP Leo chat response shapes."""
|
|
if not isinstance(payload, dict):
|
|
return None
|
|
|
|
direct_reply = payload.get("reply")
|
|
if isinstance(direct_reply, str) and direct_reply.strip():
|
|
return direct_reply.strip()
|
|
|
|
decision = payload.get("decision")
|
|
if isinstance(decision, dict):
|
|
decision_reply = decision.get("reply")
|
|
if isinstance(decision_reply, str) and decision_reply.strip():
|
|
return decision_reply.strip()
|
|
|
|
llm = payload.get("llm")
|
|
if isinstance(llm, dict):
|
|
llm_reply = llm.get("reply")
|
|
if isinstance(llm_reply, str) and llm_reply.strip():
|
|
return llm_reply.strip()
|
|
llm_decision = llm.get("decision")
|
|
if isinstance(llm_decision, dict):
|
|
llm_decision_reply = llm_decision.get("reply")
|
|
if isinstance(llm_decision_reply, str) and llm_decision_reply.strip():
|
|
return llm_decision_reply.strip()
|
|
|
|
synthesis = payload.get("synthesis")
|
|
if isinstance(synthesis, dict):
|
|
synthesis_reply = synthesis.get("reply")
|
|
if isinstance(synthesis_reply, str) and synthesis_reply.strip():
|
|
return synthesis_reply.strip()
|
|
synthesis_decision = synthesis.get("decision")
|
|
if isinstance(synthesis_decision, dict):
|
|
synthesis_decision_reply = synthesis_decision.get("reply")
|
|
if isinstance(synthesis_decision_reply, str) and synthesis_decision_reply.strip():
|
|
return synthesis_decision_reply.strip()
|
|
|
|
strongest_claim = payload.get("strongestClaimAllowed")
|
|
if isinstance(strongest_claim, str) and strongest_claim.strip():
|
|
return strongest_claim.strip()
|
|
|
|
return None
|
|
|
|
|
|
async def post_chat_proxy(
|
|
*,
|
|
url: str,
|
|
payload: dict[str, Any],
|
|
timeout_seconds: int = 30,
|
|
) -> tuple[int, dict[str, Any], str | None]:
|
|
"""POST to an agent HTTP chat route and return status, JSON body, and reply."""
|
|
import aiohttp
|
|
|
|
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=timeout_seconds)) as session:
|
|
async with session.post(
|
|
url,
|
|
json=payload,
|
|
headers={
|
|
"Accept": "application/json",
|
|
"Content-Type": "application/json",
|
|
"X-LivingIP-Source": "telegram-agent-proxy",
|
|
},
|
|
) as resp:
|
|
data = await resp.json(content_type=None)
|
|
return resp.status, data, extract_chat_proxy_reply(data)
|