|
import os |
|
import gradio as gr |
|
from anthropic import Anthropic |
|
from pypdf import PdfReader |
|
from sklearn.feature_extraction.text import TfidfVectorizer |
|
from sklearn.metrics.pairwise import cosine_similarity |
|
|
|
|
|
ANTHROPIC_API_KEY = os.getenv('ANTHROPIC_API_KEY') |
|
os.environ["ANTHROPIC_API_KEY"] = ANTHROPIC_API_KEY |
|
|
|
|
|
username = os.getenv('username') |
|
password = os.getenv('password') |
|
|
|
|
|
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 |
|
|
|
|
|
def get_relevant_chunks(query, chunks, top_n=3): |
|
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] |
|
|
|
|
|
def process_pdfs(pdf_files): |
|
all_chunks = [] |
|
for pdf_file in pdf_files: |
|
reader = PdfReader(pdf_file) |
|
full_text = ''.join(page.extract_text() for page in reader.pages) |
|
chunks = chunk_text(full_text) |
|
all_chunks.extend(chunks) |
|
return all_chunks |
|
|
|
|
|
reference_documents = ["knowledge_base.pdf"] |
|
text_chunks = process_pdfs(reference_documents) |
|
|
|
instructions = os.getenv('INSTRUCTIONS') |
|
|
|
def chat_with_assistant(message, history): |
|
|
|
relevant_chunks = get_relevant_chunks(message, text_chunks) |
|
context = "\n".join(relevant_chunks) |
|
|
|
|
|
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 |
|
""" |
|
|
|
|
|
messages = [] |
|
|
|
|
|
for human_msg, ai_msg in history: |
|
messages.append({"role": "user", "content": human_msg}) |
|
messages.append({"role": "assistant", "content": ai_msg}) |
|
|
|
|
|
messages.append({"role": "user", "content": message}) |
|
|
|
|
|
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"]) |
|
|
|
|
|
response = client.messages.create( |
|
model="claude-3-5-sonnet-20240620", |
|
max_tokens=300, |
|
system=system_message, |
|
messages=messages |
|
) |
|
|
|
return response.content[0].text.strip() |
|
|
|
|
|
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; /* Space below logo */ |
|
} |
|
#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; /* Center the title */ |
|
} |
|
.chatbot { |
|
border: none; |
|
border-radius: 5px; |
|
padding: 10px; |
|
margin-bottom: 15px; |
|
} |
|
.button-row { |
|
display: flex; |
|
gap: 10px; |
|
justify-content: center; /* Center the buttons */ |
|
margin-bottom: 15px; |
|
} |
|
.chatbot .message, |
|
.chatbot .message::before, |
|
.chatbot .message::after { |
|
border: none !important; |
|
box-shadow: none !important; |
|
} |
|
.chatbot .message > div { |
|
border: none !important; |
|
box-shadow: none !important; |
|
} |
|
.chatbot .message-content { |
|
padding: 2px 2px; /* Reduced padding to make text bubbles smaller */ |
|
margin-bottom: 5px; /* Space between messages */ |
|
max-width: 60%; /* Restrict width to make it more compact */ |
|
word-wrap: break-word; /* Ensure text wraps properly */ |
|
display: inline-block; /* Align content properly */ |
|
} |
|
.chatbot .message-bubble { |
|
background-color: #FFFFFF; /* Ensure background is white */ |
|
border-radius: 2px; /* Smaller rounded corners */ |
|
box-sizing: border-box; /* Ensure padding is included in width */ |
|
display: inline-block; /* Align the bubble content properly */ |
|
margin: 2px 0; /* Reduce margin to minimize bubble size */ |
|
} |
|
""" |
|
|
|
|
|
assistant_avatar = os.getenv('AVATAR') |
|
assistant_title = os.getenv('TITLE') |
|
assistant_logo = os.getenv('LOGO') |
|
|
|
|
|
with gr.Blocks(theme=isp_theme, css=custom_css) as iface: |
|
with gr.Column(elem_classes="container"): |
|
|
|
gr.HTML( |
|
f""" |
|
<img id='logo-img' src='{assistant_logo}' alt='Assistant Logo' onerror="this.style.display='none';document.getElementById('logo-error').style.display='block';"> |
|
<div id='logo-error' style='display:none;'>Logo not found</div> |
|
""", |
|
elem_id='logo-container' |
|
) |
|
gr.Markdown(f"# {assistant_title}", elem_classes="title") |
|
|
|
|
|
with gr.Row(): |
|
chatbot = gr.Chatbot( |
|
height=500, |
|
avatar_images=(None, assistant_avatar), |
|
elem_classes="chatbot" |
|
) |
|
|
|
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") |
|
|
|
gr.HTML( |
|
"<div id='disclaimer-footer'>You are chatting with an AI assistant. Make sure to evaluate the accuracy of its answers.</div>" |
|
) |
|
|
|
def user(user_message, history): |
|
return "", history + [[user_message, None]] |
|
|
|
def bot(history): |
|
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 |
|
|
|
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) |
|
|
|
iface.launch(auth=(username, password)) |