File size: 8,370 Bytes
d52c9ce
b3bb424
d52c9ce
 
5a8ed59
 
d52c9ce
78050e9
5a8ed59
 
 
 
ca84c57
 
066fef8
5a8ed59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d52c9ce
78050e9
 
 
 
 
 
 
 
 
 
 
146ffc5
78050e9
 
 
fb16688
d52c9ce
5a8ed59
 
 
78050e9
5a8ed59
78050e9
ac041d3
 
fb16688
 
 
 
78050e9
5a8ed59
4363711
ac041d3
 
 
 
 
d52c9ce
 
5a8ed59
2d2bfa2
78050e9
5a8ed59
 
 
 
78050e9
5a8ed59
 
78050e9
5a8ed59
d52c9ce
78050e9
5a8ed59
d52c9ce
be1f678
76fcac1
2d2bfa2
5a8ed59
d52c9ce
78050e9
d52c9ce
 
440f4cc
78050e9
440f4cc
d52c9ce
440f4cc
 
d52c9ce
440f4cc
 
d52c9ce
440f4cc
 
 
d52c9ce
440f4cc
b3bb424
 
4363711
78050e9
f23fbcc
2c07b09
 
e334bb1
78050e9
e334bb1
78050e9
f23fbcc
 
 
 
 
 
 
 
4363711
 
f23fbcc
c3b08ec
f23fbcc
c3b08ec
440f4cc
f23fbcc
 
e334bb1
2c07b09
440f4cc
f23fbcc
2c07b09
f23fbcc
2c07b09
440f4cc
 
f23fbcc
 
 
2c07b09
440f4cc
 
4b47cff
 
 
125d9fe
4b47cff
f23fbcc
4b47cff
 
 
6d7ea2e
125d9fe
c3b08ec
e334bb1
 
 
 
125d9fe
 
e334bb1
c3b08ec
e334bb1
 
 
125d9fe
78050e9
 
4363711
78050e9
 
4363711
 
 
 
440f4cc
2c07b09
 
 
 
 
 
 
 
 
ed6f8c1
 
 
2c07b09
 
 
 
 
 
 
 
 
 
 
 
440f4cc
 
 
 
ed6f8c1
 
 
 
b3bb424
4363711
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440f4cc
 
4363711
440f4cc
78050e9
066fef8
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
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

# Set up Anthropic API key in HF secrets
ANTHROPIC_API_KEY = os.getenv('ANTHROPIC_API_KEY')
os.environ["ANTHROPIC_API_KEY"] = ANTHROPIC_API_KEY

# Set up username and password in HF secrets
username = os.getenv('username')
password = os.getenv('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):
    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:
        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

# Add the paths to your desired knowledge base PDFs
reference_documents = ["Louis XIV.pdf"]
text_chunks = process_pdfs(reference_documents)

instructions = os.getenv('INSTRUCTIONS')

def chat_with_assistant(message, history):
    # 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"""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.

    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.
    
    Always use appropriate language.
    Refuse to answer inappropriate questions or questions unrelated to your role and historical figure.

    Important: Your knowledge of the world ends at the time of the death of your historical figure.
    """

    # Prepare the message array
    messages = []

    # Add conversation history
    for human_msg, ai_msg in history:
        messages.append({"role": "user", "content": human_msg})
        messages.append({"role": "assistant", "content": ai_msg})

    # Add the current user message
    messages.append({"role": "user", "content": message})

    # Create Anthropic client
    client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

    # Make the API call
    response = client.messages.create(
        model="claude-3-5-sonnet-20240620",
        max_tokens=350,
        system=system_message,
        messages=messages
    )

    return response.content[0].text.strip()

# CSS for a blue-themed style
isp_theme = gr.themes.Default().set(
    body_background_fill="#E6F3FF",  # Light blue background
    block_background_fill="#FFFFFF",  # White for input blocks
    block_title_text_color="#003366",  # Dark blue for text
    block_label_background_fill="#B8D8FF",  # Light blue for labels
    input_background_fill="#FFFFFF",  # White for input fields
    button_primary_background_fill="#0066CC",  # Medium blue for primary buttons
    button_primary_background_fill_hover="#0052A3",  # Darker blue for hover
    button_primary_text_color="#FFFFFF",  # White text on buttons
    button_secondary_background_fill="#B8D8FF",  # Light blue for secondary buttons
    button_secondary_background_fill_hover="#99C2FF",  # Slightly darker blue for hover
    button_secondary_text_color="#003366",  # Dark blue text for secondary buttons
    block_border_width="1px",
    block_border_color="#0066CC",  # Medium blue border
)

# Custom CSS for logo positioning and disclaimer footer
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 */
}
"""

# Environment variables
assistant_avatar = os.getenv('AVATAR')
assistant_title = os.getenv('TITLE')
assistant_logo = os.getenv('LOGO')

# Gradio interface using Blocks
with gr.Blocks(theme=isp_theme, css=custom_css) as iface:
    with gr.Column(elem_classes="container"):
        # Logo and Title
        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")

        # Chatbot and message input
        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))