File size: 15,335 Bytes
b03c857
abe0094
b03c857
443aa67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bff002a
 
 
 
 
497e4af
d805974
bff002a
d805974
497e4af
 
 
734741e
b765f56
 
 
 
 
d805974
bff002a
 
 
 
 
d805974
bff002a
 
 
 
 
 
 
 
 
 
497e4af
 
b765f56
 
bff002a
 
 
 
 
 
 
 
 
 
 
497e4af
 
b765f56
 
bff002a
 
 
 
 
 
 
 
 
 
 
 
 
7dca6a4
bff002a
 
 
7dca6a4
bff002a
 
 
 
 
 
 
 
b8aaebb
bff002a
8e32e49
08c22e7
0362d0e
face5af
 
 
 
 
 
 
0362d0e
74e5a00
f81ec00
 
497e4af
 
b765f56
 
497e4af
d805974
497e4af
b765f56
 
db4c5ab
f81ec00
db4c5ab
74e5a00
45040ea
face5af
 
 
 
45040ea
0362d0e
db4c5ab
b765f56
5ce6948
319bc19
 
 
735cea0
45040ea
 
74e5a00
319bc19
 
5ce6948
319bc19
 
 
735cea0
45040ea
 
74e5a00
319bc19
 
c56f775
319bc19
 
 
735cea0
45040ea
 
74e5a00
319bc19
 
 
abe0094
4064d87
 
 
abe0094
 
 
fec3486
abe0094
0c44b83
984367c
abe0094
ad41c44
4064d87
 
 
 
 
ad41c44
0abd659
 
a9a5087
 
 
 
 
 
 
 
 
 
 
 
 
 
7afc614
e877c09
 
 
 
 
 
7afc614
443aa67
db4c5ab
 
 
0310316
74e5a00
b9b0f92
74e5a00
 
5ce6948
 
 
af49bf4
467e60a
db4c5ab
 
981f2e6
db4c5ab
0310316
60f04cb
 
73b4377
9c2d246
60f04cb
4704d8f
 
9c2d246
4704d8f
 
7afc614
0bb2aa4
7afc614
 
 
 
 
 
 
 
e877c09
7afc614
0bb2aa4
7afc614
 
 
 
 
e877c09
0bb2aa4
7afc614
 
 
 
 
e877c09
7afc614
 
 
e877c09
0362d0e
 
a9a5087
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c531ca0
1ec61a7
c531ca0
 
940fd7b
c531ca0
 
 
 
f656def
74e5a00
c531ca0
 
 
 
 
 
23a71b3
 
 
0abd659
 
 
 
 
 
 
 
 
 
 
 
23a71b3
0abd659
 
6710316
 
 
0abd659
 
 
 
 
 
 
 
 
 
 
 
6710316
0abd659
6710316
0d6fdec
fa0de28
fdc8721
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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
import gradio as gr
from huggingface_hub import InferenceClient

theme = gr.themes.Monochrome(
    primary_hue=gr.themes.Color(c100="#f5f5f3", c200="#fbfaf8", c300="#f9f9f9", c400="#eee9ee", c50="rgba(255, 255, 255, 1)", c500="rgba(0, 0, 0, 1)", c600="rgba(26.934374999999992, 26.934374999999992, 26.934374999999992, 1)", c700="rgba(10.943750000000012, 10.943750000000012, 10.943750000000012, 1)", c800="rgba(17.053125000000005, 17.053125000000005, 17.053125000000005, 1)", c900="#fffefe", c950="#fffefe"),
    secondary_hue=gr.themes.Color(c100="#f29c74", c200="#f4b7a8", c300="#fffefe", c400="#fffefe", c50="#e46e45", c500="#fffefe", c600="#fffefe", c700="#fffefe", c800="#fffefe", c900="#fffefe", c950="#fffefe"),
    neutral_hue=gr.themes.Color(c100="#6b8ea8", c200="#a4c5e8", c300="rgba(0, 0, 0, 1)", c400="rgba(0, 0, 0, 1)", c50="#284566", c500="rgba(0, 0, 0, 1)", c600="rgba(0, 0, 0, 1)", c700="rgba(0, 0, 0, 1)", c800="rgba(0, 0, 0, 1)", c900="rgba(0, 0, 0, 1)", c950="rgba(0, 0, 0, 1)"),
    font=[gr.themes.GoogleFont('open sans'), 'ui-sans-serif', 'system-ui', 'sans-serif'],
).set(
    body_background_fill='*primary_50',
    body_background_fill_dark='*primary_700',
    body_text_color='*primary_500',
    body_text_color_dark='*primary_400',
    body_text_color_subdued='*neutral_50',
    body_text_color_subdued_dark='*primary_300',
    background_fill_primary='*primary_400',
    background_fill_primary_dark='*neutral_100',
    background_fill_secondary='*primary_400',
    background_fill_secondary_dark='*neutral_50',
    border_color_accent_dark='*secondary_50',
    border_color_accent_subdued_dark='*primary_50',
    border_color_primary='*neutral_50',
    border_color_primary_dark='*neutral_100',
    color_accent='*primary_50',
    color_accent_soft_dark='*neutral_100',
    block_background_fill_dark='*neutral_50',
    checkbox_background_color='*primary_400',
    checkbox_background_color_selected='*primary_700',
    checkbox_background_color_selected_dark='*neutral_50',
    checkbox_label_border_color_dark='*neutral_50',
    input_background_fill='*primary_50',
    input_background_fill_dark='*neutral_100',
    input_border_color='*neutral_50',
    input_border_color_dark='*neutral_50',
    input_border_width='2px',
    table_odd_background_fill='*neutral_100',
    button_primary_background_fill='*neutral_50',
    button_primary_background_fill_dark='*neutral_100',
    button_primary_background_fill_hover='*neutral_100'
)

