feat: AI chat conclusion + survey completion & management (#95)
* fix: increase assistant chat input height from 1 to 3 rows Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add Anthropic prompt caching to assistant chat Cache the static system prompt and conversation history prefix across turns, reducing input token costs by ~80% on multi-turn conversations. RAG context is intentionally uncached since it changes per query. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add Microsoft Learn MCP integration + refine assistant system prompt - Integrate Microsoft Learn MCP server via Anthropic's MCP connector for real-time documentation lookups (docs search, fetch, code samples) - Refine system prompt: clear persona, structured answer guidelines, when to use RAG flows vs Microsoft Learn, guardrails against fabrication - Add ENABLE_MCP_MICROSOFT_LEARN config toggle (default: True) - Fix bugs from prior edit: wrong MCP URL, broken indentation, undefined usage/token variables, NOT_GIVEN for disabled MCP params - Log MCP tool usage and cache performance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: AI chat session conclusion + survey completion & management AI Assistant - Conclude Session: - 3-step modal: select outcome (resolved/escalated/paused), add notes, AI-generated summary - AI generates structured ticket notes from conversation transcript (PSA-ready format) - Copy to clipboard for pasting into ticketing systems - "Resume in New Chat" for paused sessions (pre-loads context into new chat) - Backend: POST /chats/{id}/conclude endpoint, conclusion_summary/outcome/concluded_at fields - Migration 048: add conclusion fields to assistant_chats Survey Completion Flow: - Email-to-self option after submission (branded HTML email with formatted responses) - Finish button navigates to /survey/thank-you page - Thank you page with close-window message and feedback email callout - Already-submitted state updated with same messaging - Backend: POST /survey/email-copy public endpoint Survey Admin Management: - Read/unread indicators (cyan dot, bold name, auto-mark on expand) - Unread count stat card - Per-row context menu: mark read/unread, archive/unarchive, delete - Bulk actions bar: select all, mark read/unread, archive, delete - Show Archived toggle to filter archived responses - Backend: 7 new admin endpoints (read, unread, archive, unarchive, delete, bulk) - Migration 049: add is_read, archived_at to survey_responses Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: initialize VerifyEmailPage state from token to avoid setState in effect Moves the no-token error case from useEffect into initial state to satisfy the react-hooks/set-state-in-effect ESLint rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit was merged in pull request #95.
This commit is contained in:
@@ -354,6 +354,70 @@ class EmailService:
|
||||
logger.exception("Failed to send survey notification email")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
async def send_survey_copy_email(
|
||||
to_email: str,
|
||||
respondent_name: str | None,
|
||||
formatted_responses: str,
|
||||
) -> bool:
|
||||
"""Send a copy of survey responses to the respondent."""
|
||||
if not settings.email_enabled:
|
||||
logger.warning("Email not sent — RESEND_API_KEY not configured")
|
||||
return False
|
||||
|
||||
try:
|
||||
import resend
|
||||
import html as html_mod
|
||||
|
||||
resend.api_key = settings.RESEND_API_KEY
|
||||
|
||||
safe_name = html_mod.escape(respondent_name or "there")
|
||||
safe_responses = html_mod.escape(formatted_responses).replace("\n", "<br>")
|
||||
subject = "Your FlowPilot Survey Responses"
|
||||
|
||||
email_html = f"""<!DOCTYPE html>
|
||||
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"></head>
|
||||
<body style="margin:0;padding:0;background:#101114;font-family:'Inter',Helvetica,Arial,sans-serif;">
|
||||
<table width="100%" cellpadding="0" cellspacing="0" style="background:#101114;padding:40px 0;">
|
||||
<tr><td align="center">
|
||||
<table width="560" cellpadding="0" cellspacing="0" style="background:#14161a;border:1px solid rgba(255,255,255,0.06);border-radius:16px;">
|
||||
<tr><td style="padding:40px 40px 24px;text-align:center;">
|
||||
<h1 style="margin:0;color:#f8fafc;font-size:24px;font-weight:600;">Resolution<span style="color:#06b6d4;">Flow</span></h1>
|
||||
<p style="margin:8px 0 0;color:#5a6170;font-size:14px;">Your Survey Responses</p>
|
||||
</td></tr>
|
||||
<tr><td style="padding:0 40px 24px;">
|
||||
<p style="margin:0;color:#8891a0;font-size:16px;line-height:1.6;">
|
||||
Hi {safe_name}, here's a copy of your FlowPilot survey responses for your records.
|
||||
</p>
|
||||
</td></tr>
|
||||
<tr><td style="padding:0 40px 32px;">
|
||||
<div style="background:rgba(0,0,0,0.3);border:1px solid rgba(255,255,255,0.06);border-radius:12px;padding:24px;">
|
||||
<p style="margin:0;color:#e0e0e0;font-size:13px;line-height:1.8;">{safe_responses}</p>
|
||||
</div>
|
||||
</td></tr>
|
||||
<tr><td style="padding:0 40px 32px;">
|
||||
<p style="margin:0;color:#5a6170;font-size:12px;text-align:center;">
|
||||
Thank you for your contribution to FlowPilot research.
|
||||
</p>
|
||||
</td></tr>
|
||||
</table>
|
||||
</td></tr>
|
||||
</table>
|
||||
</body></html>"""
|
||||
|
||||
resend.Emails.send({
|
||||
"from": settings.FROM_EMAIL,
|
||||
"to": [to_email],
|
||||
"subject": subject,
|
||||
"html": email_html,
|
||||
})
|
||||
logger.info("Survey copy email sent to %s", to_email)
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
logger.exception("Failed to send survey copy email to %s", to_email)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
async def send_survey_invite_email(
|
||||
to_email: str,
|
||||
|
||||
Reference in New Issue
Block a user