iyashnayi's picture
Update app.py
443fc1c verified
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)