#STEP 1 FROM SEMATIC SEARCH
from sentence_transformers import SentenceTransformer
import torch

#STEP 2 FROM SEMATIC SEARCH
# Open the weather.txt file in read mode with UTF-8 encoding
with open("weather.txt", "r", encoding="utf-8") as file:
  # Read the entire contents of the file and store it in a variable
  weather_text = file.read()
with open("luggage.txt", "r", encoding="utf-8") as file:
  # Read the entire contents of the file and store it in a variable
  luggage_text = file.read()
with open("attractions.txt", "r", encoding="utf-8") as file:
  # Read the entire contents of the file and store it in a variable
  attraction_text = file.read()
with open("food.txt", "r", encoding="utf-8") as file:
  # Read the entire contents of the file and store it in a variable
  food_text = file.read()

#STEP 3 FROM SEMATIC SEARCH
def preprocess_text(text):
  # Strip extra whitespace from the beginning and the end of the text
  cleaned_text = text.strip()
  # Split the cleaned_text by every newline character (\n)
  chunks = cleaned_text.split("***")
  # Create an empty list to store cleaned chunks
  cleaned_chunks = []
  # Write your for-in loop below to clean each chunk and add it to the cleaned_chunks list
  for chunk in chunks:
    chunk.strip()
    if chunk != "":
      cleaned_chunks.append(chunk)
  return cleaned_chunks

# Call the preprocess_text function and store the result in a cleaned_chunks variable
cleaned_chunks_weather = preprocess_text(weather_text) # Complete this line
cleaned_chunks_luggage = preprocess_text(luggage_text)
cleaned_chunks_attraction = preprocess_text(attraction_text)
cleaned_chunks_food = preprocess_text(food_text)

#STEP 4 FROM SEMATIC SEARCH
# Load the pre-trained embedding model that converts text to vectors
model = SentenceTransformer('all-MiniLM-L6-v2')

def create_embeddings(text_chunks):
  # Convert each text chunk into a vector embedding and store as a tensor
  chunk_embeddings = model.encode(text_chunks, convert_to_tensor=True) # Replace ... with the text_chunks list
  return chunk_embeddings

# Call the create_embeddings function and store the result in a new chunk_embeddings variable
chunk_embeddings_weather = create_embeddings(cleaned_chunks_weather) # Complete this line
chunk_embeddings_luggage = create_embeddings(cleaned_chunks_luggage)
chunk_embeddings_attraction = create_embeddings(cleaned_chunks_attraction)
chunk_embeddings_food = create_embeddings(cleaned_chunks_food)

