|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Code Assistant Agent</title> |
|
|
<style> |
|
|
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f0f2f5; color: #333; } |
|
|
.container { max-width: 850px; margin: auto; background: #fff; padding: 30px; border-radius: 12px; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1); } |
|
|
h1 { color: #1f7cff; text-align: center; margin-bottom: 25px; font-weight: 600; } |
|
|
#chat-window { |
|
|
height: 400px; |
|
|
overflow-y: scroll; |
|
|
border: 1px solid #ddd; |
|
|
padding: 15px; |
|
|
border-radius: 8px; |
|
|
margin-bottom: 20px; |
|
|
background-color: #e9eff4; |
|
|
} |
|
|
|
|
|
|
|
|
.message-row { |
|
|
display: flex; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
.message { |
|
|
padding: 10px 15px; |
|
|
border-radius: 18px; |
|
|
max-width: 75%; |
|
|
position: relative; |
|
|
word-wrap: break-word; |
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); |
|
|
} |
|
|
.user { |
|
|
background-color: #1f7cff; |
|
|
color: white; |
|
|
margin-left: auto; |
|
|
border-bottom-right-radius: 4px; |
|
|
} |
|
|
.assistant { |
|
|
background-color: #ffffff; |
|
|
border: 1px solid #c9d6de; |
|
|
color: #333; |
|
|
margin-right: auto; |
|
|
border-bottom-left-radius: 4px; |
|
|
text-align: left; |
|
|
} |
|
|
|
|
|
#input-container { display: flex; gap: 10px; margin-bottom: 20px; } |
|
|
#user-input { flex-grow: 1; padding: 12px; border: 1px solid #ccc; border-radius: 6px; } |
|
|
#send-button { padding: 10px 20px; background-color: #1f7cff; color: white; border: none; border-radius: 6px; cursor: pointer; transition: background-color 0.3s; } |
|
|
#send-button:hover { background-color: #165bb0; } |
|
|
|
|
|
#state-info { padding: 15px; background-color: #e6f0ff; border-left: 5px solid #1f7cff; border-radius: 4px; font-size: 0.9em; margin-bottom: 25px; display: flex; justify-content: space-between; align-items: center; } |
|
|
|
|
|
pre { |
|
|
background-color: #272822; |
|
|
color: #f8f8f2; |
|
|
padding: 10px; |
|
|
border-radius: 4px; |
|
|
overflow-x: auto; |
|
|
margin-top: 10px; |
|
|
font-family: 'Consolas', 'Monaco', monospace; |
|
|
font-size: 0.9em; |
|
|
} |
|
|
|
|
|
.tag-area { |
|
|
margin-top: 10px; |
|
|
padding-top: 5px; |
|
|
border-top: 1px dashed #e0e0e0; |
|
|
font-size: 0.8em; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
flex-wrap: wrap; |
|
|
} |
|
|
.tag-area input[type="text"] { |
|
|
flex-grow: 1; |
|
|
padding: 4px; |
|
|
border: 1px solid #ccc; |
|
|
border-radius: 4px; |
|
|
margin-right: 5px; |
|
|
min-width: 150px; |
|
|
} |
|
|
|
|
|
.tag-chip { display: inline-block; background-color: #6c757d; color: white; padding: 3px 10px; border-radius: 16px; font-size: 0.75em; margin-right: 5px; margin-bottom: 5px; } |
|
|
.bookmark-btn { background-color: #28a745; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; transition: background-color 0.3s; font-size: 0.8em; } |
|
|
.bookmark-btn:hover { background-color: #1e7e34; } |
|
|
|
|
|
|
|
|
h2 { border-bottom: 2px solid #ddd; padding-bottom: 5px; margin-top: 30px; } |
|
|
#tag-search { padding: 10px; border: 1px solid #ccc; border-radius: 4px; width: 100%; margin-bottom: 15px; } |
|
|
.tagged-replies-list { list-style: none; padding: 0; } |
|
|
.tagged-replies-list li { border: 1px solid #f0f0f0; background: #fafafa; padding: 15px; border-radius: 6px; margin-bottom: 10px; } |
|
|
.tagged-replies-list li p { margin: 0 0 5px 0; } |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="container"> |
|
|
<h1>Code Assistant Agent 🤖</h1> |
|
|
|
|
|
<div id="state-info"> |
|
|
Current Language: <span id="current-language">Python</span> |
|
|
| Total Bookmarked Replies: <span id="tagged-count">0</span> |
|
|
</div> |
|
|
|
|
|
<div id="chat-window"> |
|
|
</div> |
|
|
|
|
|
<div id="input-container"> |
|
|
<input type="text" id="user-input" placeholder="Ask about code, concepts, or bugs..." onkeypress="if(event.key === 'Enter') sendMessage()"> |
|
|
<button id="send-button" onclick="sendMessage()">Send</button> |
|
|
</div> |
|
|
|
|
|
<h2>Bookmarked Replies</h2> |
|
|
<input type="text" id="tag-search" placeholder="Search by Tag (e.g., Python, Debugging)" oninput="filterReplies()"> |
|
|
<ul id="tagged-replies-display" class="tagged-replies-list"> |
|
|
</ul> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
let chatHistory = []; |
|
|
|
|
|
|
|
|
let assistantState = { |
|
|
conversationSummary: "", |
|
|
language: "Python", |
|
|
taggedReplies: [] |
|
|
}; |
|
|
const apiUrl = '/chat'; |
|
|
const tagUrl = '/tag_reply'; |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
updateStateInfo(); |
|
|
|
|
|
appendMessage({ role: 'assistant', content: "Hello! I'm your Code Assistant. What programming language are you working in today?" }); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
async function sendMessage() { |
|
|
const inputElement = document.getElementById('user-input'); |
|
|
const userMessage = inputElement.value.trim(); |
|
|
|
|
|
if (!userMessage) return; |
|
|
|
|
|
|
|
|
const userMsgObj = { role: 'user', content: userMessage }; |
|
|
chatHistory.push(userMsgObj); |
|
|
|
|
|
appendMessage(userMsgObj); |
|
|
inputElement.value = ''; |
|
|
|
|
|
try { |
|
|
const response = await fetch(apiUrl, { |
|
|
method: 'POST', |
|
|
headers: { 'Content-Type': 'application/json' }, |
|
|
body: JSON.stringify({ |
|
|
|
|
|
chat_history: chatHistory, |
|
|
assistant_state: assistantState |
|
|
}) |
|
|
}); |
|
|
|
|
|
if (!response.ok) { |
|
|
|
|
|
chatHistory.pop(); |
|
|
throw new Error(`HTTP error! status: ${response.status}`); |
|
|
} |
|
|
|
|
|
const data = await response.json(); |
|
|
|
|
|
|
|
|
assistantState = data.updated_state; |
|
|
|
|
|
|
|
|
const assistantMsgObj = { |
|
|
role: 'assistant', |
|
|
content: data.assistant_reply |
|
|
}; |
|
|
chatHistory.push(assistantMsgObj); |
|
|
|
|
|
|
|
|
appendMessage({ |
|
|
role: 'assistant', |
|
|
content: data.assistant_reply, |
|
|
suggestedTags: data.suggested_tags |
|
|
}); |
|
|
|
|
|
updateStateInfo(); |
|
|
|
|
|
} catch (error) { |
|
|
console.error('Chat failed:', error); |
|
|
appendMessage({ role: 'assistant', content: `Error: Could not connect to the assistant. (${error.message})` }); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function appendMessage(msg) { |
|
|
const chatWindow = document.getElementById('chat-window'); |
|
|
const messageRow = document.createElement('div'); |
|
|
messageRow.className = 'message-row'; |
|
|
|
|
|
const msgDiv = document.createElement('div'); |
|
|
msgDiv.className = `message ${msg.role}`; |
|
|
|
|
|
let content = msg.content; |
|
|
|
|
|
|
|
|
content = content.replace(/```(\w+)?\n([\s\S]*?)\n```/g, (match, lang, code) => { |
|
|
const language = lang || 'code'; |
|
|
return `<pre><code class="language-${language}">${code.trim()}</code></pre>`; |
|
|
}); |
|
|
|
|
|
msgDiv.innerHTML = content; |
|
|
messageRow.appendChild(msgDiv); |
|
|
chatWindow.appendChild(messageRow); |
|
|
|
|
|
|
|
|
|
|
|
if (msg.role === 'assistant') { |
|
|
const tagArea = document.createElement('div'); |
|
|
tagArea.className = 'tag-area'; |
|
|
tagArea.innerHTML = 'Tags: '; |
|
|
|
|
|
const tagInput = document.createElement('input'); |
|
|
tagInput.type = 'text'; |
|
|
tagInput.placeholder = 'Add your tags (comma-separated)'; |
|
|
tagInput.id = 'tag-input-' + Date.now(); |
|
|
tagInput.style.marginRight = '10px'; |
|
|
tagInput.value = (msg.suggestedTags || []).join(', '); |
|
|
|
|
|
const bookmarkBtn = document.createElement('button'); |
|
|
bookmarkBtn.className = 'bookmark-btn'; |
|
|
bookmarkBtn.textContent = 'Save/Bookmark'; |
|
|
bookmarkBtn.onclick = () => saveReply(msg.content, tagInput.value, bookmarkBtn); |
|
|
|
|
|
tagArea.appendChild(tagInput); |
|
|
tagArea.appendChild(bookmarkBtn); |
|
|
msgDiv.appendChild(tagArea); |
|
|
} |
|
|
|
|
|
chatWindow.scrollTop = chatWindow.scrollHeight; |
|
|
} |
|
|
|
|
|
function updateStateInfo() { |
|
|
document.getElementById('current-language').textContent = assistantState.language; |
|
|
document.getElementById('tagged-count').textContent = assistantState.taggedReplies.length; |
|
|
filterReplies(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async function saveReply(replyContent, tagsString, buttonElement) { |
|
|
const tags = tagsString.split(',').map(tag => tag.trim()).filter(tag => tag.length > 0); |
|
|
|
|
|
if (tags.length === 0) { |
|
|
alert("Please enter at least one tag."); |
|
|
return; |
|
|
} |
|
|
|
|
|
try { |
|
|
const response = await fetch(tagUrl, { |
|
|
method: 'POST', |
|
|
headers: { 'Content-Type': 'application/json' }, |
|
|
body: JSON.stringify({ |
|
|
reply: replyContent, |
|
|
tags: tags, |
|
|
assistant_state: assistantState |
|
|
}) |
|
|
}); |
|
|
|
|
|
if (!response.ok) { |
|
|
throw new Error(`HTTP error! status: ${response.status}`); |
|
|
} |
|
|
|
|
|
const data = await response.json(); |
|
|
assistantState = data.updated_state; |
|
|
|
|
|
alert(data.message); |
|
|
buttonElement.textContent = 'Saved!'; |
|
|
buttonElement.disabled = true; |
|
|
updateStateInfo(); |
|
|
|
|
|
} catch (error) { |
|
|
console.error('Tagging failed:', error); |
|
|
alert(`Failed to save reply: ${error.message}`); |
|
|
} |
|
|
} |
|
|
|
|
|
function filterReplies() { |
|
|
const query = document.getElementById('tag-search').value.toLowerCase(); |
|
|
const displayList = document.getElementById('tagged-replies-display'); |
|
|
displayList.innerHTML = ''; |
|
|
|
|
|
const filtered = assistantState.taggedReplies.filter(item => { |
|
|
if (!query) return true; |
|
|
return item.tags.some(tag => tag.toLowerCase().includes(query)); |
|
|
}); |
|
|
|
|
|
if (filtered.length === 0 && query) { |
|
|
displayList.innerHTML = '<li>No bookmarked replies match your search tag.</li>'; |
|
|
return; |
|
|
} |
|
|
|
|
|
filtered.forEach(item => { |
|
|
const li = document.createElement('li'); |
|
|
let tagChips = item.tags.map(tag => `<span class="tag-chip">${tag}</span>`).join(''); |
|
|
|
|
|
|
|
|
li.innerHTML = ` |
|
|
<p style="font-weight: bold;">${item.reply.substring(0, 150)}...</p> |
|
|
<p style="font-size: 0.9em;">${tagChips}</p> |
|
|
`; |
|
|
displayList.appendChild(li); |
|
|
}); |
|
|
} |
|
|
</script> |
|
|
</body> |
|
|
</html> |