File size: 11,048 Bytes
d52c9ce b3bb424 0feee73 d52c9ce 5a8ed59 d52c9ce 0feee73 83f1db3 5a8ed59 ca84c57 0feee73 066fef8 5a8ed59 0feee73 5a8ed59 d52c9ce 78050e9 0feee73 78050e9 0feee73 78050e9 fb16688 83f1db3 d52c9ce 83f1db3 5a8ed59 78050e9 5a8ed59 d05f09f fb16688 78050e9 5a8ed59 4363711 d05f09f d52c9ce 5a8ed59 0feee73 78050e9 5a8ed59 0feee73 78050e9 5a8ed59 78050e9 0feee73 78050e9 0feee73 d52c9ce 440f4cc 78050e9 0feee73 d52c9ce 0feee73 b3bb424 78050e9 f23fbcc 2c07b09 e334bb1 78050e9 0feee73 78050e9 f23fbcc 4363711 f23fbcc c3b08ec f23fbcc c3b08ec 440f4cc f23fbcc e334bb1 0feee73 440f4cc f23fbcc 0feee73 440f4cc 78050e9 4363711 78050e9 0feee73 4363711 0feee73 2c07b09 0feee73 2c07b09 0feee73 ed6f8c1 0feee73 b3bb424 0feee73 4363711 0feee73 4363711 0feee73 4363711 0feee73 78050e9 0feee73 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | import os
import gradio as gr
from openai import OpenAI
from pypdf import PdfReader
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
# Set up OpenAI API key in HF secrets
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
client = OpenAI(api_key=OPENAI_API_KEY)
# Set up username and password in HF secrets
username = os.getenv('username')
password = os.getenv('password')
APP_PASSWORD = os.getenv('password', 'default_password')
# Function to chunk the document
def chunk_text(text, chunk_size=1000, overlap=100):
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunk = text[start:end]
chunks.append(chunk)
start = end - overlap
return chunks
# Function to find the most relevant chunks
def get_relevant_chunks(query, chunks, top_n=3):
if not chunks:
return []
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(chunks + [query])
cosine_similarities = cosine_similarity(tfidf_matrix[-1], tfidf_matrix[:-1]).flatten()
relevant_indices = cosine_similarities.argsort()[-top_n:][::-1]
return [chunks[i] for i in relevant_indices]
# Function to process multiple PDFs
def process_pdfs(pdf_files):
all_chunks = []
for pdf_file in pdf_files:
try:
reader = PdfReader(pdf_file)
full_text = ''.join(page.extract_text() for page in reader.pages)
chunks = chunk_text(full_text)
all_chunks.extend(chunks)
except Exception as e:
print(f"Error processing PDF {pdf_file}: {e}")
return all_chunks
# Add the paths to your desired knowledge base PDFs
try:
reference_documents = ["knowledge_base.pdf"]
text_chunks = process_pdfs(reference_documents)
except:
text_chunks = [] # If PDF doesn't exist, use empty chunks
instructions = os.getenv('INSTRUCTIONS')
def moderate_input(text):
"""Run input through OpenAI moderation API"""
try:
response = client.moderations.create(
model="omni-moderation-latest",
input=text
)
results = response.results[0]
if results.flagged:
return False, results.categories
return True, None
except Exception as e:
# Fail safe: allow text if moderation API is down
print(f"Moderation API error: {e}")
return True, None
def chat_with_assistant(message, history):
# Run moderation before processing
allowed, categories = moderate_input(message)
if not allowed:
return "⚠️ Sorry, I can’t respond to that request because it violates the usage policy."
# Find relevant chunks based on the user message
relevant_chunks = get_relevant_chunks(message, text_chunks)
context = "\n".join(relevant_chunks)
# Prepare the system message
system_message = f"""
#Role
-You are an impersonator and an educator.
-Your role is to adopt the personality, style, psychology, ideas, background, and circumstances of a historical figure.
-Your goal is to help students understand the historical figure better through and engaging conversation.
#Information
Your assigned historical figure is stated in your instructions:
{instructions}
Use the following as context for your answers.
{context}
However, use it seamlessly as background knowledge for a lively discussion and combine it with your own information. Do not provide citations or adopt a Q&A or academic tone.
#Important
-Always speak in the first person ("I") as the historical figure you are to incarnate.
-Always use appropriate language.
-Refuse to answer inappropriate questions or questions unrelated to your role and historical figure.
#Critical
-Important: Your knowledge of the world ends at the time of the death of your historical figure.
-Keep your responses concise and to the point. Avoid repetitions and always end on a period "." token
"""
# Prepare the message array
messages = [{"role": "system", "content": system_message}]
# Add conversation history
for human_msg, ai_msg in history:
if human_msg:
messages.append({"role": "user", "content": human_msg})
if ai_msg:
messages.append({"role": "assistant", "content": ai_msg})
# Add the current user message
messages.append({"role": "user", "content": message})
try:
# Make the API call
response = client.chat.completions.create(
model="gpt-4.1-mini",
messages=messages,
max_tokens=300,
)
return response.choices[0].message.content.strip()
except Exception as e:
return f"I apologize, but I'm having trouble responding right now. Error: {str(e)}"
# CSS for a blue-themed style
isp_theme = gr.themes.Default().set(
body_background_fill="#E6F3FF",
block_background_fill="#FFFFFF",
block_title_text_color="#003366",
block_label_background_fill="#B8D8FF",
input_background_fill="#FFFFFF",
button_primary_background_fill="#0066CC",
button_primary_background_fill_hover="#0052A3",
button_primary_text_color="#FFFFFF",
button_secondary_background_fill="#B8D8FF",
button_secondary_background_fill_hover="#99C2FF",
button_secondary_text_color="#003366",
block_border_width="1px",
block_border_color="#0066CC",
)
custom_css = """
#logo-img {
display: block;
margin: 0 auto;
width: 150px;
height: auto;
padding-bottom: 20px;
}
#disclaimer-footer {
width: 100%;
background-color: #B8D8FF;
color: #003366;
text-align: center;
padding: 10px 0;
font-size: 14px;
border-top: 1px solid #0066CC;
margin-top: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 10px;
}
.title {
color: #003366;
margin-bottom: 10px;
text-align: center;
}
.button-row {
display: flex;
gap: 10px;
justify-content: center;
margin-bottom: 15px;
}
"""
# Environment variables
assistant_avatar = os.getenv('AVATAR')
assistant_title = os.getenv('TITLE', 'AI Assistant')
assistant_logo = os.getenv('LOGO')
# Check if credentials are set
if not username or not password:
with gr.Blocks(theme=isp_theme, css=custom_css) as demo:
gr.Markdown("# Configuration Error")
gr.Markdown("Username and password are not configured in Hugging Face secrets.")
gr.Markdown("Please set 'username' and 'password' in your Space secrets.")
demo.launch()
else:
# Main interface with login
with gr.Blocks(theme=isp_theme, css=custom_css) as demo:
# Login Screen
with gr.Row(visible=True) as login_screen:
with gr.Column():
if assistant_logo:
gr.HTML(f'<img id="logo-img" src="{assistant_logo}" alt="Assistant Logo" onerror="this.style.display=\'none\';">')
gr.Markdown(f"<h1 style='text-align: center; color: #003366;'>{assistant_title}</h1>")
gr.Markdown("<h3 style='text-align: center; color: #003366;'>Please enter your credentials to continue.</h3>")
username_input = gr.Textbox(label="Username", placeholder="Enter username...")
password_input = gr.Textbox(label="Password", type="password", placeholder="Enter password...")
login_button = gr.Button("Login", variant="primary")
error_message = gr.Markdown()
# Main App (initially hidden)
with gr.Row(visible=False) as main_app:
with gr.Column(elem_classes="container"):
# Logo and Title
if assistant_logo:
gr.HTML(f'<img id="logo-img" src="{assistant_logo}" alt="Assistant Logo" onerror="this.style.display=\'none\';">')
gr.Markdown(f"# {assistant_title}", elem_classes="title")
# Chatbot
chatbot = gr.Chatbot(height=500, avatar_images=(None, assistant_avatar))
msg = gr.Textbox(placeholder="Type your message here...", container=False, scale=7)
with gr.Row(elem_classes="button-row"):
submit = gr.Button("Submit", variant="primary")
clear = gr.ClearButton([msg, chatbot], value="Clear", variant="secondary")
undo = gr.Button("Delete Previous", variant="secondary")
logout_button = gr.Button("Logout", variant="secondary")
gr.HTML('<div id="disclaimer-footer">You are chatting with an AI assistant. Make sure to evaluate the accuracy of its answers.</div>')
def login(entered_username, entered_password):
if entered_username == username and entered_password == password:
return (
gr.update(visible=False), # Hide login screen
gr.update(visible=True), # Show main app
"" # Clear error message
)
else:
return (
gr.update(visible=True), # Keep login screen visible
gr.update(visible=False), # Keep main app hidden
"<p style='color: red; text-align: center;'>Invalid credentials. Please try again.</p>"
)
def logout():
return (
gr.update(visible=True), # Show login screen
gr.update(visible=False), # Hide main app
"", # Clear error message
[], # Clear chat history
"" # Clear message input
)
def user(user_message, history):
return "", history + [[user_message, None]]
def bot(history):
if history and history[-1][0]:
bot_message = chat_with_assistant(history[-1][0], history[:-1])
history[-1][1] = bot_message
return history
def delete_previous(history):
if len(history) > 0:
return history[:-1]
return history
# Login event handlers
login_button.click(
login,
inputs=[username_input, password_input],
outputs=[login_screen, main_app, error_message]
)
password_input.submit(
login,
inputs=[username_input, password_input],
outputs=[login_screen, main_app, error_message]
)
# Logout event handler
logout_button.click(
logout,
outputs=[login_screen, main_app, error_message, chatbot, msg]
)
# Chat event handlers
msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
bot, chatbot, chatbot
)
submit.click(user, [msg, chatbot], [msg, chatbot], queue=False).then(
bot, chatbot, chatbot
)
undo.click(delete_previous, chatbot, chatbot)
demo.launch() |