#STEP 5 FROM SEMATIC SEARCH
# Define a function to find the most relevant text chunks for a given query, chunk_embeddings, and text_chunks
def get_top_chunks(query, chunk_embeddings, text_chunks):
  # Convert the query text into a vector embedding
  query_embedding = model.encode(query, convert_to_tensor = True) # Complete this line
  # Normalize the query embedding to unit length for accurate similarity comparison
  query_embedding_normalized = query_embedding / query_embedding.norm()
  # Normalize all chunk embeddings to unit length for consistent comparison
  chunk_embeddings_normalized = chunk_embeddings / chunk_embeddings.norm(dim=1, keepdim=True)
  # Calculate cosine similarity between query and all chunks using matrix multiplication
  similarities = torch.matmul(chunk_embeddings_normalized, query_embedding_normalized) # Complete this line
  # Print the similarities
  #print(similarities)
  # Find the indices of the 3 chunks with highest similarity scores
  top_indices = torch.topk(similarities, k=3).indices
  # Print the top indices
  #print(top_indices)
  # Create an empty list to store the most relevant chunks
  top_chunks = []
  # Loop through the top indices and retrieve the corresponding text chunks
  for top_index in top_indices:
    top_chunks.append(text_chunks[top_index])
  # Return the list of most relevant chunks
  return top_chunks

#STEP 6 FROM SEMANTIC SEARCH

client = InferenceClient("Qwen/Qwen2.5-72B-Instruct")

def respond(message, history, language, chatbot_mode, destinations, trip_length, trip_unit, trip_season, luggage_types, luggage_size, food_prefs, activity):
    destinations = destinations or []
    trip_length = trip_length or "Not specified"
    trip_unit = trip_unit or ""
    trip_season = trip_season or "Not specified"
    luggage_types = luggage_types or []
    luggage_size = luggage_size or "Not specified"
    food_prefs = food_prefs or []
    activity = activity or []
    language = language or "English"

    #conduct a semantic search for each of our files
    top_weather = get_top_chunks(message, chunk_embeddings_weather, cleaned_chunks_weather)
    top_luggage = get_top_chunks(message, chunk_embeddings_luggage, cleaned_chunks_luggage)
    top_attraction = get_top_chunks(message, chunk_embeddings_attraction, cleaned_chunks_attraction)
    top_food = get_top_chunks(message, chunk_embeddings_food, cleaned_chunks_food)
    
    str_top_weather = "\n".join(top_weather)
    str_top_luggage = "\n".join(top_luggage)
    str_top_attraction = "\n".join(top_attraction)
    str_top_food = "\n".join(top_food)

    #collect inputed data from the sidebar elements
    ctx = (
        f"Language: {language}"
        f"Destination: {', '.join(destinations) if destinations else 'Not specified'}\n"
        f"Trip Length: {trip_length} {trip_unit}\n"
        f"Trip Season: {trip_season}\n"
        f"Luggage: {', '.join(luggage_types) if luggage_types else 'Not specified'}, "
        f"Size: {luggage_size}L\n"
        f"Food Preferences: {', '.join(food_prefs) if food_prefs else 'Not specified'}\n"
        f"Activity Preferences: {', '.join(activity) if activity else 'Not specified'}\n"
    )

    if chatbot_mode == "Packing":
        messages = [{
            "role": "system",
            "content": (
                f"You are a friendly and Gen Z travel chatbot helping with packing advice.\n\n"
                f"{ctx}\n"
                f"Relevant context:\n{str_top_weather}\n{str_top_luggage}"
                f"Please respond in {language}"
            )
        }]
    elif chatbot_mode == "Food/Attractions":
        messages = [{
            "role": "system",
            "content": (
                f"You are a friendly and Gen Z travel chatbot recommending food and attractions.\n\n"
                f"{ctx}\n"
                f"Relevant context:\n{str_top_food}\n{str_top_attraction}"
                f"Please respond in {language}"
            )
        }]
    else:
        messages = [{
            "role": "system",
            "content": (
                f"You are a friendly and Gen Z travel chatbot helping travelers plan trips to San Francisco and/or Los Angeles.\n\n"
                f"{ctx}\n"
                f"Use relevant context:\n{str_top_weather}\n{str_top_luggage}\n{str_top_food}\n{str_top_attraction}"
                f"Please respond in {language}"
            )
        }]

    if history:
        for user_msg, bot_reply in history:
            messages.append({"role": "user", "content": user_msg})
            messages.append({"role": "assistant", "content": bot_reply})

    messages.append({"role": "user", "content": message})

    response = client.chat_completion(
        messages,
        max_tokens =  2000,
        temperature = 1
    )

    if isinstance(response, dict):
        reply = response['choices'][0]['message']['content'].strip()
    else:
        reply = response.strip()
    
    history.append((message, reply))
    return history, ""
    
