← back to Designer Wallcoverings

DW-Agents/dw-agents/agent-marketing/shared-chat.ts

175 lines

/**
 * Shared Chat Interface for All DW-Agents
 *
 * Provides a universal chat interface that can be embedded in any agent
 * Uses Claude AI to answer questions about the specific agent's domain
 */

import Anthropic from '@anthropic-ai/sdk';
import { Request, Response } from 'express';

const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
});

// Chat interface HTML and CSS to inject into pages
export const CHAT_INTERFACE_HTML = `
<!-- Universal Agent Chat -->
<div id="universalChat" style="position: fixed; top: 20px; right: 20px; z-index: 10000; display: none;">
  <div style="background: white; width: 400px; max-height: 600px; border-radius: 16px; box-shadow: 0 10px 40px rgba(0,0,0,0.3); display: flex; flex-direction: column; overflow: hidden;">
    <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; display: flex; justify-content: space-between; align-items: center;">
      <div>
        <div style="font-weight: 600; font-size: 1.1em;">💬 Agent Assistant</div>
        <div style="font-size: 0.85em; opacity: 0.9;">Ask me anything about this agent</div>
      </div>
      <button onclick="toggleUniversalChat()" style="background: rgba(255,255,255,0.2); border: none; color: white; width: 32px; height: 32px; border-radius: 50%; cursor: pointer; font-size: 1.2em;">×</button>
    </div>
    <div id="chatMessages" style="flex: 1; overflow-y: auto; padding: 20px; background: #f8f9fa; max-height: 400px;">
      <div style="text-align: center; color: #888; padding: 20px;">
        👋 Hi! I'm your AI assistant for this agent.<br>
        Ask me anything!
      </div>
    </div>
    <div style="padding: 15px; border-top: 1px solid #e0e0e0; background: white;">
      <div style="display: flex; gap: 10px;">
        <input
          type="text"
          id="chatInput"
          placeholder="Type your question..."
          style="flex: 1; padding: 12px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 1em;"
          onkeypress="if(event.key==='Enter') sendUniversalChat()"
        >
        <button
          onclick="sendUniversalChat()"
          id="chatSendBtn"
          style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 12px 20px; border-radius: 8px; cursor: pointer; font-weight: 600; white-space: nowrap;"
        >
          Send
        </button>
      </div>
    </div>
  </div>
</div>

<button
  onclick="toggleUniversalChat()"
  id="chatToggleBtn"
  style="position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 12px 24px; border-radius: 25px; cursor: pointer; font-weight: 600; box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4); z-index: 9999; display: flex; align-items: center; gap: 8px; transition: transform 0.2s;"
  onmouseover="this.style.transform='scale(1.05)'"
  onmouseout="this.style.transform='scale(1)'"
>
  💬 Chat
</button>

<script>
function toggleUniversalChat() {
  const chat = document.getElementById('universalChat');
  const btn = document.getElementById('chatToggleBtn');

  if (chat.style.display === 'none') {
    chat.style.display = 'block';
    btn.style.display = 'none';
  } else {
    chat.style.display = 'none';
    btn.style.display = 'flex';
  }
}

async function sendUniversalChat() {
  const input = document.getElementById('chatInput');
  const messages = document.getElementById('chatMessages');
  const sendBtn = document.getElementById('chatSendBtn');
  const message = input.value.trim();

  if (!message) return;

  // Add user message
  const userMsg = document.createElement('div');
  userMsg.style = 'background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px 16px; border-radius: 12px; margin-bottom: 10px; max-width: 80%; margin-left: auto; text-align: right;';
  userMsg.textContent = message;
  messages.appendChild(userMsg);

  input.value = '';
  messages.scrollTop = messages.scrollHeight;

  // Disable send button
  sendBtn.disabled = true;
  sendBtn.textContent = 'Thinking...';

  try {
    const response = await fetch('/api/chat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ message })
    });

    const data = await response.json();

    // Add assistant message
    const assistantMsg = document.createElement('div');
    assistantMsg.style = 'background: white; color: #333; padding: 12px 16px; border-radius: 12px; margin-bottom: 10px; max-width: 80%; border: 1px solid #e0e0e0;';
    assistantMsg.textContent = data.response || 'Sorry, I encountered an error.';
    messages.appendChild(assistantMsg);

    messages.scrollTop = messages.scrollHeight;
  } catch (error) {
    const errorMsg = document.createElement('div');
    errorMsg.style = 'background: #fee; color: #c33; padding: 12px 16px; border-radius: 12px; margin-bottom: 10px; max-width: 80%; border: 1px solid #fcc;';
    errorMsg.textContent = 'Sorry, I encountered an error. Please try again.';
    messages.appendChild(errorMsg);
  } finally {
    sendBtn.disabled = false;
    sendBtn.textContent = 'Send';
  }
}
</script>
`;

// Express route handler for chat endpoint
export async function handleChatRequest(req: Request, res: Response, agentContext: string) {
  try {
    const { message } = req.body;

    if (!message) {
      return res.status(400).json({ error: 'Message is required' });
    }

    // Initialize chat history if not exists
    if (!req.session.chatHistory) {
      req.session.chatHistory = [];
    }

    // Add user message to history
    req.session.chatHistory.push({
      role: 'user',
      content: message
    });

    // Keep only last 10 messages
    if (req.session.chatHistory.length > 10) {
      req.session.chatHistory = req.session.chatHistory.slice(-10);
    }

    // Call Claude API
    const response = await anthropic.messages.create({
      model: 'claude-3-5-sonnet-20241022',
      max_tokens: 1024,
      system: agentContext,
      messages: req.session.chatHistory
    });

    const assistantMessage = response.content[0].type === 'text' ? response.content[0].text : 'I apologize, I could not process that request.';

    // Add assistant response to history
    req.session.chatHistory.push({
      role: 'assistant',
      content: assistantMessage
    });

    res.json({ response: assistantMessage });
  } catch (error) {
    console.error('Chat error:', error);
    res.status(500).json({ error: 'Failed to process chat message' });
  }
}