handoff fixes
This commit is contained in:
parent
6c52cd19cb
commit
89ddebdb60
15 changed files with 991 additions and 271 deletions
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue