This shows you the differences between two versions of the page.
| wiki:ai:emotional_support_bot [2025/07/21 04:34] – created ymurugesan | wiki:ai:emotional_support_bot [2025/07/21 04:37] (current) – ymurugesan | ||
|---|---|---|---|
| Line 319: | Line 319: | ||
| </ | </ | ||
| + | |||
| + | src/ | ||
| + | |||
| + | < | ||
| + | |||
| + | < | ||
| + | <html lang=" | ||
| + | < | ||
| + | <meta charset=" | ||
| + | <meta name=" | ||
| + | < | ||
| + | < | ||
| + | * { | ||
| + | margin: 0; | ||
| + | padding: 0; | ||
| + | box-sizing: border-box; | ||
| + | } | ||
| + | |||
| + | body { | ||
| + | font-family: | ||
| + | background: linear-gradient(135deg, | ||
| + | min-height: 100vh; | ||
| + | display: flex; | ||
| + | align-items: | ||
| + | justify-content: | ||
| + | padding: 20px; | ||
| + | } | ||
| + | |||
| + | .container { | ||
| + | background: rgba(255, 255, 255, 0.95); | ||
| + | backdrop-filter: | ||
| + | border-radius: | ||
| + | padding: 30px; | ||
| + | max-width: 600px; | ||
| + | width: 100%; | ||
| + | box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); | ||
| + | } | ||
| + | |||
| + | .header { | ||
| + | text-align: center; | ||
| + | margin-bottom: | ||
| + | } | ||
| + | |||
| + | .header h1 { | ||
| + | color: #333; | ||
| + | font-size: 2.5em; | ||
| + | font-weight: | ||
| + | margin-bottom: | ||
| + | } | ||
| + | |||
| + | .header p { | ||
| + | color: #666; | ||
| + | font-size: 1.1em; | ||
| + | } | ||
| + | |||
| + | .chat-container { | ||
| + | background: #f8f9fa; | ||
| + | border-radius: | ||
| + | padding: 20px; | ||
| + | margin-bottom: | ||
| + | min-height: 300px; | ||
| + | max-height: 400px; | ||
| + | overflow-y: auto; | ||
| + | } | ||
| + | |||
| + | .message { | ||
| + | margin-bottom: | ||
| + | padding: 15px; | ||
| + | border-radius: | ||
| + | max-width: 80%; | ||
| + | word-wrap: break-word; | ||
| + | animation: fadeIn 0.3s ease-in; | ||
| + | } | ||
| + | |||
| + | .user-message { | ||
| + | background: #007bff; | ||
| + | color: white; | ||
| + | margin-left: | ||
| + | text-align: right; | ||
| + | } | ||
| + | |||
| + | .bot-message { | ||
| + | background: white; | ||
| + | color: #333; | ||
| + | border: 1px solid #e0e0e0; | ||
| + | margin-right: | ||
| + | } | ||
| + | |||
| + | .sentiment-indicator { | ||
| + | display: inline-block; | ||
| + | padding: 4px 8px; | ||
| + | border-radius: | ||
| + | font-size: 0.8em; | ||
| + | font-weight: | ||
| + | margin-bottom: | ||
| + | } | ||
| + | |||
| + | .sentiment-positive { background: #d4edda; color: #155724; } | ||
| + | .sentiment-negative { background: #f8d7da; color: #721c24; } | ||
| + | .sentiment-neutral { background: #e2e3e5; color: #383d41; } | ||
| + | |||
| + | .input-container { | ||
| + | display: flex; | ||
| + | gap: 10px; | ||
| + | margin-bottom: | ||
| + | } | ||
| + | |||
| + | .input-field { | ||
| + | flex: 1; | ||
| + | padding: 15px; | ||
| + | border: 2px solid #e0e0e0; | ||
| + | border-radius: | ||
| + | font-size: 1em; | ||
| + | outline: none; | ||
| + | transition: border-color 0.3s ease; | ||
| + | } | ||
| + | |||
| + | .input-field: | ||
| + | border-color: | ||
| + | } | ||
| + | |||
| + | .send-button { | ||
| + | background: linear-gradient(45deg, | ||
| + | color: white; | ||
| + | border: none; | ||
| + | padding: 15px 25px; | ||
| + | border-radius: | ||
| + | font-size: 1em; | ||
| + | font-weight: | ||
| + | cursor: pointer; | ||
| + | transition: transform 0.2s ease, box-shadow 0.2s ease; | ||
| + | } | ||
| + | |||
| + | .send-button: | ||
| + | transform: translateY(-2px); | ||
| + | box-shadow: 0 10px 20px rgba(0, 123, 255, 0.3); | ||
| + | } | ||
| + | |||
| + | .send-button: | ||
| + | background: #ccc; | ||
| + | cursor: not-allowed; | ||
| + | transform: none; | ||
| + | box-shadow: none; | ||
| + | } | ||
| + | |||
| + | |||
| + | |||
| + | .loading { | ||
| + | display: none; | ||
| + | text-align: center; | ||
| + | color: #666; | ||
| + | font-style: italic; | ||
| + | } | ||
| + | |||
| + | .error { | ||
| + | color: #dc3545; | ||
| + | background: #f8d7da; | ||
| + | padding: 10px; | ||
| + | border-radius: | ||
| + | margin-bottom: | ||
| + | } | ||
| + | |||
| + | @keyframes fadeIn { | ||
| + | from { opacity: 0; transform: translateY(10px); | ||
| + | to { opacity: 1; transform: translateY(0); | ||
| + | } | ||
| + | |||
| + | @media (max-width: 600px) { | ||
| + | .input-container { | ||
| + | flex-direction: | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | </ | ||
| + | < | ||
| + | <div class=" | ||
| + | <div class=" | ||
| + | < | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | <div class=" | ||
| + | <div class=" | ||
| + | <div class=" | ||
| + | Hello! I'm here to provide emotional support. Please share what's on your mind, and I'll analyze your sentiment and respond accordingly. | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | <div class=" | ||
| + | <div class=" | ||
| + | |||
| + | <div class=" | ||
| + | <input type=" | ||
| + | <button onclick=" | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | class SentimentSupportBot { | ||
| + | constructor() { | ||
| + | this.chatContainer = document.getElementById(' | ||
| + | this.userInput = document.getElementById(' | ||
| + | this.sendButton = document.getElementById(' | ||
| + | this.loading = document.getElementById(' | ||
| + | this.error = document.getElementById(' | ||
| + | this.conversationHistory = []; | ||
| + | } | ||
| + | |||
| + | async analyzeSentiment(text) { | ||
| + | const response = await fetch('/ | ||
| + | method: ' | ||
| + | headers: { | ||
| + | ' | ||
| + | }, | ||
| + | body: JSON.stringify({ text }) | ||
| + | }); | ||
| + | |||
| + | if (!response.ok) { | ||
| + | throw new Error(`Sentiment analysis failed: ${response.status}`); | ||
| + | } | ||
| + | |||
| + | const data = await response.json(); | ||
| + | return data; | ||
| + | } | ||
| + | |||
| + | async generateSupportResponse(userMessage, | ||
| + | const messages = [ | ||
| + | ...this.conversationHistory, | ||
| + | { role: ' | ||
| + | ]; | ||
| + | |||
| + | const response = await fetch('/ | ||
| + | method: ' | ||
| + | headers: { | ||
| + | ' | ||
| + | }, | ||
| + | body: JSON.stringify({ | ||
| + | messages, | ||
| + | sentiment | ||
| + | }) | ||
| + | }); | ||
| + | |||
| + | if (!response.ok) { | ||
| + | throw new Error(`Response generation failed: ${response.status}`); | ||
| + | } | ||
| + | |||
| + | const data = await response.json(); | ||
| + | return data.content; | ||
| + | } | ||
| + | |||
| + | getSystemPrompt(sentiment) { | ||
| + | const sentimentLabel = sentiment.sentiment; | ||
| + | const confidence = sentiment.confidenceScores; | ||
| + | |||
| + | let prompt = `You are an empathetic AI support assistant. The user's message has been analyzed with sentiment: ${sentimentLabel} (positive: ${confidence.positive.toFixed(2)}, | ||
| + | |||
| + | Based on this sentiment analysis, provide appropriate emotional support: | ||
| + | |||
| + | `; | ||
| + | |||
| + | if (sentimentLabel === ' | ||
| + | prompt += `- The user seems to be feeling good. Acknowledge their positive emotions and encourage them to continue. | ||
| + | - Be supportive and celebratory while staying genuine. | ||
| + | - Ask follow-up questions to understand what's going well.`; | ||
| + | } else if (sentimentLabel === ' | ||
| + | prompt += `- The user seems to be struggling or feeling down. Provide compassionate support. | ||
| + | - Validate their feelings and offer comfort. | ||
| + | - Suggest coping strategies or gentle encouragement. | ||
| + | - Be careful not to dismiss their concerns.`; | ||
| + | } else { | ||
| + | prompt += `- The user's sentiment is neutral. Provide balanced support. | ||
| + | - Try to understand more about their situation. | ||
| + | - Offer helpful guidance without making assumptions.`; | ||
| + | } | ||
| + | |||
| + | prompt += `\n\nKeep responses concise (2-3 sentences), warm, and genuinely supportive. Focus on being helpful rather than clinical.`; | ||
| + | |||
| + | return prompt; | ||
| + | } | ||
| + | |||
| + | addMessage(content, | ||
| + | const messageDiv = document.createElement(' | ||
| + | messageDiv.className = `message ${isUser ? ' | ||
| + | | ||
| + | if (!isUser && sentiment) { | ||
| + | const sentimentIndicator = document.createElement(' | ||
| + | sentimentIndicator.className = `sentiment-indicator sentiment-${sentiment.sentiment}`; | ||
| + | sentimentIndicator.textContent = sentiment.sentiment.charAt(0).toUpperCase() + sentiment.sentiment.slice(1); | ||
| + | messageDiv.appendChild(sentimentIndicator); | ||
| + | } | ||
| + | | ||
| + | const contentDiv = document.createElement(' | ||
| + | contentDiv.textContent = content; | ||
| + | messageDiv.appendChild(contentDiv); | ||
| + | | ||
| + | this.chatContainer.appendChild(messageDiv); | ||
| + | this.chatContainer.scrollTop = this.chatContainer.scrollHeight; | ||
| + | } | ||
| + | |||
| + | showError(message) { | ||
| + | this.error.textContent = message; | ||
| + | this.error.style.display = ' | ||
| + | setTimeout(() => { | ||
| + | this.error.style.display = ' | ||
| + | }, 5000); | ||
| + | } | ||
| + | |||
| + | setLoading(isLoading) { | ||
| + | this.loading.style.display = isLoading ? ' | ||
| + | this.sendButton.disabled = isLoading; | ||
| + | this.userInput.disabled = isLoading; | ||
| + | } | ||
| + | |||
| + | async sendMessage() { | ||
| + | const message = this.userInput.value.trim(); | ||
| + | if (!message) return; | ||
| + | |||
| + | this.addMessage(message, | ||
| + | this.userInput.value = ''; | ||
| + | this.setLoading(true); | ||
| + | |||
| + | try { | ||
| + | // Analyze sentiment | ||
| + | const sentimentResult = await this.analyzeSentiment(message); | ||
| + | | ||
| + | // Generate response | ||
| + | const supportResponse = await this.generateSupportResponse(message, | ||
| + | | ||
| + | // Add to conversation history | ||
| + | this.conversationHistory.push( | ||
| + | { role: ' | ||
| + | { role: ' | ||
| + | ); | ||
| + | | ||
| + | // Keep only last 10 messages for context | ||
| + | if (this.conversationHistory.length > 10) { | ||
| + | this.conversationHistory = this.conversationHistory.slice(-10); | ||
| + | } | ||
| + | | ||
| + | this.addMessage(supportResponse, | ||
| + | | ||
| + | } catch (error) { | ||
| + | this.showError(error.message); | ||
| + | } finally { | ||
| + | this.setLoading(false); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Initialize the bot | ||
| + | const bot = new SentimentSupportBot(); | ||
| + | |||
| + | // Global functions for HTML events | ||
| + | function sendMessage() { | ||
| + | bot.sendMessage(); | ||
| + | } | ||
| + | |||
| + | function handleKeyPress(event) { | ||
| + | if (event.key === ' | ||
| + | sendMessage(); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Focus on input when page loads | ||
| + | document.addEventListener(' | ||
| + | document.getElementById(' | ||
| + | }); | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | </ | ||
| + | |||
| + | |||
| + | staticwebapp.config.json | ||
| + | |||
| + | < | ||
| + | |||
| + | { | ||
| + | " | ||
| + | { | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | }, | ||
| + | { | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | } | ||
| + | ], | ||
| + | " | ||
| + | " | ||
| + | }, | ||
| + | " | ||
| + | " | ||
| + | }, | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | Key points to note: | ||
| + | 1. do not use function.json for Azure functions v4 | ||
| + | 2. check the build and deploy step to verify function is created. | ||