Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -59,13 +59,9 @@ translations = {
|
|
| 59 |
|
| 60 |
def clean_bird_name(name):
|
| 61 |
"""Clean bird name by removing numbers and special characters, and fix formatting"""
|
| 62 |
-
# Remove numbers and dots at the beginning
|
| 63 |
cleaned = re.sub(r'^\d+\.', '', name)
|
| 64 |
-
# Replace underscores with spaces
|
| 65 |
cleaned = cleaned.replace('_', ' ')
|
| 66 |
-
# Remove any remaining special characters
|
| 67 |
cleaned = re.sub(r'[^\w\s]', '', cleaned)
|
| 68 |
-
# Fix spacing
|
| 69 |
cleaned = ' '.join(cleaned.split())
|
| 70 |
return cleaned
|
| 71 |
|
|
@@ -73,7 +69,6 @@ def get_bird_habitat_map(bird_name, check_tanzania=True):
|
|
| 73 |
"""Get habitat map locations for the bird using Groq API"""
|
| 74 |
clean_name = clean_bird_name(bird_name)
|
| 75 |
|
| 76 |
-
# First check if the bird is endemic to Tanzania
|
| 77 |
if check_tanzania:
|
| 78 |
tanzania_check_prompt = f"""
|
| 79 |
Is the {clean_name} bird native to or commonly found in Tanzania?
|
|
@@ -87,12 +82,10 @@ def get_bird_habitat_map(bird_name, check_tanzania=True):
|
|
| 87 |
)
|
| 88 |
is_in_tanzania = "yes" in tanzania_check.choices[0].message.content.lower()
|
| 89 |
except:
|
| 90 |
-
# Default to showing Tanzania if we can't determine
|
| 91 |
is_in_tanzania = True
|
| 92 |
else:
|
| 93 |
is_in_tanzania = True
|
| 94 |
|
| 95 |
-
# Now get the habitat locations
|
| 96 |
prompt = f"""
|
| 97 |
Provide a JSON array of the main habitat locations for the {clean_name} bird in the world.
|
| 98 |
Return ONLY a JSON array with 3-5 entries, each containing:
|
|
@@ -112,98 +105,70 @@ def get_bird_habitat_map(bird_name, check_tanzania=True):
|
|
| 112 |
|
| 113 |
try:
|
| 114 |
chat_completion = client.chat.completions.create(
|
| 115 |
-
messages=[
|
| 116 |
-
{
|
| 117 |
-
"role": "user",
|
| 118 |
-
"content": prompt,
|
| 119 |
-
}
|
| 120 |
-
],
|
| 121 |
model="llama-3.3-70b-versatile",
|
| 122 |
)
|
| 123 |
response = chat_completion.choices[0].message.content
|
| 124 |
|
| 125 |
-
# Extract JSON from response (in case there's additional text)
|
| 126 |
-
import json
|
| 127 |
-
import re
|
| 128 |
-
|
| 129 |
-
# Find JSON pattern in response
|
| 130 |
json_match = re.search(r'\[.*\]', response, re.DOTALL)
|
| 131 |
if json_match:
|
| 132 |
locations = json.loads(json_match.group())
|
| 133 |
else:
|
| 134 |
-
# Fallback if JSON extraction fails
|
| 135 |
locations = [
|
| 136 |
{"name": "Primary habitat region", "lat": 0, "lon": 0,
|
| 137 |
"description": "Could not retrieve specific habitat information for this bird."}
|
| 138 |
]
|
| 139 |
-
|
| 140 |
return locations, is_in_tanzania
|
| 141 |
-
|
| 142 |
-
except Exception as e:
|
| 143 |
return [{"name": "Error retrieving data", "lat": 0, "lon": 0,
|
| 144 |
"description": "Please try again or check your connection."}], False
|
| 145 |
|
| 146 |
def create_habitat_map(habitat_locations):
|
| 147 |
"""Create a folium map with the habitat locations"""
|
| 148 |
-
# Find center point based on valid coordinates
|
| 149 |
valid_coords = [(loc.get("lat", 0), loc.get("lon", 0))
|
| 150 |
for loc in habitat_locations
|
| 151 |
if loc.get("lat", 0) != 0 or loc.get("lon", 0) != 0]
|
| 152 |
|
| 153 |
if valid_coords:
|
| 154 |
-
# Calculate the average of the coordinates
|
| 155 |
avg_lat = sum(lat for lat, _ in valid_coords) / len(valid_coords)
|
| 156 |
avg_lon = sum(lon for _, lon in valid_coords) / len(valid_coords)
|
| 157 |
-
# Create map centered on the average coordinates
|
| 158 |
m = folium.Map(location=[avg_lat, avg_lon], zoom_start=3)
|
| 159 |
else:
|
| 160 |
-
# Default world map if no valid coordinates
|
| 161 |
m = folium.Map(location=[20, 0], zoom_start=2)
|
| 162 |
|
| 163 |
-
# Add markers for each habitat location
|
| 164 |
for location in habitat_locations:
|
| 165 |
name = location.get("name", "Unknown")
|
| 166 |
lat = location.get("lat", 0)
|
| 167 |
lon = location.get("lon", 0)
|
| 168 |
description = location.get("description", "No description available")
|
| 169 |
|
| 170 |
-
# Skip invalid coordinates
|
| 171 |
if lat == 0 and lon == 0:
|
| 172 |
continue
|
| 173 |
|
| 174 |
-
# Add marker
|
| 175 |
folium.Marker(
|
| 176 |
location=[lat, lon],
|
| 177 |
popup=folium.Popup(f"<b>{name}</b><br>{description}", max_width=300),
|
| 178 |
tooltip=name
|
| 179 |
).add_to(m)
|
| 180 |
|
| 181 |
-
# Save map to HTML
|
| 182 |
map_html = m._repr_html_()
|
| 183 |
return map_html
|
| 184 |
|
| 185 |
def format_bird_info(raw_info, language="en"):
|
| 186 |
"""Improve the formatting of bird information"""
|
| 187 |
-
# Add proper line breaks between sections and ensure consistent heading levels
|
| 188 |
formatted = raw_info
|
| 189 |
|
| 190 |
-
# Get translation of warning text based on language
|
| 191 |
warning_text = "NOT TYPICALLY FOUND IN TANZANIA"
|
| 192 |
warning_translation = "HAPATIKANI SANA TANZANIA" if language == "sw" else warning_text
|
| 193 |
|
| 194 |
-
# Fix heading levels (make all main sections h3)
|
| 195 |
formatted = re.sub(r'#+\s+' + warning_text,
|
| 196 |
-
|
| 197 |
-
|
| 198 |
|
| 199 |
-
# Replace markdown headings with HTML headings for better control
|
| 200 |
formatted = re.sub(r'#+\s+(.*)', r'<h3>\1</h3>', formatted)
|
| 201 |
-
|
| 202 |
-
# Add paragraph tags for better spacing
|
| 203 |
formatted = re.sub(r'\n\*\s+(.*)', r'<p>• \1</p>', formatted)
|
| 204 |
formatted = re.sub(r'\n([^<\n].*)', r'<p>\1</p>', formatted)
|
| 205 |
|
| 206 |
-
# Remove any duplicate paragraph tags
|
| 207 |
formatted = formatted.replace('<p><p>', '<p>')
|
| 208 |
formatted = formatted.replace('</p></p>', '</p>')
|
| 209 |
|
|
@@ -213,10 +178,7 @@ def get_bird_info(bird_name, language="en"):
|
|
| 213 |
"""Get detailed information about a bird using Groq API"""
|
| 214 |
clean_name = clean_bird_name(bird_name)
|
| 215 |
|
| 216 |
-
|
| 217 |
-
lang_instruction = ""
|
| 218 |
-
if language == "sw":
|
| 219 |
-
lang_instruction = " Provide your response in Swahili language."
|
| 220 |
|
| 221 |
prompt = f"""
|
| 222 |
Provide detailed information about the {clean_name} bird, including:
|
|
@@ -233,12 +195,7 @@ def get_bird_info(bird_name, language="en"):
|
|
| 233 |
|
| 234 |
try:
|
| 235 |
chat_completion = client.chat.completions.create(
|
| 236 |
-
messages=[
|
| 237 |
-
{
|
| 238 |
-
"role": "user",
|
| 239 |
-
"content": prompt,
|
| 240 |
-
}
|
| 241 |
-
],
|
| 242 |
model="llama-3.3-70b-versatile",
|
| 243 |
)
|
| 244 |
return chat_completion.choices[0].message.content
|
|
@@ -248,38 +205,63 @@ def get_bird_info(bird_name, language="en"):
|
|
| 248 |
|
| 249 |
def predict_and_get_info(img, language="en"):
|
| 250 |
"""Predict bird species and get detailed information"""
|
| 251 |
-
# Get translations
|
| 252 |
t = translations[language]
|
| 253 |
|
| 254 |
-
# Process the image
|
| 255 |
img = PILImage.create(img)
|
| 256 |
-
|
| 257 |
-
# Get prediction
|
| 258 |
pred, pred_idx, probs = learn.predict(img)
|
| 259 |
|
| 260 |
-
# Get top 5 predictions (or all if less than 5)
|
| 261 |
num_classes = min(5, len(labels))
|
| 262 |
top_indices = probs.argsort(descending=True)[:num_classes]
|
| 263 |
top_probs = probs[top_indices]
|
| 264 |
top_labels = [labels[i] for i in top_indices]
|
| 265 |
|
| 266 |
-
# Format as dictionary with cleaned names for display
|
| 267 |
prediction_results = {clean_bird_name(top_labels[i]): float(top_probs[i]) for i in range(num_classes)}
|
| 268 |
|
| 269 |
-
# Get top prediction (original format for info retrieval)
|
| 270 |
top_bird = str(pred)
|
| 271 |
-
# Also keep a clean version for display
|
| 272 |
clean_top_bird = clean_bird_name(top_bird)
|
| 273 |
|
| 274 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 275 |
habitat_locations, is_in_tanzania = get_bird_habitat_map(top_bird)
|
| 276 |
habitat_map_html = create_habitat_map(habitat_locations)
|
| 277 |
|
| 278 |
-
# Get detailed information about the top predicted bird
|
| 279 |
bird_info = get_bird_info(top_bird, language)
|
| 280 |
formatted_info = format_bird_info(bird_info, language)
|
| 281 |
|
| 282 |
-
# Create combined info with map at the top and properly formatted information
|
| 283 |
custom_css = """
|
| 284 |
<style>
|
| 285 |
.bird-container {
|
|
@@ -344,10 +326,7 @@ def follow_up_question(question, bird_name, language="en"):
|
|
| 344 |
if not question.strip() or not bird_name:
|
| 345 |
return "Please identify a bird first and ask a specific question about it." if language == "en" else "Tafadhali tambua ndege kwanza na uulize swali maalum kuhusu ndege huyo."
|
| 346 |
|
| 347 |
-
|
| 348 |
-
lang_instruction = ""
|
| 349 |
-
if language == "sw":
|
| 350 |
-
lang_instruction = " Provide your response in Swahili language."
|
| 351 |
|
| 352 |
prompt = f"""
|
| 353 |
The researcher is asking about the {bird_name} bird: "{question}"
|
|
@@ -364,12 +343,7 @@ def follow_up_question(question, bird_name, language="en"):
|
|
| 364 |
|
| 365 |
try:
|
| 366 |
chat_completion = client.chat.completions.create(
|
| 367 |
-
messages=[
|
| 368 |
-
{
|
| 369 |
-
"role": "user",
|
| 370 |
-
"content": prompt,
|
| 371 |
-
}
|
| 372 |
-
],
|
| 373 |
model="llama-3.3-70b-versatile",
|
| 374 |
)
|
| 375 |
return chat_completion.choices[0].message.content
|
|
@@ -379,11 +353,9 @@ def follow_up_question(question, bird_name, language="en"):
|
|
| 379 |
|
| 380 |
# Create the Gradio interface
|
| 381 |
with gr.Blocks(theme=gr.themes.Soft()) as app:
|
| 382 |
-
# Current language and bird state
|
| 383 |
current_lang = gr.State("en")
|
| 384 |
current_bird = gr.State("")
|
| 385 |
|
| 386 |
-
# Header with language switcher
|
| 387 |
with gr.Row():
|
| 388 |
with gr.Column(scale=3):
|
| 389 |
title_md = gr.Markdown(f"# {translations['en']['app_title']}")
|
|
@@ -394,10 +366,8 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
| 394 |
value="English"
|
| 395 |
)
|
| 396 |
|
| 397 |
-
# App description
|
| 398 |
description_md = gr.Markdown(f"{translations['en']['app_description']}")
|
| 399 |
|
| 400 |
-
# Main identification section
|
| 401 |
with gr.Row():
|
| 402 |
with gr.Column(scale=1):
|
| 403 |
input_image = gr.Image(type="pil", label=translations['en']['upload_label'])
|
|
@@ -407,10 +377,8 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
| 407 |
prediction_output = gr.Label(label=translations['en']['predictions_label'], num_top_classes=5)
|
| 408 |
bird_info_output = gr.HTML(label=translations['en']['bird_info_label'])
|
| 409 |
|
| 410 |
-
# Clear divider
|
| 411 |
gr.Markdown("---")
|
| 412 |
|
| 413 |
-
# Follow-up question section with improved UI
|
| 414 |
questions_header = gr.Markdown(f"## {translations['en']['research_questions']}")
|
| 415 |
|
| 416 |
conversation_history = gr.Markdown("")
|
|
@@ -426,7 +394,6 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
| 426 |
follow_up_btn = gr.Button(translations['en']['submit_question'], variant="primary")
|
| 427 |
clear_btn = gr.Button(translations['en']['clear_conversation'])
|
| 428 |
|
| 429 |
-
# Functions for event handlers
|
| 430 |
def process_image(img, lang):
|
| 431 |
if img is None:
|
| 432 |
return None, translations[lang]['upload_prompt'], "", ""
|
|
@@ -446,7 +413,6 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
| 446 |
|
| 447 |
answer = follow_up_question(question, bird_name, lang)
|
| 448 |
|
| 449 |
-
# Format the conversation with clear separation
|
| 450 |
new_exchange = f"""
|
| 451 |
### {t['question_title']}
|
| 452 |
{question}
|
|
@@ -454,18 +420,15 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
| 454 |
{answer}
|
| 455 |
---
|
| 456 |
"""
|
| 457 |
-
|
| 458 |
-
return updated_history
|
| 459 |
|
| 460 |
def clear_conversation_history():
|
| 461 |
return ""
|
| 462 |
|
| 463 |
def update_language(choice):
|
| 464 |
-
# Convert selection to language code
|
| 465 |
lang = "sw" if choice == "Kiswahili" else "en"
|
| 466 |
t = translations[lang]
|
| 467 |
|
| 468 |
-
# Return updated UI components based on selected language
|
| 469 |
return (
|
| 470 |
lang,
|
| 471 |
f"# {t['app_title']}",
|
|
@@ -481,7 +444,6 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
| 481 |
t['clear_conversation']
|
| 482 |
)
|
| 483 |
|
| 484 |
-
# Set up event handlers
|
| 485 |
language_selector.change(
|
| 486 |
update_language,
|
| 487 |
inputs=[language_selector],
|
|
@@ -521,5 +483,4 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
| 521 |
outputs=[conversation_history]
|
| 522 |
)
|
| 523 |
|
| 524 |
-
# Launch the app
|
| 525 |
app.launch(share=True)
|
|
|
|
| 59 |
|
| 60 |
def clean_bird_name(name):
|
| 61 |
"""Clean bird name by removing numbers and special characters, and fix formatting"""
|
|
|
|
| 62 |
cleaned = re.sub(r'^\d+\.', '', name)
|
|
|
|
| 63 |
cleaned = cleaned.replace('_', ' ')
|
|
|
|
| 64 |
cleaned = re.sub(r'[^\w\s]', '', cleaned)
|
|
|
|
| 65 |
cleaned = ' '.join(cleaned.split())
|
| 66 |
return cleaned
|
| 67 |
|
|
|
|
| 69 |
"""Get habitat map locations for the bird using Groq API"""
|
| 70 |
clean_name = clean_bird_name(bird_name)
|
| 71 |
|
|
|
|
| 72 |
if check_tanzania:
|
| 73 |
tanzania_check_prompt = f"""
|
| 74 |
Is the {clean_name} bird native to or commonly found in Tanzania?
|
|
|
|
| 82 |
)
|
| 83 |
is_in_tanzania = "yes" in tanzania_check.choices[0].message.content.lower()
|
| 84 |
except:
|
|
|
|
| 85 |
is_in_tanzania = True
|
| 86 |
else:
|
| 87 |
is_in_tanzania = True
|
| 88 |
|
|
|
|
| 89 |
prompt = f"""
|
| 90 |
Provide a JSON array of the main habitat locations for the {clean_name} bird in the world.
|
| 91 |
Return ONLY a JSON array with 3-5 entries, each containing:
|
|
|
|
| 105 |
|
| 106 |
try:
|
| 107 |
chat_completion = client.chat.completions.create(
|
| 108 |
+
messages=[{"role": "user", "content": prompt}],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
model="llama-3.3-70b-versatile",
|
| 110 |
)
|
| 111 |
response = chat_completion.choices[0].message.content
|
| 112 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
json_match = re.search(r'\[.*\]', response, re.DOTALL)
|
| 114 |
if json_match:
|
| 115 |
locations = json.loads(json_match.group())
|
| 116 |
else:
|
|
|
|
| 117 |
locations = [
|
| 118 |
{"name": "Primary habitat region", "lat": 0, "lon": 0,
|
| 119 |
"description": "Could not retrieve specific habitat information for this bird."}
|
| 120 |
]
|
|
|
|
| 121 |
return locations, is_in_tanzania
|
| 122 |
+
except:
|
|
|
|
| 123 |
return [{"name": "Error retrieving data", "lat": 0, "lon": 0,
|
| 124 |
"description": "Please try again or check your connection."}], False
|
| 125 |
|
| 126 |
def create_habitat_map(habitat_locations):
|
| 127 |
"""Create a folium map with the habitat locations"""
|
|
|
|
| 128 |
valid_coords = [(loc.get("lat", 0), loc.get("lon", 0))
|
| 129 |
for loc in habitat_locations
|
| 130 |
if loc.get("lat", 0) != 0 or loc.get("lon", 0) != 0]
|
| 131 |
|
| 132 |
if valid_coords:
|
|
|
|
| 133 |
avg_lat = sum(lat for lat, _ in valid_coords) / len(valid_coords)
|
| 134 |
avg_lon = sum(lon for _, lon in valid_coords) / len(valid_coords)
|
|
|
|
| 135 |
m = folium.Map(location=[avg_lat, avg_lon], zoom_start=3)
|
| 136 |
else:
|
|
|
|
| 137 |
m = folium.Map(location=[20, 0], zoom_start=2)
|
| 138 |
|
|
|
|
| 139 |
for location in habitat_locations:
|
| 140 |
name = location.get("name", "Unknown")
|
| 141 |
lat = location.get("lat", 0)
|
| 142 |
lon = location.get("lon", 0)
|
| 143 |
description = location.get("description", "No description available")
|
| 144 |
|
|
|
|
| 145 |
if lat == 0 and lon == 0:
|
| 146 |
continue
|
| 147 |
|
|
|
|
| 148 |
folium.Marker(
|
| 149 |
location=[lat, lon],
|
| 150 |
popup=folium.Popup(f"<b>{name}</b><br>{description}", max_width=300),
|
| 151 |
tooltip=name
|
| 152 |
).add_to(m)
|
| 153 |
|
|
|
|
| 154 |
map_html = m._repr_html_()
|
| 155 |
return map_html
|
| 156 |
|
| 157 |
def format_bird_info(raw_info, language="en"):
|
| 158 |
"""Improve the formatting of bird information"""
|
|
|
|
| 159 |
formatted = raw_info
|
| 160 |
|
|
|
|
| 161 |
warning_text = "NOT TYPICALLY FOUND IN TANZANIA"
|
| 162 |
warning_translation = "HAPATIKANI SANA TANZANIA" if language == "sw" else warning_text
|
| 163 |
|
|
|
|
| 164 |
formatted = re.sub(r'#+\s+' + warning_text,
|
| 165 |
+
f'<div class="alert alert-warning"><strong>⚠️ {warning_translation}</strong></div>',
|
| 166 |
+
formatted)
|
| 167 |
|
|
|
|
| 168 |
formatted = re.sub(r'#+\s+(.*)', r'<h3>\1</h3>', formatted)
|
|
|
|
|
|
|
| 169 |
formatted = re.sub(r'\n\*\s+(.*)', r'<p>• \1</p>', formatted)
|
| 170 |
formatted = re.sub(r'\n([^<\n].*)', r'<p>\1</p>', formatted)
|
| 171 |
|
|
|
|
| 172 |
formatted = formatted.replace('<p><p>', '<p>')
|
| 173 |
formatted = formatted.replace('</p></p>', '</p>')
|
| 174 |
|
|
|
|
| 178 |
"""Get detailed information about a bird using Groq API"""
|
| 179 |
clean_name = clean_bird_name(bird_name)
|
| 180 |
|
| 181 |
+
lang_instruction = "" if language == "en" else " Provide your response in Swahili language."
|
|
|
|
|
|
|
|
|
|
| 182 |
|
| 183 |
prompt = f"""
|
| 184 |
Provide detailed information about the {clean_name} bird, including:
|
|
|
|
| 195 |
|
| 196 |
try:
|
| 197 |
chat_completion = client.chat.completions.create(
|
| 198 |
+
messages=[{"role": "user", "content": prompt}],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
model="llama-3.3-70b-versatile",
|
| 200 |
)
|
| 201 |
return chat_completion.choices[0].message.content
|
|
|
|
| 205 |
|
| 206 |
def predict_and_get_info(img, language="en"):
|
| 207 |
"""Predict bird species and get detailed information"""
|
|
|
|
| 208 |
t = translations[language]
|
| 209 |
|
|
|
|
| 210 |
img = PILImage.create(img)
|
|
|
|
|
|
|
| 211 |
pred, pred_idx, probs = learn.predict(img)
|
| 212 |
|
|
|
|
| 213 |
num_classes = min(5, len(labels))
|
| 214 |
top_indices = probs.argsort(descending=True)[:num_classes]
|
| 215 |
top_probs = probs[top_indices]
|
| 216 |
top_labels = [labels[i] for i in top_indices]
|
| 217 |
|
|
|
|
| 218 |
prediction_results = {clean_bird_name(top_labels[i]): float(top_probs[i]) for i in range(num_classes)}
|
| 219 |
|
|
|
|
| 220 |
top_bird = str(pred)
|
|
|
|
| 221 |
clean_top_bird = clean_bird_name(top_bird)
|
| 222 |
|
| 223 |
+
if top_bird.lower() == "other" and float(probs[pred_idx]) > 0.8:
|
| 224 |
+
message = (
|
| 225 |
+
"This image does not appear to match any bird species in our trained dataset. "
|
| 226 |
+
"Please upload a clear image of a bird for accurate identification."
|
| 227 |
+
) if language == "en" else (
|
| 228 |
+
"Picha hii haionekani kulingana na spishi yoyote ya ndege katika seti yetu ya mafunzo. "
|
| 229 |
+
"Tafadhali pakia picha ya wazi ya ndege kwa utambuzi sahihi."
|
| 230 |
+
)
|
| 231 |
+
|
| 232 |
+
custom_css = """
|
| 233 |
+
<style>
|
| 234 |
+
.bird-container {
|
| 235 |
+
font-family: Arial, sans-serif;
|
| 236 |
+
padding: 10px;
|
| 237 |
+
}
|
| 238 |
+
.alert-info {
|
| 239 |
+
background-color: #d9edf7;
|
| 240 |
+
border: 1px solid #bce8f1;
|
| 241 |
+
color: #31708f;
|
| 242 |
+
padding: 10px;
|
| 243 |
+
margin-bottom: 15px;
|
| 244 |
+
border-radius: 4px;
|
| 245 |
+
}
|
| 246 |
+
</style>
|
| 247 |
+
"""
|
| 248 |
+
|
| 249 |
+
combined_info = f"""
|
| 250 |
+
{custom_css}
|
| 251 |
+
<div class="bird-container">
|
| 252 |
+
<div class="alert-info">
|
| 253 |
+
<strong>ℹ️ {message}</strong>
|
| 254 |
+
</div>
|
| 255 |
+
</div>
|
| 256 |
+
"""
|
| 257 |
+
return prediction_results, combined_info, ""
|
| 258 |
+
|
| 259 |
habitat_locations, is_in_tanzania = get_bird_habitat_map(top_bird)
|
| 260 |
habitat_map_html = create_habitat_map(habitat_locations)
|
| 261 |
|
|
|
|
| 262 |
bird_info = get_bird_info(top_bird, language)
|
| 263 |
formatted_info = format_bird_info(bird_info, language)
|
| 264 |
|
|
|
|
| 265 |
custom_css = """
|
| 266 |
<style>
|
| 267 |
.bird-container {
|
|
|
|
| 326 |
if not question.strip() or not bird_name:
|
| 327 |
return "Please identify a bird first and ask a specific question about it." if language == "en" else "Tafadhali tambua ndege kwanza na uulize swali maalum kuhusu ndege huyo."
|
| 328 |
|
| 329 |
+
lang_instruction = "" if language == "en" else " Provide your response in Swahili language."
|
|
|
|
|
|
|
|
|
|
| 330 |
|
| 331 |
prompt = f"""
|
| 332 |
The researcher is asking about the {bird_name} bird: "{question}"
|
|
|
|
| 343 |
|
| 344 |
try:
|
| 345 |
chat_completion = client.chat.completions.create(
|
| 346 |
+
messages=[{"role": "user", "content": prompt}],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
model="llama-3.3-70b-versatile",
|
| 348 |
)
|
| 349 |
return chat_completion.choices[0].message.content
|
|
|
|
| 353 |
|
| 354 |
# Create the Gradio interface
|
| 355 |
with gr.Blocks(theme=gr.themes.Soft()) as app:
|
|
|
|
| 356 |
current_lang = gr.State("en")
|
| 357 |
current_bird = gr.State("")
|
| 358 |
|
|
|
|
| 359 |
with gr.Row():
|
| 360 |
with gr.Column(scale=3):
|
| 361 |
title_md = gr.Markdown(f"# {translations['en']['app_title']}")
|
|
|
|
| 366 |
value="English"
|
| 367 |
)
|
| 368 |
|
|
|
|
| 369 |
description_md = gr.Markdown(f"{translations['en']['app_description']}")
|
| 370 |
|
|
|
|
| 371 |
with gr.Row():
|
| 372 |
with gr.Column(scale=1):
|
| 373 |
input_image = gr.Image(type="pil", label=translations['en']['upload_label'])
|
|
|
|
| 377 |
prediction_output = gr.Label(label=translations['en']['predictions_label'], num_top_classes=5)
|
| 378 |
bird_info_output = gr.HTML(label=translations['en']['bird_info_label'])
|
| 379 |
|
|
|
|
| 380 |
gr.Markdown("---")
|
| 381 |
|
|
|
|
| 382 |
questions_header = gr.Markdown(f"## {translations['en']['research_questions']}")
|
| 383 |
|
| 384 |
conversation_history = gr.Markdown("")
|
|
|
|
| 394 |
follow_up_btn = gr.Button(translations['en']['submit_question'], variant="primary")
|
| 395 |
clear_btn = gr.Button(translations['en']['clear_conversation'])
|
| 396 |
|
|
|
|
| 397 |
def process_image(img, lang):
|
| 398 |
if img is None:
|
| 399 |
return None, translations[lang]['upload_prompt'], "", ""
|
|
|
|
| 413 |
|
| 414 |
answer = follow_up_question(question, bird_name, lang)
|
| 415 |
|
|
|
|
| 416 |
new_exchange = f"""
|
| 417 |
### {t['question_title']}
|
| 418 |
{question}
|
|
|
|
| 420 |
{answer}
|
| 421 |
---
|
| 422 |
"""
|
| 423 |
+
return new_exchange + history
|
|
|
|
| 424 |
|
| 425 |
def clear_conversation_history():
|
| 426 |
return ""
|
| 427 |
|
| 428 |
def update_language(choice):
|
|
|
|
| 429 |
lang = "sw" if choice == "Kiswahili" else "en"
|
| 430 |
t = translations[lang]
|
| 431 |
|
|
|
|
| 432 |
return (
|
| 433 |
lang,
|
| 434 |
f"# {t['app_title']}",
|
|
|
|
| 444 |
t['clear_conversation']
|
| 445 |
)
|
| 446 |
|
|
|
|
| 447 |
language_selector.change(
|
| 448 |
update_language,
|
| 449 |
inputs=[language_selector],
|
|
|
|
| 483 |
outputs=[conversation_history]
|
| 484 |
)
|
| 485 |
|
|
|
|
| 486 |
app.launch(share=True)
|