← back to Designer Wallcoverings
DW-Agents/dw-agents/agent-parallel-processes/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' });
}
}