mirror of
https://github.com/awslabs/amazon-bedrock-agentcore-samples.git
synced 2025-09-08 20:50:46 +00:00
* updated README.md file with bearer token generation * updated README.md file with bearer token generation-removed client id and secret credentials * removed hardcoded domain * added agent runtime, frontend, observability and agentcore identity * update README.md file to reflect frontend testing
354 lines
13 KiB
HTML
354 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Device Management Assistant</title>
|
|
<link rel="stylesheet" href="{{ url_for('static', path='/css/styles.css') }}">
|
|
<style>
|
|
.user-info {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 10px 15px;
|
|
background-color: rgba(255, 255, 255, 0.1);
|
|
border-radius: 4px;
|
|
margin-bottom: 20px;
|
|
font-size: 14px;
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
}
|
|
.user-name {
|
|
font-weight: bold;
|
|
color: white;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
max-width: 120px;
|
|
}
|
|
.logout-btn {
|
|
background-color: #ff9900;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
padding: 5px 10px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
white-space: nowrap;
|
|
}
|
|
.logout-btn:hover {
|
|
background-color: #e88a00;
|
|
}
|
|
.typing-indicator {
|
|
display: inline-block;
|
|
margin-left: 5px;
|
|
}
|
|
.typing-indicator span {
|
|
display: inline-block;
|
|
width: 6px;
|
|
height: 6px;
|
|
background-color: #888;
|
|
border-radius: 50%;
|
|
margin: 0 2px;
|
|
animation: typing 1.4s infinite both;
|
|
}
|
|
.typing-indicator span:nth-child(2) {
|
|
animation-delay: 0.2s;
|
|
}
|
|
.typing-indicator span:nth-child(3) {
|
|
animation-delay: 0.4s;
|
|
}
|
|
@keyframes typing {
|
|
0% { transform: translateY(0); }
|
|
50% { transform: translateY(-5px); }
|
|
100% { transform: translateY(0); }
|
|
}
|
|
.device-item {
|
|
background-color: rgba(0, 123, 255, 0.1);
|
|
border-left: 3px solid #007bff;
|
|
padding: 8px 12px;
|
|
margin: 5px 0;
|
|
border-radius: 4px;
|
|
}
|
|
.device-item strong {
|
|
color: #007bff;
|
|
}
|
|
pre {
|
|
background-color: #f8f9fa;
|
|
border: 1px solid #e9ecef;
|
|
border-radius: 4px;
|
|
padding: 12px;
|
|
overflow-x: auto;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.9em;
|
|
}
|
|
pre.json {
|
|
background-color: #f8f9fa;
|
|
border-left: 3px solid #28a745;
|
|
}
|
|
code {
|
|
background-color: #f8f9fa;
|
|
padding: 2px 4px;
|
|
border-radius: 3px;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.9em;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="sidebar">
|
|
<h2>Device Management</h2>
|
|
<div class="user-info">
|
|
<span class="user-name" title="{{ user.username if user.username else (user.name if user.name else user.email) }}">
|
|
{{ user.username if user.username else (user.name if user.name else user.email) }}
|
|
</span>
|
|
<a href="/logout"><button class="logout-btn">Logout</button></a>
|
|
</div>
|
|
<div class="menu">
|
|
<div class="menu-item active">Chat Assistant</div>
|
|
<div class="menu-item">About</div>
|
|
</div>
|
|
<div class="info-box">
|
|
<h3>Example Commands</h3>
|
|
<ul>
|
|
<li>List all devices</li>
|
|
<li>Show settings for device DEV001</li>
|
|
<li>List WiFi networks for Living Room Router</li>
|
|
<li>Update WiFi SSID to MyNewNetwork on device DEV001</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="main-content">
|
|
<div class="chat-container">
|
|
<div class="chat-header">
|
|
<h2>Device Management Assistant</h2>
|
|
</div>
|
|
<div class="chat-messages" id="chat-messages">
|
|
<div class="message system">
|
|
<div class="message-content">
|
|
<p>👋 Welcome to the Device Management Assistant!</p>
|
|
<p>I can help you manage your connected devices, check their status, and update settings. Try asking me about your devices or specific settings.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="chat-input">
|
|
<textarea id="user-input" placeholder="Type your message here..." rows="1"></textarea>
|
|
<button id="send-button">Send</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const chatMessages = document.getElementById('chat-messages');
|
|
const userInput = document.getElementById('user-input');
|
|
const sendButton = document.getElementById('send-button');
|
|
|
|
// Generate a unique client ID for this session
|
|
const clientId = Date.now() + Math.random().toString(36).substring(2);
|
|
|
|
// WebSocket connection
|
|
const ws = new WebSocket(`ws://${window.location.host}/ws/${clientId}`);
|
|
|
|
// Track current response message element for streaming
|
|
let currentResponseElement = null;
|
|
let currentResponseContent = '';
|
|
|
|
// Handle WebSocket events
|
|
ws.onopen = () => {
|
|
console.log('WebSocket connection established');
|
|
};
|
|
|
|
ws.onmessage = (event) => {
|
|
try {
|
|
const data = JSON.parse(event.data);
|
|
|
|
if (data.error) {
|
|
// Display error message
|
|
removeTypingIndicator();
|
|
addMessage('error', data.error);
|
|
currentResponseElement = null;
|
|
currentResponseContent = '';
|
|
} else if (data.complete || data.response || data.final_response) {
|
|
// Response is complete - display the final result
|
|
removeTypingIndicator();
|
|
|
|
let responseText = '';
|
|
if (data.final_response) {
|
|
responseText = data.final_response;
|
|
} else if (data.response) {
|
|
responseText = data.response;
|
|
} else if (currentResponseContent) {
|
|
responseText = currentResponseContent;
|
|
}
|
|
|
|
if (responseText) {
|
|
addMessage('assistant', responseText);
|
|
} else {
|
|
addMessage('error', 'No response content received');
|
|
}
|
|
|
|
// Reset streaming state
|
|
currentResponseElement = null;
|
|
currentResponseContent = '';
|
|
} else if (data.status) {
|
|
// Display status message
|
|
addMessage('system', data.status);
|
|
} else {
|
|
// Log unexpected data format for debugging
|
|
console.log('Unexpected message format:', data);
|
|
}
|
|
|
|
// Scroll to bottom
|
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
|
|
} catch (error) {
|
|
console.error('Error parsing WebSocket message:', error);
|
|
console.log('Raw message:', event.data);
|
|
removeTypingIndicator();
|
|
addMessage('error', 'Error processing server response');
|
|
}
|
|
};
|
|
|
|
ws.onclose = () => {
|
|
console.log('WebSocket connection closed');
|
|
addMessage('system', 'Connection closed. Please refresh the page to reconnect.');
|
|
};
|
|
|
|
ws.onerror = (error) => {
|
|
console.error('WebSocket error:', error);
|
|
addMessage('error', 'Connection error. Please refresh the page to try again.');
|
|
};
|
|
|
|
// Send message when button is clicked
|
|
sendButton.addEventListener('click', sendMessage);
|
|
|
|
// Send message when Enter key is pressed (but allow Shift+Enter for new lines)
|
|
userInput.addEventListener('keydown', (event) => {
|
|
if (event.key === 'Enter' && !event.shiftKey) {
|
|
event.preventDefault();
|
|
sendMessage();
|
|
}
|
|
});
|
|
|
|
// Auto-resize textarea as user types
|
|
userInput.addEventListener('input', () => {
|
|
userInput.style.height = 'auto';
|
|
userInput.style.height = (userInput.scrollHeight) + 'px';
|
|
});
|
|
|
|
function sendMessage() {
|
|
const message = userInput.value.trim();
|
|
if (!message) return;
|
|
|
|
// Add user message to chat
|
|
addMessage('user', message);
|
|
|
|
// Add typing indicator
|
|
addTypingIndicator();
|
|
|
|
// Send message to server
|
|
ws.send(message);
|
|
|
|
// Clear input
|
|
userInput.value = '';
|
|
userInput.style.height = 'auto';
|
|
|
|
// Reset current response tracking
|
|
currentResponseElement = null;
|
|
currentResponseContent = '';
|
|
|
|
// Scroll to bottom
|
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
}
|
|
|
|
function addMessage(role, content) {
|
|
const messageDiv = document.createElement('div');
|
|
messageDiv.className = `message ${role}`;
|
|
|
|
const contentDiv = document.createElement('div');
|
|
contentDiv.className = 'message-content';
|
|
|
|
// Format the content
|
|
contentDiv.innerHTML = formatContent(content);
|
|
|
|
messageDiv.appendChild(contentDiv);
|
|
chatMessages.appendChild(messageDiv);
|
|
}
|
|
|
|
function formatContent(content) {
|
|
// Process markdown-like formatting
|
|
let formattedContent = content
|
|
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>') // Bold
|
|
.replace(/\*(.*?)\*/g, '<em>$1</em>') // Italic
|
|
.replace(/`([^`]+)`/g, '<code>$1</code>') // Inline code
|
|
.replace(/\n\n/g, '<br><br>') // Double line breaks
|
|
.replace(/\n/g, '<br>'); // Single line breaks
|
|
|
|
// Handle code blocks
|
|
if (formattedContent.includes('```')) {
|
|
const parts = formattedContent.split('```');
|
|
formattedContent = '';
|
|
|
|
for (let i = 0; i < parts.length; i++) {
|
|
if (i % 2 === 0) {
|
|
// Regular text
|
|
formattedContent += parts[i];
|
|
} else {
|
|
// Code block
|
|
let codeContent = parts[i];
|
|
let language = '';
|
|
|
|
// Check if language is specified
|
|
if (codeContent.includes('<br>')) {
|
|
const lines = codeContent.split('<br>');
|
|
language = lines[0].trim();
|
|
codeContent = lines.slice(1).join('<br>').trim();
|
|
}
|
|
|
|
// Add JSON formatting for JSON code blocks
|
|
const codeClass = language.toLowerCase() === 'json' ? 'json' : '';
|
|
formattedContent += `<pre class="${codeClass}"><code>${codeContent}</code></pre>`;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle device list formatting with better styling
|
|
if (formattedContent.includes('📱') || formattedContent.includes('Device List')) {
|
|
formattedContent = formattedContent.replace(/\*\*(\d+\.\s.*?)\*\*/g, '<div class="device-item"><strong>$1</strong></div>');
|
|
}
|
|
|
|
return formattedContent;
|
|
}
|
|
|
|
function addTypingIndicator() {
|
|
// Remove any existing typing indicator
|
|
removeTypingIndicator();
|
|
|
|
// Create typing indicator
|
|
const typingDiv = document.createElement('div');
|
|
typingDiv.className = 'message assistant typing-message';
|
|
typingDiv.innerHTML = `
|
|
<div class="message-content">
|
|
Thinking<div class="typing-indicator">
|
|
<span></span><span></span><span></span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
chatMessages.appendChild(typingDiv);
|
|
|
|
// Scroll to bottom
|
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
}
|
|
|
|
function removeTypingIndicator() {
|
|
const typingIndicator = document.querySelector('.typing-message');
|
|
if (typingIndicator) {
|
|
typingIndicator.remove();
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|