def reset_inputs():
    return (
        None,  # language
        None,  # chatbot_mode
        [],    # destinations
        None,  # trip_length
        None,  # trip_unit
        None,  # trip_season
        [],    # luggage_types
        20,    # luggage_size (default)
        [],    # food_prefs
        []     # activity
    )

def update_visibility(chatbot_mode):
        if chatbot_mode == "Packing":
            return gr.update(visible=True), gr.update(visible=False)
        elif chatbot_mode == "Food/Attractions":
            return gr.update(visible=False), gr.update(visible=True)
        else:
            return gr.update(visible=True), gr.update(visible=True)

with gr.Blocks(theme=theme) as demo:
    with gr.Row():
        # ─── left column: your controls ───
        with gr.Column(scale=1):
            gr.Markdown("### Chatbot Settings")
            language = gr.Radio(
                choices=["English","Español", "Italiano", "Français", "日本語", "中文"],
                label="What language would you like to use?"
            )
            chatbot_mode = gr.Radio(
                choices=["Packing", "Food/Attractions"],
                label="What do you need help with?"
            )
            gr.Markdown("### Destination")
            destinations = gr.CheckboxGroup(
                choices=["San Francisco","Los Angeles"],
                label="Where are you going?"
            )
            gr.Markdown("### Trip Length")
            trip_length = gr.Number(label="How long is your trip?", precision=0)
            trip_unit = gr.Radio(
                choices=["day", "week", "month"],
                label="Trip length unit:"
            )
            trip_season = gr.Radio(
                choices=["Warm/Dry (May to October)", "Cool/Wet (November to April)"],
                label="Season:"
            )
            
            with gr.Group(visible=True) as packing_group:
                #gr.Markdown("### Luggage")
                luggage_types = gr.CheckboxGroup(
                    choices=["Carry-on", "Checked"],
                    label="What is your luggage type?"
                )
                luggage_size = gr.Slider(
                    minimum=10, maximum=100, step=10, value=20,
                    label="What is the size of your luggage (liters)?"
                )
            
            with gr.Group(visible=True) as food_group:
                    #gr.Markdown("### Food")
                    food_prefs = gr.Dropdown(
                        choices=["Italian", "Thai", "Mexican", "Japanese", "Vegan", "Seafood"],
                        multiselect=True,
                        label="What are your food preferences?"
                    )
            
                    #gr.Markdown("### Activities")
                    activity = gr.Dropdown(
                        choices=["Outdoor & Nature", "Indoor", "Museums", "Shopping", "Relaxation"],
                        multiselect=True,
                        label="What are your activity preferences?"
                    )

            chatbot_mode.change(
                fn=update_visibility,
                inputs=[chatbot_mode],
                outputs=[packing_group, food_group]
            )

            reset_btn = gr.Button("Reset All", variant="secondary")
            reset_btn.click(
                fn=reset_inputs,
                inputs=[],
                outputs=[
                    language,
                    chatbot_mode,
                    destinations,
                    trip_length,
                    trip_unit,
                    trip_season,
                    luggage_types,
                    luggage_size,
                    food_prefs,
                    activity
                ]
            )

        with gr.Column(scale=3):
            gr.Image(value="Go Buddy2.png", interactive=False, show_label=False)

            # 1) the chat history panel
            chat_box = gr.Chatbot(height=900)

            # 2) where the user types
            msg = gr.Textbox(
                placeholder="Ask me anything…",
                lines=1,
                label = "How can we help you?"
            )

            # 3) send button
            send_btn = gr.Button("Send")

            # 4) hook up the click event
            send_btn.click(
                fn=respond,
                inputs=[
                    msg,            
                    chat_box,       
                    language,
                    chatbot_mode,
                    destinations,
                    trip_length,
                    trip_unit,
                    trip_season, 
                    luggage_types,
                    luggage_size,
                    food_prefs, 
                    activity
                ],
                outputs=[chat_box, msg] 
            )
            msg.submit(
                fn=respond,
                inputs=[
                    msg,
                    chat_box,
                    language,
                    chatbot_mode,
                    destinations,
                    trip_length,
                    trip_unit,
                    trip_season, 
                    luggage_types,
                    luggage_size,
                    food_prefs, 
                    activity
                ],
                outputs=[chat_box, msg]
            )


demo.launch(debug=True)