commcoach: email subject uses context title instead of UUID, AI generates HTML directly
Made-with: Cursor
This commit is contained in:
parent
364e431749
commit
740d2a0b49
2 changed files with 59 additions and 22 deletions
|
|
@ -86,6 +86,27 @@ def cleanupSessionEvents(sessionId: str):
|
|||
CHUNK_WORD_SIZE = 4
|
||||
CHUNK_DELAY_SECONDS = 0.05
|
||||
|
||||
def _wrapEmailHtml(contentHtml: str) -> str:
|
||||
"""Wrap AI-generated HTML content in a styled email shell."""
|
||||
return f"""<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"></head>
|
||||
<body style="margin:0;padding:0;background-color:#f4f4f7;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif">
|
||||
<div style="background-color:#f4f4f7;padding:32px 16px">
|
||||
<div style="max-width:600px;margin:0 auto;background:#ffffff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.06)">
|
||||
<div style="background:linear-gradient(135deg,#2563eb,#1e40af);padding:28px 32px">
|
||||
<h1 style="margin:0;color:#ffffff;font-size:20px;font-weight:600">Coaching-Session Zusammenfassung</h1>
|
||||
<p style="margin:6px 0 0;color:rgba(255,255,255,0.8);font-size:13px">PowerOn CommCoach</p>
|
||||
</div>
|
||||
<div style="padding:28px 32px;color:#374151;font-size:15px;line-height:1.65">{contentHtml}</div>
|
||||
<div style="padding:18px 32px;background:#f9fafb;border-top:1px solid #e5e7eb;text-align:center">
|
||||
<p style="margin:0;color:#9ca3af;font-size:12px">Diese Zusammenfassung wurde automatisch erstellt.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
DOC_INTENT_MAX_DOCS = 3
|
||||
DOC_CONTENT_MAX_CHARS = 3000
|
||||
|
||||
|
|
@ -634,14 +655,21 @@ class CommcoachService:
|
|||
})
|
||||
return session
|
||||
|
||||
# Generate summary
|
||||
# Generate summary (AI returns JSON with summary + emailHtml)
|
||||
summary = None
|
||||
emailHtml = None
|
||||
try:
|
||||
summaryPrompt = aiPrompts.buildSummaryPrompt(messages, context.get("title", "Coaching"))
|
||||
summaryResponse = await self._callAi("Du bist ein präziser Zusammenfasser.", summaryPrompt)
|
||||
summary = summaryResponse.content.strip() if summaryResponse and summaryResponse.errorCount == 0 else None
|
||||
summaryResponse = await self._callAi("Du bist ein präziser Zusammenfasser. Antworte NUR als JSON.", summaryPrompt)
|
||||
if summaryResponse and summaryResponse.errorCount == 0 and summaryResponse.content:
|
||||
parsed = aiPrompts.parseJsonResponse(summaryResponse.content.strip(), None)
|
||||
if isinstance(parsed, dict):
|
||||
summary = parsed.get("summary") or parsed.get("text")
|
||||
emailHtml = parsed.get("emailHtml")
|
||||
else:
|
||||
summary = summaryResponse.content.strip()
|
||||
except Exception as e:
|
||||
logger.warning(f"Summary generation failed: {e}")
|
||||
summary = None
|
||||
|
||||
keyTopics = None
|
||||
if summary:
|
||||
|
|
@ -782,7 +810,8 @@ class CommcoachService:
|
|||
|
||||
# Send email summary
|
||||
if summary:
|
||||
await self._sendSessionEmail(session, summary, interface)
|
||||
contextTitle = context.get("title", "Coaching") if context else "Coaching"
|
||||
await self._sendSessionEmail(session, summary, emailHtml, contextTitle, interface)
|
||||
|
||||
await emitSessionEvent(sessionId, "sessionState", {
|
||||
"status": "completed",
|
||||
|
|
@ -833,8 +862,8 @@ class CommcoachService:
|
|||
except Exception as e:
|
||||
logger.warning(f"Failed to update streak: {e}")
|
||||
|
||||
async def _sendSessionEmail(self, session: Dict[str, Any], summary: str, interface):
|
||||
"""Send session summary via email if enabled."""
|
||||
async def _sendSessionEmail(self, session: Dict[str, Any], summary: str, emailHtml: str, contextTitle: str, interface):
|
||||
"""Send session summary via email if enabled. Uses AI-generated HTML directly."""
|
||||
try:
|
||||
profile = interface.getProfile(self.userId, self.instanceId)
|
||||
if profile and not profile.get("emailSummaryEnabled", True):
|
||||
|
|
@ -849,13 +878,10 @@ class CommcoachService:
|
|||
return
|
||||
|
||||
messaging = getMessagingInterface()
|
||||
subject = f"Coaching-Session Zusammenfassung: {session.get('contextId', 'Session')}"
|
||||
htmlMessage = f"""
|
||||
<h2>Coaching-Session Zusammenfassung</h2>
|
||||
<p>{summary.replace(chr(10), '<br>')}</p>
|
||||
<hr>
|
||||
<p><small>Diese Zusammenfassung wurde automatisch erstellt.</small></p>
|
||||
"""
|
||||
subject = f"Coaching-Session Zusammenfassung: {contextTitle}"
|
||||
|
||||
contentHtml = emailHtml if emailHtml else f"<p>{summary}</p>"
|
||||
htmlMessage = _wrapEmailHtml(contentHtml)
|
||||
|
||||
messaging.send("email", user.email, subject, htmlMessage)
|
||||
interface.updateSession(session.get("id"), {"emailSent": True})
|
||||
|
|
|
|||
|
|
@ -260,24 +260,35 @@ Fuer ein NEUES Dokument: {"title": "...", "content": "...Inhalt..."}"""
|
|||
|
||||
|
||||
def buildSummaryPrompt(messages: List[Dict[str, Any]], contextTitle: str) -> str:
|
||||
"""Build a prompt to generate a session summary."""
|
||||
"""Build a prompt to generate a session summary as JSON with plain text and styled HTML email."""
|
||||
conversation = ""
|
||||
for msg in messages:
|
||||
role = "Benutzer" if msg.get("role") == "user" else "Coach"
|
||||
conversation += f"\n{role}: {msg.get('content', '')}"
|
||||
|
||||
return f"""Erstelle eine kompakte Zusammenfassung dieser Coaching-Session zum Thema "{contextTitle}".
|
||||
return f"""Erstelle eine Zusammenfassung dieser Coaching-Session zum Thema "{contextTitle}".
|
||||
|
||||
Struktur:
|
||||
1. **Kernthema**: Was wurde besprochen (1-2 Sätze)
|
||||
2. **Erkenntnisse**: Was wurde erkannt/gelernt (Stichpunkte)
|
||||
3. **Nächste Schritte**: Konkrete Aufgaben für den Benutzer (Stichpunkte)
|
||||
4. **Fortschritt**: Einschätzung des Fortschritts
|
||||
Antworte AUSSCHLIESSLICH als JSON mit zwei Feldern:
|
||||
|
||||
{{
|
||||
"summary": "Kompakte Zusammenfassung als Plaintext (fuer Anzeige in der App). Struktur: 1. Kernthema, 2. Erkenntnisse, 3. Naechste Schritte, 4. Fortschritt.",
|
||||
"emailHtml": "<div>...</div>"
|
||||
}}
|
||||
|
||||
Fuer "emailHtml": Erstelle ein professionell formatiertes HTML-Fragment (KEIN vollstaendiges HTML-Dokument, nur der Inhalt-Block).
|
||||
Verwende inline CSS fuer schoene Darstellung in E-Mail-Clients:
|
||||
- Verwende <h3> fuer Abschnitte (color: #1e40af; margin: 20px 0 8px; font-size: 16px)
|
||||
- Verwende <ul>/<li> fuer Stichpunkte (margin: 4px 0; line-height: 1.6)
|
||||
- Verwende <strong> fuer Hervorhebungen
|
||||
- Verwende <p> fuer Fliesstext (color: #374151; line-height: 1.65; font-size: 15px)
|
||||
- Verwende <hr style="border:none;border-top:1px solid #e5e7eb;margin:20px 0"> als Trenner
|
||||
|
||||
Fuer "summary": Kompakter Plaintext ohne HTML/Markdown. Abschnitte mit Zeilenumbruechen trennen.
|
||||
|
||||
Gespräch:
|
||||
{conversation}
|
||||
|
||||
Antworte auf Deutsch, sachlich und kompakt."""
|
||||
Antworte auf Deutsch, sachlich und kompakt. NUR JSON, keine Erklaerungen."""
|
||||
|
||||
|
||||
def buildScoringPrompt(messages: List[Dict[str, Any]], contextCategory: str) -> str:
|
||||
|
|
|
|||
Loading…
Reference in a new issue