handoff fixes

This commit is contained in:
debu-2 2026-10-06 02:44:54 +05:30
parent 6c52cd19cb
commit 89ddebdb60
15 changed files with 991 additions and 271 deletions

View file

@ -4,15 +4,16 @@ from datetime import datetime, timezone
from bson import ObjectId
from app.services.external_reply_service import (
build_external_reply_plan,
send_external_reply,
)
from app.services.handoff_email_templates import (
build_initial_handoff_html,
build_initial_handoff_text,
build_relay_confirmation_text,
build_update_handoff_html,
build_update_handoff_text,
external_reply_subject,
handoff_email_subject,
)
from app.services.handoff_mirror import load_mirror_payload, load_mirror_update_payload
from app.services.handoff_evaluator import evaluate_handoff
from app.services.mail_sender import deliver_email
from app.services.message_ids import normalize_message_id, parse_message_id_list
@ -297,6 +298,7 @@ async def store_handoff_message(
"received_at": now if direction == "inbound" else None,
"status": "received" if direction == "inbound" else "accepted",
"message_id": normalize_message_id(internet_message_id),
"internet_message_id": normalize_message_id(internet_message_id),
"in_reply_to": normalize_message_id(in_reply_to),
"references": references or [],
}
@ -355,28 +357,17 @@ async def send_handoff_to_human(
domain,
existing_handoff: dict | None = None,
) -> dict:
external_sender = external_message.get("sender", {})
external_sender_text = external_sender.get("address") or "unknown sender"
external_subject = external_message.get("subject", "")
external_body = external_message.get("body_text") or external_message.get("snippet") or ""
stable_subject = handoff_email_subject(external_subject)
mirror_source = {**external_message, "_agent_email": agent_email}
if existing_handoff:
handoff_id = existing_handoff["_id"]
handoff_thread_id = existing_handoff["handoff_thread_id"]
parent = await _latest_handoff_message(database, account_id, handoff_thread_id)
threading = threading_fields_from_parent(parent) if parent else {}
subject = _handoff_reply_subject(external_subject)
body_text = build_update_handoff_text(
external_sender=external_sender_text,
external_body=external_body,
agent_email=agent_email,
)
body_html = build_update_handoff_html(
external_sender=external_sender_text,
external_body=external_body,
agent_email=agent_email,
)
mirror = await load_mirror_update_payload(mirror_source, agent_email=agent_email)
relationship = LINK_EXTERNAL_UPDATE_RELAYED
else:
handoff_thread_id = await create_empty_handoff_thread(
@ -386,20 +377,8 @@ async def send_handoff_to_human(
)
threading = {}
subject = stable_subject
body_text = build_initial_handoff_text(
external_sender=external_sender_text,
external_subject=external_subject,
external_body=external_body,
handoff_reason=handoff_reason,
agent_email=agent_email,
)
body_html = build_initial_handoff_html(
external_sender=external_sender_text,
external_subject=external_subject,
external_body=external_body,
handoff_reason=handoff_reason,
agent_email=agent_email,
)
mirror = await load_mirror_payload(mirror_source)
external_sender_text = (external_message.get("sender") or {}).get("address") or "unknown sender"
handoff_doc = {
"account_id": account_id,
"agent_email": agent_email.lower(),
@ -426,11 +405,13 @@ async def send_handoff_to_human(
cc=[],
bcc=[],
subject=subject,
body_text=body_text,
body_html=body_html,
body_text=mirror.body_text,
body_html=mirror.body_html,
domain=domain,
in_reply_to=threading.get("in_reply_to"),
references=threading.get("references"),
inline_parts=mirror.inline_parts,
file_attachments=mirror.file_attachments,
)
handoff_message = await store_handoff_message(
@ -442,8 +423,8 @@ async def send_handoff_to_human(
sender={"name": "Gentic Agent", "address": agent_email},
recipients=[{"name": human_email.split("@")[0], "address": human_email}],
subject=subject,
body_text=body_text,
body_html=body_html,
body_text=mirror.body_text,
body_html=mirror.body_html,
internet_message_id=internet_message_id,
in_reply_to=threading.get("in_reply_to"),
references=threading.get("references"),
@ -581,81 +562,28 @@ async def process_human_reply(
logger.warning("handoff_reply_missing_external_parent handoff_id=%s", handoff["_id"])
return
threading = threading_fields_from_parent(external_parent)
external_subject = external_reply_subject(
handoff.get("external_subject") or external_parent.get("subject", "")
)
external_recipient = external_recipient or external_parent.get("sender", {}).get("address")
if not external_recipient:
return
customer_body = new_reply_content
internet_message_id = await deliver_email(
sender=agent_email,
recipients=[external_recipient],
cc=[],
bcc=[],
subject=external_subject,
reply_plan = build_external_reply_plan(
parent_external_message=external_parent,
body_text=customer_body,
body_html=None,
domain=domain,
in_reply_to=threading.get("in_reply_to"),
references=threading.get("references"),
subject_override=external_reply_subject(
handoff.get("external_subject") or external_parent.get("subject", "")
),
recipients=[external_recipient],
)
external_doc = {
"direction": "outbound",
"message_scope": MESSAGE_SCOPE_EXTERNAL,
"account_id": account_id,
"sender": {"name": "Gentic Agent", "address": agent_email},
"to": [{"name": external_recipient.split("@")[0], "address": external_recipient}],
"subject": external_subject,
"snippet": _snippet(customer_body),
"body_text": customer_body,
"body_html": None,
"attachments": [],
"created_at": now,
"sent_at": now,
"status": "accepted",
"message_id": normalize_message_id(internet_message_id),
"in_reply_to": threading.get("in_reply_to"),
"references": threading.get("references", []),
"parent_message_id": external_parent["_id"],
"thread_relation": "reply",
"thread_resolution_method": "human_handoff_bridge",
}
ext_result = await database.messages.insert_one(external_doc)
external_doc["_id"] = ext_result.inserted_id
await attach_message_to_thread(
external_doc = await send_external_reply(
database,
account_id=account_id,
thread_id=external_thread_id,
message_id=ext_result.inserted_id,
subject=external_subject,
event_at=now,
)
await database.mailbox_entries.insert_one(
{
"account_id": account_id,
"message_id": ext_result.inserted_id,
"thread_id": external_thread_id,
"mailbox": "sent",
"message_scope": MESSAGE_SCOPE_EXTERNAL,
"direction": "outbound",
"sender": external_doc["sender"],
"recipients": external_doc["to"],
"subject": external_subject,
"snippet": external_doc["snippet"],
"attachments": [],
"status": "accepted",
"is_read": True,
"is_starred": False,
"event_at": now,
"created_at": now,
"trashed_at": None,
"deletion_state": None,
}
sender=agent_email,
sender_name="Gentic Agent",
external_thread_id=external_thread_id,
plan=reply_plan,
domain=domain,
resolution_method="human_handoff_bridge",
)
relay_body = build_relay_confirmation_text(customer_body)