Spaces:
Sleeping
Sleeping
OliverPerrin
commited on
Commit
Β·
2906d4e
1
Parent(s):
a265cf2
Fix Gradio demo: whole-book summaries, emotion sorting, styling
Browse files- scripts/demo_gradio.py +86 -32
scripts/demo_gradio.py
CHANGED
|
@@ -76,7 +76,7 @@ TOPIC_EMOJI = {
|
|
| 76 |
|
| 77 |
|
| 78 |
def load_books_data() -> list[dict[str, Any]]:
|
| 79 |
-
"""Load book
|
| 80 |
books = []
|
| 81 |
library_path = BOOKS_DIR / "library.json"
|
| 82 |
|
|
@@ -104,6 +104,9 @@ def load_books_data() -> list[dict[str, Any]]:
|
|
| 104 |
"title": title,
|
| 105 |
"paragraphs": paragraphs[:20], # Limit to first 20 substantial paragraphs
|
| 106 |
"word_count": book_info.get("word_count", 0),
|
|
|
|
|
|
|
|
|
|
| 107 |
})
|
| 108 |
|
| 109 |
return books
|
|
@@ -200,13 +203,18 @@ def analyze_text(text: str) -> tuple[str, str, str]:
|
|
| 200 |
emotions = pipe.predict_emotions([text], threshold=0.3)[0]
|
| 201 |
topic = pipe.predict_topics([text])[0]
|
| 202 |
|
| 203 |
-
# Format emotions
|
| 204 |
if emotions.labels:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
emotion_parts = []
|
| 206 |
-
for lbl, score in
|
| 207 |
emoji = EMOTION_EMOJI.get(lbl.lower(), "β’")
|
| 208 |
emotion_parts.append(f"{emoji} **{lbl.title()}** ({score:.0%})")
|
| 209 |
-
|
|
|
|
| 210 |
else:
|
| 211 |
emotion_str = "π No strong emotions detected"
|
| 212 |
|
|
@@ -249,15 +257,47 @@ def get_book_info(title: str) -> str:
|
|
| 249 |
if book["title"] == title:
|
| 250 |
num_paras = len(book["paragraphs"])
|
| 251 |
word_count = book["word_count"]
|
| 252 |
-
|
|
|
|
| 253 |
return ""
|
| 254 |
|
| 255 |
|
| 256 |
-
def
|
| 257 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
info = get_book_info(title)
|
| 259 |
excerpt = get_book_excerpt(title, 0)
|
| 260 |
-
|
|
|
|
| 261 |
|
| 262 |
|
| 263 |
def on_paragraph_change(title: str, idx: int) -> str:
|
|
@@ -356,7 +396,8 @@ with gr.Blocks(
|
|
| 356 |
title="LexiMind - Multi-Task NLP",
|
| 357 |
theme=gr.themes.Soft(),
|
| 358 |
css="""
|
| 359 |
-
.book-card { padding:
|
|
|
|
| 360 |
.results-panel { min-height: 200px; }
|
| 361 |
"""
|
| 362 |
) as demo:
|
|
@@ -379,8 +420,8 @@ with gr.Blocks(
|
|
| 379 |
gr.Markdown(
|
| 380 |
"""
|
| 381 |
### Classic Literature Collection
|
| 382 |
-
|
| 383 |
-
|
| 384 |
"""
|
| 385 |
)
|
| 386 |
|
|
@@ -393,48 +434,61 @@ with gr.Blocks(
|
|
| 393 |
)
|
| 394 |
book_info = gr.Markdown(elem_classes=["book-card"])
|
| 395 |
|
|
|
|
|
|
|
| 396 |
para_slider = gr.Slider(
|
| 397 |
minimum=0,
|
| 398 |
maximum=19,
|
| 399 |
step=1,
|
| 400 |
value=0,
|
| 401 |
-
label="
|
| 402 |
info="Navigate through different parts of the book"
|
| 403 |
)
|
| 404 |
|
| 405 |
-
|
| 406 |
|
| 407 |
with gr.Column(scale=2):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 408 |
book_excerpt = gr.Textbox(
|
| 409 |
-
label="
|
| 410 |
-
lines=
|
| 411 |
-
max_lines=
|
| 412 |
interactive=False,
|
|
|
|
| 413 |
)
|
| 414 |
|
| 415 |
with gr.Row():
|
| 416 |
with gr.Column():
|
| 417 |
-
|
| 418 |
-
label="π
|
| 419 |
-
lines=
|
| 420 |
interactive=False,
|
| 421 |
)
|
| 422 |
with gr.Column():
|
| 423 |
-
|
| 424 |
-
book_emotions = gr.Markdown(
|
| 425 |
-
label="π Emotions",
|
| 426 |
-
value="*Click Analyze*",
|
| 427 |
-
)
|
| 428 |
-
book_topic = gr.Markdown(
|
| 429 |
-
label="π Topic",
|
| 430 |
-
value="*Click Analyze*",
|
| 431 |
-
)
|
| 432 |
|
| 433 |
# Book event handlers
|
| 434 |
book_dropdown.change(
|
| 435 |
fn=on_book_select,
|
| 436 |
inputs=[book_dropdown],
|
| 437 |
-
outputs=[book_info, book_excerpt,
|
| 438 |
)
|
| 439 |
|
| 440 |
para_slider.change(
|
|
@@ -443,17 +497,17 @@ with gr.Blocks(
|
|
| 443 |
outputs=[book_excerpt],
|
| 444 |
)
|
| 445 |
|
| 446 |
-
|
| 447 |
fn=analyze_text,
|
| 448 |
inputs=[book_excerpt],
|
| 449 |
-
outputs=[
|
| 450 |
)
|
| 451 |
|
| 452 |
# Initialize with first book
|
| 453 |
demo.load(
|
| 454 |
fn=on_book_select,
|
| 455 |
inputs=[book_dropdown],
|
| 456 |
-
outputs=[book_info, book_excerpt,
|
| 457 |
)
|
| 458 |
|
| 459 |
# ===================== TAB 2: EXPLORE NEWS =====================
|
|
|
|
| 76 |
|
| 77 |
|
| 78 |
def load_books_data() -> list[dict[str, Any]]:
|
| 79 |
+
"""Load book data including pre-computed summaries from library.json."""
|
| 80 |
books = []
|
| 81 |
library_path = BOOKS_DIR / "library.json"
|
| 82 |
|
|
|
|
| 104 |
"title": title,
|
| 105 |
"paragraphs": paragraphs[:20], # Limit to first 20 substantial paragraphs
|
| 106 |
"word_count": book_info.get("word_count", 0),
|
| 107 |
+
"summary": book_info.get("summary", ""),
|
| 108 |
+
"topic": book_info.get("topic", ""),
|
| 109 |
+
"emotions": book_info.get("emotions", []),
|
| 110 |
})
|
| 111 |
|
| 112 |
return books
|
|
|
|
| 203 |
emotions = pipe.predict_emotions([text], threshold=0.3)[0]
|
| 204 |
topic = pipe.predict_topics([text])[0]
|
| 205 |
|
| 206 |
+
# Format emotions - sort by score descending (not alphabetical)
|
| 207 |
if emotions.labels:
|
| 208 |
+
# Pair labels with scores and sort by score descending
|
| 209 |
+
paired = list(zip(emotions.labels, emotions.scores, strict=False))
|
| 210 |
+
paired_sorted = sorted(paired, key=lambda x: x[1], reverse=True)[:5]
|
| 211 |
+
|
| 212 |
emotion_parts = []
|
| 213 |
+
for lbl, score in paired_sorted:
|
| 214 |
emoji = EMOTION_EMOJI.get(lbl.lower(), "β’")
|
| 215 |
emotion_parts.append(f"{emoji} **{lbl.title()}** ({score:.0%})")
|
| 216 |
+
# Use double newline for proper Markdown paragraph breaks
|
| 217 |
+
emotion_str = " \n".join(emotion_parts)
|
| 218 |
else:
|
| 219 |
emotion_str = "π No strong emotions detected"
|
| 220 |
|
|
|
|
| 257 |
if book["title"] == title:
|
| 258 |
num_paras = len(book["paragraphs"])
|
| 259 |
word_count = book["word_count"]
|
| 260 |
+
topic = book.get("topic", "Unknown")
|
| 261 |
+
return f"**{title}**\n\nπ {word_count:,} words | {num_paras} excerpts\n\nπ Topic: {topic}"
|
| 262 |
return ""
|
| 263 |
|
| 264 |
|
| 265 |
+
def get_book_summary_and_emotions(title: str) -> tuple[str, str, str]:
|
| 266 |
+
"""Get pre-computed book summary and emotions from library.json."""
|
| 267 |
+
books = get_books()
|
| 268 |
+
for book in books:
|
| 269 |
+
if book["title"] == title:
|
| 270 |
+
# Get summary
|
| 271 |
+
summary = book.get("summary", "No summary available.")
|
| 272 |
+
|
| 273 |
+
# Get topic
|
| 274 |
+
topic = book.get("topic", "Unknown")
|
| 275 |
+
topic_emoji = TOPIC_EMOJI.get(topic, "π")
|
| 276 |
+
topic_str = f"{topic_emoji} **{topic}**"
|
| 277 |
+
|
| 278 |
+
# Get emotions - already sorted by score in library.json
|
| 279 |
+
emotions = book.get("emotions", [])
|
| 280 |
+
if emotions:
|
| 281 |
+
emotion_parts = []
|
| 282 |
+
for emo in emotions[:5]:
|
| 283 |
+
label = emo.get("label", "")
|
| 284 |
+
score = emo.get("score", 0)
|
| 285 |
+
emoji = EMOTION_EMOJI.get(label.lower(), "β’")
|
| 286 |
+
emotion_parts.append(f"{emoji} **{label.title()}** ({score:.0%})")
|
| 287 |
+
emotion_str = " \n".join(emotion_parts)
|
| 288 |
+
else:
|
| 289 |
+
emotion_str = "π No strong emotions detected"
|
| 290 |
+
|
| 291 |
+
return summary, emotion_str, topic_str
|
| 292 |
+
return "No summary available.", "", ""
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
def on_book_select(title: str) -> tuple[str, str, str, str, str]:
|
| 296 |
+
"""Handle book selection - return book info, excerpt, and pre-computed analysis."""
|
| 297 |
info = get_book_info(title)
|
| 298 |
excerpt = get_book_excerpt(title, 0)
|
| 299 |
+
summary, emotions, topic = get_book_summary_and_emotions(title)
|
| 300 |
+
return info, excerpt, summary, emotions, topic
|
| 301 |
|
| 302 |
|
| 303 |
def on_paragraph_change(title: str, idx: int) -> str:
|
|
|
|
| 396 |
title="LexiMind - Multi-Task NLP",
|
| 397 |
theme=gr.themes.Soft(),
|
| 398 |
css="""
|
| 399 |
+
.book-card { padding: 12px; border-radius: 8px; background: #2d3748; color: #f7fafc; }
|
| 400 |
+
.book-card p { color: #f7fafc !important; }
|
| 401 |
.results-panel { min-height: 200px; }
|
| 402 |
"""
|
| 403 |
) as demo:
|
|
|
|
| 420 |
gr.Markdown(
|
| 421 |
"""
|
| 422 |
### Classic Literature Collection
|
| 423 |
+
Select a book to see LexiMind's **whole-book analysis**: summary, emotions, and topic.
|
| 424 |
+
You can also browse excerpts and analyze them individually.
|
| 425 |
"""
|
| 426 |
)
|
| 427 |
|
|
|
|
| 434 |
)
|
| 435 |
book_info = gr.Markdown(elem_classes=["book-card"])
|
| 436 |
|
| 437 |
+
gr.Markdown("---")
|
| 438 |
+
gr.Markdown("**π Browse Excerpts:**")
|
| 439 |
para_slider = gr.Slider(
|
| 440 |
minimum=0,
|
| 441 |
maximum=19,
|
| 442 |
step=1,
|
| 443 |
value=0,
|
| 444 |
+
label="Excerpt Number",
|
| 445 |
info="Navigate through different parts of the book"
|
| 446 |
)
|
| 447 |
|
| 448 |
+
analyze_excerpt_btn = gr.Button("π Analyze This Excerpt", variant="secondary", size="sm")
|
| 449 |
|
| 450 |
with gr.Column(scale=2):
|
| 451 |
+
gr.Markdown("#### π Book Summary")
|
| 452 |
+
book_summary = gr.Textbox(
|
| 453 |
+
label="",
|
| 454 |
+
lines=4,
|
| 455 |
+
interactive=False,
|
| 456 |
+
show_label=False,
|
| 457 |
+
)
|
| 458 |
+
|
| 459 |
+
with gr.Row():
|
| 460 |
+
with gr.Column():
|
| 461 |
+
gr.Markdown("#### π Emotions")
|
| 462 |
+
book_emotions = gr.Markdown(value="*Select a book*")
|
| 463 |
+
with gr.Column():
|
| 464 |
+
gr.Markdown("#### π Topic")
|
| 465 |
+
book_topic = gr.Markdown(value="*Select a book*")
|
| 466 |
+
|
| 467 |
+
gr.Markdown("---")
|
| 468 |
+
gr.Markdown("#### π Book Excerpt")
|
| 469 |
book_excerpt = gr.Textbox(
|
| 470 |
+
label="",
|
| 471 |
+
lines=6,
|
| 472 |
+
max_lines=10,
|
| 473 |
interactive=False,
|
| 474 |
+
show_label=False,
|
| 475 |
)
|
| 476 |
|
| 477 |
with gr.Row():
|
| 478 |
with gr.Column():
|
| 479 |
+
excerpt_summary = gr.Textbox(
|
| 480 |
+
label="π Excerpt Summary",
|
| 481 |
+
lines=3,
|
| 482 |
interactive=False,
|
| 483 |
)
|
| 484 |
with gr.Column():
|
| 485 |
+
excerpt_emotions = gr.Markdown(value="*Click Analyze Excerpt*")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 486 |
|
| 487 |
# Book event handlers
|
| 488 |
book_dropdown.change(
|
| 489 |
fn=on_book_select,
|
| 490 |
inputs=[book_dropdown],
|
| 491 |
+
outputs=[book_info, book_excerpt, book_summary, book_emotions, book_topic],
|
| 492 |
)
|
| 493 |
|
| 494 |
para_slider.change(
|
|
|
|
| 497 |
outputs=[book_excerpt],
|
| 498 |
)
|
| 499 |
|
| 500 |
+
analyze_excerpt_btn.click(
|
| 501 |
fn=analyze_text,
|
| 502 |
inputs=[book_excerpt],
|
| 503 |
+
outputs=[excerpt_summary, excerpt_emotions, book_topic],
|
| 504 |
)
|
| 505 |
|
| 506 |
# Initialize with first book
|
| 507 |
demo.load(
|
| 508 |
fn=on_book_select,
|
| 509 |
inputs=[book_dropdown],
|
| 510 |
+
outputs=[book_info, book_excerpt, book_summary, book_emotions, book_topic],
|
| 511 |
)
|
| 512 |
|
| 513 |
# ===================== TAB 2: EXPLORE NEWS =====================
|