Spaces:
Build error
Build error
import streamlit as st | |
import requests | |
import json | |
import time | |
from streamlit.components.v1 import html | |
# Initialize session state for chat history | |
if "messages" not in st.session_state: | |
st.session_state.messages = [] | |
# Hugging Face API configuration | |
API_URL = "https://huggingface.co/spaces/iyashnayi/SocioLens-llama-3.2-3B" | |
HF_TOKEN = st.secrets.get("HUGGINGFACE_API_KEY") | |
HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"} | |
# Function to query the SocioLens-llama-3.2-3B model | |
def query_model(prompt): | |
try: | |
payload = { | |
"inputs": prompt, | |
"parameters": { | |
"max_new_tokens": 500, | |
"temperature": 0.01, | |
"top_p": 0.9, | |
"do_sample": True, | |
"return_full_text": False | |
} | |
} | |
response = requests.post(API_URL, headers=HEADERS, json=payload) | |
response.raise_for_status() | |
result = response.json() | |
return result[0]["generated_text"] if isinstance(result, list) else result.get("generated_text", "Error: No response") | |
except Exception as e: | |
return f"Error: {str(e)}" | |
# Check for Hugging Face API key | |
if not HF_TOKEN: | |
st.error("Please set your Hugging Face API key in Streamlit secrets (HUGGINGFACE_API_KEY).") | |
st.stop() | |
# HTML content for the chat interface (unchanged) | |
html_content = """ | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>SocioLens Chat Interface</title> | |
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
<style> | |
body { | |
margin: 0; | |
padding: 0; | |
font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; | |
background: linear-gradient(35deg, #0D151D, #091933); | |
color: #ffffff; | |
display: flex; | |
height: 100vh; | |
} | |
.header { | |
padding: 15px 20px; | |
border-radius: 10px; | |
background-color: rgba(105, 104, 104, 0.2); | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
} | |
.header-title, .separator, .model-name { | |
font-size: 16px; | |
font-weight: 500; | |
} | |
.separator { | |
margin: 0 10px; | |
color: #e7e4e4; | |
font-size: 20px; | |
} | |
.model-name { | |
color: #b6b4b4; | |
font-size: 14px; | |
} | |
.theme-manager .toggle-switch { | |
position: relative; | |
display: inline-block; | |
width: 50px; | |
height: 24px; | |
} | |
.theme-manager .toggle-switch input { | |
opacity: 0; | |
width: 0; | |
height: 0; | |
} | |
.theme-manager .toggle-switch .slider { | |
position: absolute; | |
cursor: pointer; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
background-color: #fff; | |
transition: 0.4s; | |
border-radius: 24px; | |
} | |
.theme-manager .toggle-switch .slider:before { | |
position: absolute; | |
content: ""; | |
height: 20px; | |
width: 20px; | |
left: 2px; | |
bottom: 2px; | |
background-color: #0D151D; | |
transition: 0.4s; | |
border-radius: 50%; | |
} | |
.theme-manager .toggle-switch input:checked+.slider { | |
background-color: #ccc; | |
} | |
.theme-manager .toggle-switch input:checked+.slider:before { | |
transform: translateX(26px); | |
background-color: #000; | |
} | |
.sidebar { | |
width: 270px; | |
margin: 10px; | |
border-radius: 10px; | |
background-color: rgba(105, 104, 104, 0.2); | |
padding: 20px 0; | |
display: flex; | |
flex-direction: column; | |
} | |
.user-profile { | |
display: flex; | |
align-items: center; | |
padding: 10px 20px; | |
margin-bottom: 20px; | |
} | |
.avatar { | |
width: 40px; | |
height: 40px; | |
border-radius: 50%; | |
margin-right: 15px; | |
background-color: #333; | |
overflow: hidden; | |
} | |
.avatar img { | |
width: 100%; | |
height: 100%; | |
object-fit: cover; | |
} | |
.chat-list { | |
flex: 1; | |
} | |
.chat-item { | |
padding: 10px 20px; | |
margin: 10px; | |
cursor: pointer; | |
border-radius: 5px; | |
display: flex; | |
align-items: center; | |
gap: 8px; | |
} | |
.chat-item:hover, .chat-item.active { | |
margin: 10px; | |
background-color: rgba(175, 175, 175, 0.2); | |
} | |
.header-right { | |
display: flex; | |
gap: 10px; | |
align-items: center; | |
} | |
.model-info { | |
position: relative; | |
display: inline-block; | |
} | |
.info-button { | |
background: none; | |
border: none; | |
border-radius: 50%; | |
color: #fff; | |
padding: 4px; | |
cursor: pointer; | |
font-size: 20px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
} | |
.info-tooltip { | |
visibility: hidden; | |
opacity: 0; | |
width: 220px; | |
background-color: #fff; | |
color: #000; | |
text-align: center; | |
border-radius: 6px; | |
padding: 5px; | |
position: absolute; | |
z-index: 1; | |
top: 125%; | |
left: 50%; | |
transform: translateX(-90%); | |
transition: opacity 0.3s; | |
font-size: 12px; | |
} | |
.model-info:hover .info-tooltip { | |
visibility: visible; | |
opacity: 1; | |
} | |
.main-content { | |
flex: 1; | |
margin: 10px 10px 10px 0; | |
border-radius: 10px; | |
display: flex; | |
flex-direction: column; | |
} | |
.chat-area { | |
flex: 1; | |
padding: 20px; | |
overflow-y: auto; | |
display: flex; | |
flex-direction: column; | |
justify-content: center; | |
align-items: center; | |
text-align: center; | |
} | |
.chat-conversation { | |
display: flex; | |
flex-direction: column; | |
gap: 16px; | |
width: 100%; | |
max-width: 900px; | |
margin: 0 auto; | |
} | |
.message { | |
display: flex; | |
align-items: center; | |
} | |
.message.user { | |
justify-content: flex-end; | |
} | |
.message.bot { | |
justify-content: flex-start; | |
} | |
.bubble { | |
max-width: 95%; | |
padding: 12px 16px; | |
border-radius: 20px; | |
} | |
.message.user .bubble { | |
background: #007aff; | |
color: #fff; | |
} | |
.message.bot .bubble { | |
background: rgba(105, 104, 104, 0.2); | |
text-align: start; | |
color: #fff; | |
width: 100%; | |
} | |
.welcome-message { | |
max-width: 500px; | |
} | |
.welcome-message h2 { | |
font-size: 24px; | |
margin-bottom: 10px; | |
font-weight: 400; | |
background: linear-gradient(90deg, #ffffff, #6f6f7b, #ffffff); | |
background-size: 200% auto; | |
background-clip: text; | |
-webkit-background-clip: text; | |
color: transparent; | |
animation: shimmer 3s linear infinite; | |
} | |
.welcome-message h1 { | |
font-size: 28px; | |
margin-top: 0; | |
} | |
@keyframes shimmer { | |
0% { background-position: -200% center; } | |
100% { background-position: 200% center; } | |
} | |
.input-area { | |
padding: 20px; | |
display: flex; | |
align-items: center; | |
} | |
.message-input { | |
width: 80%; | |
margin: auto; | |
background-color: rgba(105, 104, 104, 0.2); | |
border-radius: 12px; | |
padding: 15px; | |
display: flex; | |
flex-direction: column; | |
gap: 10px; | |
} | |
.message-input textarea { | |
width: 100%; | |
background: transparent; | |
border: none; | |
color: #fff; | |
font-size: 16px; | |
resize: vertical; | |
outline: none; | |
min-height: 40px; | |
} | |
.icon-row { | |
display: flex; | |
align-items: center; | |
justify-content: space-between; | |
} | |
.icon-left { | |
display: flex; | |
align-items: center; | |
gap: 10px; | |
} | |
.attachment-btn, .plus-btn, .send-button { | |
cursor: pointer; | |
font-size: 20px; | |
color: #fff; | |
background: none; | |
border: none; | |
outline: none; | |
} | |
.attachment-btn { | |
margin-right: 8px; | |
font-size: 24px; | |
} | |
.plus-btn { | |
margin-right: 8px; | |
width: 24px; | |
height: 24px; | |
border-radius: 4px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
font-size: 24px; | |
} | |
.send-button { | |
font-size: 25px; | |
} | |
.white-theme { | |
background: linear-gradient(35deg, #ffffff, #f0f0f0) !important; | |
color: #000 !important; | |
} | |
.white-theme .header, .white-theme .sidebar, .white-theme .message-input { | |
background-color: rgba(228, 228, 229, 0.8) !important; | |
} | |
.white-theme .chat-item:hover, .white-theme .chat-item.active { | |
background-color: rgba(200, 200, 200, 0.4) !important; | |
} | |
.white-theme .send-button, .white-theme .info-button, .white-theme .attachment-btn, .white-theme .plus-btn { | |
color: #000 !important; | |
} | |
.white-theme .info-tooltip { | |
background-color: rgba(0, 0, 0, 0.8); | |
color: #fff; | |
} | |
.white-theme .bubble { | |
color: #000 !important; | |
} | |
.white-theme .message.user .bubble { | |
background: #007aff; | |
color: #fff !important; | |
} | |
.white-theme .message.bot .bubble { | |
background: rgba(200, 200, 200, 0.4) !important; | |
color: #000 !important; | |
} | |
.white-theme .message-input textarea { | |
color: #000 !important; | |
} | |
.white-theme .welcome-message h2 { | |
background: linear-gradient(90deg, #bfbebe, #6f6f7b, #bfbebe); | |
background-size: 200% auto; | |
background-clip: text; | |
-webkit-background-clip: text; | |
color: transparent; | |
animation: shimmer 3s linear infinite; | |
} | |
</style> | |
<script type="module" src="https://unpkg.com/[email protected]/dist/ionicons/ionicons.esm.js"></script> | |
<script nomodule src="https://unpkg.com/[email protected]/dist/ionicons/ionicons.js"></script> | |
</head> | |
<body> | |
<div class="sidebar"> | |
<div class="user-profile"> | |
<div class="avatar"> | |
<img src="https://via.placeholder.com/40" alt="User Avatar"> | |
</div> | |
<div> | |
<strong>Hi, Yash</strong> | |
</div> | |
</div> | |
<div class="chat-list"> | |
<div class="chat-item active"> | |
<ion-icon name="folder-open"></ion-icon> | |
Chat Title | |
</div> | |
<div class="chat-item"> | |
<ion-icon name="folder"></ion-icon> | |
Chat Title | |
</div> | |
<div class="chat-item"> | |
<ion-icon name="folder"></ion-icon> | |
Chat Title | |
</div> | |
</div> | |
</div> | |
<div class="main-content"> | |
<div class="header"> | |
<div class="header-left"> | |
<span class="header-title">Research Title</span> | |
<span class="separator">•</span> | |
<span class="model-name">SocioLens-llama-3.2-3B</span> | |
</div> | |
<div class="header-right"> | |
<div class="theme-manager"> | |
<label class="toggle-switch"> | |
<input type="checkbox" id="theme-toggle"> | |
<span class="slider"></span> | |
</label> | |
</div> | |
<div class="model-info"> | |
<button class="info-button"> | |
<ion-icon name="information-circle-outline"></ion-icon> | |
</button> | |
<div class="info-tooltip"> | |
The model can make mistakes; check your information carefully. | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="chat-area" id="chatArea"> | |
<div class="welcome-message" id="welcomeMessage"> | |
<h1>Hey! I'm SocioLens</h1> | |
<h2>How may I assist you today?</h2> | |
</div> | |
<div class="chat-conversation" id="chatConversation" style="display: none;"> | |
<!-- Messages will be injected here --> | |
</div> | |
</div> | |
<div class="input-area"> | |
<div class="message-input"> | |
<textarea id="messageInput" placeholder="Ask Anything"></textarea> | |
<div class="icon-row"> | |
<div class="icon-left"> | |
<div class="attachment-btn"> | |
<ion-icon name="link-outline"></ion-icon> | |
</div> | |
<div class="plus-btn"> | |
<ion-icon name="add-circle-outline"></ion-icon> | |
</div> | |
</div> | |
<button class="send-button" id="sendButton"> | |
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" | |
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
<path d="M22 2L11 13M22 2L15 22L11 13L2 9L22 2z"></path> | |
</svg> | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
const themeToggle = document.getElementById('theme-toggle'); | |
themeToggle.addEventListener('change', function () { | |
document.body.classList.toggle('white-theme', this.checked); | |
}); | |
const chatArea = document.getElementById('chatArea'); | |
const welcomeMessage = document.getElementById('welcomeMessage'); | |
const chatConversation = document.getElementById('chatConversation'); | |
const messageInput = document.getElementById('messageInput'); | |
const sendButton = document.getElementById('sendButton'); | |
// Function to append a message to the conversation | |
function appendMessage(role, content) { | |
const messageDiv = document.createElement('div'); | |
messageDiv.className = `message ${role}`; | |
messageDiv.innerHTML = `<div class="bubble">${content}</div>`; | |
chatConversation.appendChild(messageDiv); | |
chatConversation.scrollTop = chatConversation.scrollHeight; | |
} | |
// Function to add a spinner | |
function addSpinner() { | |
const spinnerDiv = document.createElement('div'); | |
spinnerDiv.className = 'message bot'; | |
spinnerDiv.innerHTML = '<div class="bubble">Thinking...</div>'; | |
chatConversation.appendChild(spinnerDiv); | |
chatConversation.scrollTop = chatConversation.scrollHeight; | |
return spinnerDiv; | |
} | |
// Load initial messages from Streamlit session state | |
window.addEventListener('load', () => { | |
if (window.parent && window.parent.streamlitMessages) { | |
const messages = window.parent.streamlitMessages; | |
if (messages.length > 0) { | |
welcomeMessage.style.display = 'none'; | |
chatConversation.style.display = 'flex'; | |
messages.forEach(msg => { | |
appendMessage(msg.role, msg.content); | |
}); | |
} | |
} | |
}); | |
// Handle send button click | |
sendButton.addEventListener('click', () => { | |
const userMessage = messageInput.value.trim(); | |
if (userMessage) { | |
// Hide welcome message on first interaction | |
if (welcomeMessage.style.display !== 'none') { | |
welcomeMessage.style.display = 'none'; | |
chatConversation.style.display = 'flex'; | |
} | |
// Append user message | |
appendMessage('user', userMessage); | |
// Clear input | |
messageInput.value = ''; | |
// Add spinner | |
const spinner = addSpinner(); | |
// Send message to Streamlit | |
if (window.parent && window.parent.postMessage) { | |
window.parent.postMessage({ | |
type: 'STREAMLIT_MESSAGE', | |
message: userMessage | |
}, '*'); | |
} | |
} | |
}); | |
// Listen for messages from Streamlit (bot responses) | |
window.addEventListener('message', (event) => { | |
if (event.data.type === 'BOT_RESPONSE') { | |
// Remove spinner | |
const spinners = chatConversation.querySelectorAll('.bubble:contains("Thinking...")'); | |
spinners.forEach(s => s.parentElement.remove()); | |
// Append bot response | |
appendMessage('bot', event.data.response); | |
} | |
}); | |
</script> | |
</body> | |
</html> | |
""" | |
# Streamlit app logic | |
st.set_page_config(page_title="SocioLens Chatbot", layout="wide") | |
# Check for Hugging Face API key | |
if not HF_TOKEN: | |
st.error("Please set your Hugging Face API key in Streamlit secrets (HUGGINGFACE_API_KEY).") | |
st.stop() | |
# Inject JavaScript to pass messages to the iframe | |
st.markdown(""" | |
<script> | |
window.streamlitMessages = %s; | |
const iframe = document.querySelector('iframe'); | |
if (iframe) { | |
iframe.contentWindow.postMessage({ | |
type: 'INITIAL_MESSAGES', | |
messages: window.streamlitMessages | |
}, '*'); | |
} | |
</script> | |
""" % json.dumps(st.session_state.messages), unsafe_allow_html=True) | |
# Render the HTML content | |
html(html_content, height=800, scrolling=True) | |
# Handle incoming messages from the frontend | |
if "pending_message" not in st.session_state: | |
st.session_state.pending_message = None | |
# Listen for messages from JavaScript | |
if st.session_state.pending_message: | |
user_message = st.session_state.pending_message | |
st.session_state.messages.append({"role": "user", "content": user_message}) | |
# Query the model | |
with st.spinner("Thinking..."): | |
bot_response = query_model(user_message) | |
st.session_state.messages.append({"role": "bot", "content": bot_response}) | |
# Send response back to the frontend | |
st.markdown(""" | |
<script> | |
const iframe = document.querySelector('iframe'); | |
if (iframe) { | |
iframe.contentWindow.postMessage({ | |
type: 'BOT_RESPONSE', | |
response: %s | |
}, '*'); | |
} | |
</script> | |
""" % json.dumps(bot_response), unsafe_allow_html=True) | |
st.session_state.pending_message = None | |
# JavaScript to receive messages from the frontend | |
st.markdown(""" | |
<script> | |
window.addEventListener('message', (event) => { | |
if (event.data.type === 'STREAMLIT_MESSAGE') { | |
// Store the message in session state | |
window.streamlitPendingMessage = event.data.message; | |
// Trigger rerun to process the message | |
window.parent.streamlitRerun(); | |
} | |
}); | |
// Function to trigger Streamlit rerun | |
window.streamlitRerun = function() { | |
const rerunButton = document.querySelector('[data-testid="stToolbarButton"]'); | |
if (rerunButton) { | |
rerunButton.click(); | |
} else { | |
// Fallback: Force a reload | |
window.location.reload(); | |
} | |
}; | |
</script> | |
""", unsafe_allow_html=True) | |