Finalizing README.md
Browse files
app.py
CHANGED
|
@@ -645,6 +645,92 @@ footer a,
|
|
| 645 |
transform: translateY(0);
|
| 646 |
}
|
| 647 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 648 |
"""
|
| 649 |
|
| 650 |
# ============================================================================
|
|
@@ -1475,198 +1561,209 @@ with gr.Blocks() as demo:
|
|
| 1475 |
|
| 1476 |
# Step 3: Main App
|
| 1477 |
with gr.Step("BirdScope AI", id=3):
|
| 1478 |
-
with gr.
|
| 1479 |
-
|
| 1480 |
-
with gr.Column(scale=2):
|
| 1481 |
-
chatbot = gr.Chatbot(
|
| 1482 |
-
show_label=False,
|
| 1483 |
-
height=500,
|
| 1484 |
-
elem_classes=["chatbot-container"]
|
| 1485 |
-
)
|
| 1486 |
-
|
| 1487 |
-
msg = gr.MultimodalTextbox(
|
| 1488 |
-
placeholder="Ask about birds or upload an image...",
|
| 1489 |
-
file_count="single",
|
| 1490 |
-
file_types=["image"],
|
| 1491 |
-
interactive=True,
|
| 1492 |
-
show_label=False
|
| 1493 |
-
)
|
| 1494 |
-
|
| 1495 |
with gr.Row():
|
| 1496 |
-
|
| 1497 |
-
|
| 1498 |
-
|
| 1499 |
-
|
| 1500 |
-
|
| 1501 |
-
|
| 1502 |
-
|
| 1503 |
-
|
| 1504 |
-
|
| 1505 |
-
|
| 1506 |
-
|
| 1507 |
-
|
| 1508 |
-
|
| 1509 |
-
|
| 1510 |
-
|
| 1511 |
-
|
| 1512 |
-
|
| 1513 |
-
|
| 1514 |
-
|
| 1515 |
-
|
| 1516 |
-
|
| 1517 |
-
|
| 1518 |
-
|
| 1519 |
-
|
| 1520 |
-
|
| 1521 |
-
|
| 1522 |
-
|
| 1523 |
-
|
| 1524 |
-
|
| 1525 |
-
|
| 1526 |
-
|
| 1527 |
-
|
| 1528 |
-
|
| 1529 |
-
|
| 1530 |
-
|
| 1531 |
-
|
| 1532 |
-
|
| 1533 |
-
|
| 1534 |
-
|
| 1535 |
-
|
| 1536 |
-
|
| 1537 |
-
|
| 1538 |
-
|
| 1539 |
-
|
| 1540 |
-
|
| 1541 |
-
|
| 1542 |
-
|
| 1543 |
-
|
| 1544 |
-
|
| 1545 |
-
|
| 1546 |
-
|
| 1547 |
-
|
| 1548 |
-
|
| 1549 |
-
|
| 1550 |
-
|
| 1551 |
-
|
| 1552 |
-
|
| 1553 |
-
|
| 1554 |
-
|
| 1555 |
-
|
| 1556 |
-
|
| 1557 |
-
|
| 1558 |
-
|
| 1559 |
-
|
| 1560 |
-
|
| 1561 |
-
|
| 1562 |
-
|
| 1563 |
-
|
| 1564 |
-
|
| 1565 |
-
|
| 1566 |
-
|
| 1567 |
-
|
| 1568 |
-
|
| 1569 |
-
|
| 1570 |
-
|
| 1571 |
-
|
| 1572 |
-
|
| 1573 |
-
|
| 1574 |
-
|
| 1575 |
-
|
| 1576 |
-
|
| 1577 |
-
|
| 1578 |
-
|
| 1579 |
-
|
| 1580 |
-
|
| 1581 |
-
|
| 1582 |
-
|
| 1583 |
-
|
| 1584 |
-
|
| 1585 |
-
|
| 1586 |
-
|
| 1587 |
-
|
| 1588 |
-
|
| 1589 |
-
|
| 1590 |
-
|
| 1591 |
-
|
| 1592 |
-
|
| 1593 |
-
|
| 1594 |
-
|
| 1595 |
-
|
| 1596 |
-
|
| 1597 |
-
|
| 1598 |
-
|
| 1599 |
-
|
| 1600 |
-
|
| 1601 |
-
|
| 1602 |
-
|
| 1603 |
-
|
| 1604 |
-
|
| 1605 |
-
|
| 1606 |
-
|
| 1607 |
-
|
| 1608 |
-
|
| 1609 |
-
|
| 1610 |
-
|
| 1611 |
-
|
| 1612 |
-
|
| 1613 |
-
|
| 1614 |
-
|
| 1615 |
-
|
| 1616 |
-
|
| 1617 |
-
|
| 1618 |
-
|
| 1619 |
-
|
| 1620 |
-
|
| 1621 |
-
|
| 1622 |
-
|
| 1623 |
-
|
| 1624 |
-
|
| 1625 |
-
|
| 1626 |
-
|
| 1627 |
-
|
| 1628 |
-
|
| 1629 |
-
|
| 1630 |
-
|
| 1631 |
-
|
| 1632 |
-
|
| 1633 |
-
|
| 1634 |
-
|
| 1635 |
-
|
| 1636 |
-
|
| 1637 |
-
|
| 1638 |
-
|
| 1639 |
-
|
| 1640 |
-
|
| 1641 |
-
|
| 1642 |
-
|
| 1643 |
-
|
| 1644 |
-
|
| 1645 |
-
|
| 1646 |
-
|
| 1647 |
-
|
| 1648 |
-
|
| 1649 |
-
|
| 1650 |
-
|
| 1651 |
-
|
| 1652 |
-
|
| 1653 |
-
|
| 1654 |
-
|
| 1655 |
-
|
| 1656 |
-
|
| 1657 |
-
|
| 1658 |
-
|
| 1659 |
-
|
| 1660 |
-
|
| 1661 |
-
|
| 1662 |
-
|
| 1663 |
-
|
| 1664 |
-
|
| 1665 |
-
|
| 1666 |
-
|
| 1667 |
-
|
| 1668 |
-
|
| 1669 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1670 |
|
| 1671 |
# State for tool log
|
| 1672 |
tool_log_state = gr.State("*Waiting for tool calls...*")
|
|
|
|
| 645 |
transform: translateY(0);
|
| 646 |
}
|
| 647 |
}
|
| 648 |
+
|
| 649 |
+
/* ========================================================================
|
| 650 |
+
README TAB STYLING - BLACK TEXT ON WHITE BACKGROUND
|
| 651 |
+
======================================================================== */
|
| 652 |
+
|
| 653 |
+
.readme-tab-container {
|
| 654 |
+
background-color: #ffffff !important;
|
| 655 |
+
padding: 2rem !important;
|
| 656 |
+
border-radius: 12px !important;
|
| 657 |
+
max-width: 1200px !important;
|
| 658 |
+
margin: 1rem auto !important;
|
| 659 |
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08) !important;
|
| 660 |
+
}
|
| 661 |
+
|
| 662 |
+
.readme-markdown,
|
| 663 |
+
.readme-markdown *,
|
| 664 |
+
.readme-markdown h1,
|
| 665 |
+
.readme-markdown h2,
|
| 666 |
+
.readme-markdown h3,
|
| 667 |
+
.readme-markdown h4,
|
| 668 |
+
.readme-markdown h5,
|
| 669 |
+
.readme-markdown h6,
|
| 670 |
+
.readme-markdown p,
|
| 671 |
+
.readme-markdown li,
|
| 672 |
+
.readme-markdown span,
|
| 673 |
+
.readme-markdown div,
|
| 674 |
+
.readme-markdown strong,
|
| 675 |
+
.readme-markdown em,
|
| 676 |
+
.readme-markdown code {
|
| 677 |
+
color: #000000 !important;
|
| 678 |
+
background-color: transparent !important;
|
| 679 |
+
}
|
| 680 |
+
|
| 681 |
+
.readme-markdown a {
|
| 682 |
+
color: #2563eb !important;
|
| 683 |
+
text-decoration: underline !important;
|
| 684 |
+
}
|
| 685 |
+
|
| 686 |
+
.readme-markdown a:hover {
|
| 687 |
+
color: #1d4ed8 !important;
|
| 688 |
+
}
|
| 689 |
+
|
| 690 |
+
.readme-markdown code {
|
| 691 |
+
background-color: #f3f4f6 !important;
|
| 692 |
+
padding: 2px 6px !important;
|
| 693 |
+
border-radius: 4px !important;
|
| 694 |
+
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Consolas', monospace !important;
|
| 695 |
+
}
|
| 696 |
+
|
| 697 |
+
.readme-markdown pre {
|
| 698 |
+
background-color: #f3f4f6 !important;
|
| 699 |
+
padding: 1rem !important;
|
| 700 |
+
border-radius: 8px !important;
|
| 701 |
+
overflow-x: auto !important;
|
| 702 |
+
}
|
| 703 |
+
|
| 704 |
+
.readme-markdown pre code {
|
| 705 |
+
background-color: transparent !important;
|
| 706 |
+
padding: 0 !important;
|
| 707 |
+
}
|
| 708 |
+
|
| 709 |
+
.readme-markdown blockquote {
|
| 710 |
+
border-left: 4px solid #e5e7eb !important;
|
| 711 |
+
padding-left: 1rem !important;
|
| 712 |
+
color: #4b5563 !important;
|
| 713 |
+
}
|
| 714 |
+
|
| 715 |
+
.readme-markdown hr {
|
| 716 |
+
border-top: 1px solid #e5e7eb !important;
|
| 717 |
+
}
|
| 718 |
+
|
| 719 |
+
.readme-markdown table {
|
| 720 |
+
border-collapse: collapse !important;
|
| 721 |
+
width: 100% !important;
|
| 722 |
+
}
|
| 723 |
+
|
| 724 |
+
.readme-markdown table th,
|
| 725 |
+
.readme-markdown table td {
|
| 726 |
+
border: 1px solid #e5e7eb !important;
|
| 727 |
+
padding: 0.5rem !important;
|
| 728 |
+
}
|
| 729 |
+
|
| 730 |
+
.readme-markdown table th {
|
| 731 |
+
background-color: #f9fafb !important;
|
| 732 |
+
font-weight: 600 !important;
|
| 733 |
+
}
|
| 734 |
"""
|
| 735 |
|
| 736 |
# ============================================================================
|
|
|
|
| 1561 |
|
| 1562 |
# Step 3: Main App
|
| 1563 |
with gr.Step("BirdScope AI", id=3):
|
| 1564 |
+
with gr.Tabs():
|
| 1565 |
+
with gr.Tab("💬 Chat"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1566 |
with gr.Row():
|
| 1567 |
+
# Left: Chat interface (scale=2)
|
| 1568 |
+
with gr.Column(scale=2):
|
| 1569 |
+
chatbot = gr.Chatbot(
|
| 1570 |
+
show_label=False,
|
| 1571 |
+
height=500,
|
| 1572 |
+
elem_classes=["chatbot-container"]
|
| 1573 |
+
)
|
| 1574 |
+
|
| 1575 |
+
msg = gr.MultimodalTextbox(
|
| 1576 |
+
placeholder="Ask about birds or upload an image...",
|
| 1577 |
+
file_count="single",
|
| 1578 |
+
file_types=["image"],
|
| 1579 |
+
interactive=True,
|
| 1580 |
+
show_label=False
|
| 1581 |
+
)
|
| 1582 |
+
|
| 1583 |
+
with gr.Row():
|
| 1584 |
+
submit = gr.Button("Send", scale=3)
|
| 1585 |
+
clear = gr.Button("Clear", scale=1)
|
| 1586 |
+
|
| 1587 |
+
# Photo examples - always shown (static)
|
| 1588 |
+
gr.Markdown("**Try uploading a bird photo:**")
|
| 1589 |
+
gr.Examples(
|
| 1590 |
+
examples=PHOTO_EXAMPLES,
|
| 1591 |
+
inputs=msg,
|
| 1592 |
+
cache_examples=False
|
| 1593 |
+
)
|
| 1594 |
+
|
| 1595 |
+
# Text examples - change based on agent mode (dynamic)
|
| 1596 |
+
gr.Markdown("**Or try a text query:**")
|
| 1597 |
+
text_examples = gr.Examples(
|
| 1598 |
+
examples=MULTI_AGENT_TEXT_EXAMPLES, # Default to multi-agent text examples
|
| 1599 |
+
inputs=msg,
|
| 1600 |
+
cache_examples=False
|
| 1601 |
+
)
|
| 1602 |
+
|
| 1603 |
+
# Middle: Tool execution log (scale=1)
|
| 1604 |
+
with gr.Column(scale=1):
|
| 1605 |
+
tool_output = gr.Textbox(
|
| 1606 |
+
value="*Waiting for tool calls...*",
|
| 1607 |
+
elem_classes=["tool-log-panel"],
|
| 1608 |
+
elem_id="tool-log-output",
|
| 1609 |
+
autoscroll=True,
|
| 1610 |
+
show_label=False,
|
| 1611 |
+
interactive=False,
|
| 1612 |
+
container=False
|
| 1613 |
+
)
|
| 1614 |
+
|
| 1615 |
+
# Right: Sidebar (scale=1)
|
| 1616 |
+
with gr.Column(scale=1, elem_classes=["sidebar"]):
|
| 1617 |
+
|
| 1618 |
+
# MCP Server Status Check
|
| 1619 |
+
mcp_status_html = gr.HTML("""
|
| 1620 |
+
<div class="mcp-badge online" style="margin-bottom: 16px; justify-content: center;">
|
| 1621 |
+
<span class="mcp-pulse"></span>
|
| 1622 |
+
<span>Powered by Modal MCP</span>
|
| 1623 |
+
</div>
|
| 1624 |
+
""")
|
| 1625 |
+
check_mcp_btn = gr.Button("Check Modal MCP Server Status", size="sm", variant="secondary", elem_classes=["modal-check-btn"])
|
| 1626 |
+
|
| 1627 |
+
gr.HTML("""
|
| 1628 |
+
<p style="font-size: 0.75rem; color: #9ca3af; margin-top: 8px; margin-bottom: 16px; line-height: 1.4;">
|
| 1629 |
+
Please be patient if the Modal MCP server needs to cold start
|
| 1630 |
+
</p>
|
| 1631 |
+
""")
|
| 1632 |
+
|
| 1633 |
+
gr.Markdown("---")
|
| 1634 |
+
|
| 1635 |
+
# Provider selection
|
| 1636 |
+
gr.Markdown("### SELECT LLM PROVIDER")
|
| 1637 |
+
provider = gr.Dropdown(
|
| 1638 |
+
choices=["HuggingFace", "OpenAI", "Anthropic"],
|
| 1639 |
+
value="OpenAI",
|
| 1640 |
+
show_label=False,
|
| 1641 |
+
container=False
|
| 1642 |
+
)
|
| 1643 |
+
|
| 1644 |
+
# Agent Mode Selector
|
| 1645 |
+
gr.Markdown("**Agent Configuration**")
|
| 1646 |
+
gr.Markdown("Choose between unified agent or specialized routing")
|
| 1647 |
+
agent_mode = gr.Dropdown(
|
| 1648 |
+
choices=[
|
| 1649 |
+
"Supervisor (Multi-Agent)"
|
| 1650 |
+
],
|
| 1651 |
+
value="Supervisor (Multi-Agent)",
|
| 1652 |
+
show_label=False,
|
| 1653 |
+
container=False
|
| 1654 |
+
)
|
| 1655 |
+
|
| 1656 |
+
gr.Markdown("---")
|
| 1657 |
+
|
| 1658 |
+
# API Keys
|
| 1659 |
+
gr.Markdown("### AUTHENTICATION")
|
| 1660 |
+
|
| 1661 |
+
gr.HTML("""
|
| 1662 |
+
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
|
| 1663 |
+
<img src="https://cdn.brandfetch.io/idGqKHD5xE/theme/dark/symbol.svg?c=1bxid64Mup7aczewSAYMX&t=1668516030712"
|
| 1664 |
+
alt="HuggingFace"
|
| 1665 |
+
style="width: 20px; height: 20px;">
|
| 1666 |
+
<strong style="color: #d1d5db;">HuggingFace API Key</strong>
|
| 1667 |
+
</div>
|
| 1668 |
+
""")
|
| 1669 |
+
hf_key = gr.Textbox(
|
| 1670 |
+
placeholder="hf_...",
|
| 1671 |
+
type="password",
|
| 1672 |
+
show_label=False,
|
| 1673 |
+
container=False,
|
| 1674 |
+
elem_classes=["hf-section"]
|
| 1675 |
+
)
|
| 1676 |
+
gr.Markdown("Get your key from [HF Settings](https://huggingface.co/settings/tokens)")
|
| 1677 |
+
|
| 1678 |
+
gr.HTML("""
|
| 1679 |
+
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
|
| 1680 |
+
<img src="https://cdn.oaistatic.com/_next/static/media/apple-touch-icon.59f2e898.png"
|
| 1681 |
+
alt="OpenAI"
|
| 1682 |
+
style="width: 20px; height: 20px; border-radius: 4px;">
|
| 1683 |
+
<strong style="color: #d1d5db;">OpenAI API Key</strong>
|
| 1684 |
+
</div>
|
| 1685 |
+
""")
|
| 1686 |
+
openai_key = gr.Textbox(
|
| 1687 |
+
placeholder="sk-...",
|
| 1688 |
+
type="password",
|
| 1689 |
+
show_label=False,
|
| 1690 |
+
container=False,
|
| 1691 |
+
elem_classes=["openai-section"]
|
| 1692 |
+
)
|
| 1693 |
+
gr.Markdown("Get your key from [OpenAI Platform](https://platform.openai.com/api-keys)")
|
| 1694 |
+
|
| 1695 |
+
gr.HTML("""
|
| 1696 |
+
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
|
| 1697 |
+
<img src="https://cdn.brandfetch.io/idmJWF3N06/theme/dark/symbol.svg?c=1bxid64Mup7aczewSAYMX&t=1721803183716"
|
| 1698 |
+
alt="Anthropic"
|
| 1699 |
+
style="width: 20px; height: 20px; filter: invert(52%) sepia(48%) saturate(779%) hue-rotate(327deg) brightness(91%) contrast(88%);">
|
| 1700 |
+
<strong style="color: #d1d5db;">Anthropic API Key</strong>
|
| 1701 |
+
</div>
|
| 1702 |
+
""")
|
| 1703 |
+
anthropic_key = gr.Textbox(
|
| 1704 |
+
placeholder="sk-ant-...",
|
| 1705 |
+
type="password",
|
| 1706 |
+
show_label=False,
|
| 1707 |
+
container=False,
|
| 1708 |
+
elem_classes=["anthropic-section"]
|
| 1709 |
+
)
|
| 1710 |
+
gr.Markdown("Get your key from [Anthropic Console](https://console.anthropic.com/settings/keys)")
|
| 1711 |
+
|
| 1712 |
+
# Current Configuration Display
|
| 1713 |
+
gr.Markdown("---")
|
| 1714 |
+
gr.Markdown("### CURRENT CONFIG")
|
| 1715 |
+
|
| 1716 |
+
# Generate initial config HTML
|
| 1717 |
+
session_status = gr.HTML(
|
| 1718 |
+
value=create_config_html(
|
| 1719 |
+
provider_choice="OpenAI",
|
| 1720 |
+
agent_mode_choice="Supervisor (Multi-Agent)",
|
| 1721 |
+
hf_key_input="",
|
| 1722 |
+
openai_key_input="",
|
| 1723 |
+
anthropic_key_input=""
|
| 1724 |
+
)
|
| 1725 |
+
)
|
| 1726 |
+
|
| 1727 |
+
# About
|
| 1728 |
+
gr.Markdown("---")
|
| 1729 |
+
gr.Markdown("""
|
| 1730 |
+
### ABOUT
|
| 1731 |
+
|
| 1732 |
+
Built for the [Hugging Face MCP-1st-Birthday Hackathon](https://huggingface.co/MCP-1st-Birthday)
|
| 1733 |
+
""")
|
| 1734 |
+
|
| 1735 |
+
gr.HTML("""
|
| 1736 |
+
<div style="text-align: center; margin: 16px 0;">
|
| 1737 |
+
<img src="https://cdn-uploads.huggingface.co/production/uploads/60d2dc1007da9c17c72708f8/s4q7RzD3S-8xQ8ecXrSwb.png"
|
| 1738 |
+
alt="Hugging Face MCP 1st Birthday"
|
| 1739 |
+
style="max-width: 100%; height: auto; border-radius: 8px;">
|
| 1740 |
+
</div>
|
| 1741 |
+
""")
|
| 1742 |
+
|
| 1743 |
+
gr.Markdown("""
|
| 1744 |
+
**MCP Servers:**
|
| 1745 |
+
- Modal GPU classifier (2 tools)
|
| 1746 |
+
- Nuthatch species database (7 tools)
|
| 1747 |
+
|
| 1748 |
+
**Capabilities:**
|
| 1749 |
+
- Visual bird identification
|
| 1750 |
+
- Species reference images (Unsplash)
|
| 1751 |
+
- Audio recordings (xeno-canto)
|
| 1752 |
+
- Conservation status data
|
| 1753 |
+
- Taxonomic exploration
|
| 1754 |
+
- Separate tool log panel
|
| 1755 |
+
- Detailed execution tracking
|
| 1756 |
+
- Tool input/output inspection
|
| 1757 |
+
""")
|
| 1758 |
+
|
| 1759 |
+
with gr.Tab("📖 README"):
|
| 1760 |
+
with gr.Column(elem_classes=["readme-tab-container"]):
|
| 1761 |
+
try:
|
| 1762 |
+
with open("README.md", "r", encoding="utf-8") as f:
|
| 1763 |
+
readme_content = f.read()
|
| 1764 |
+
gr.Markdown(readme_content, elem_classes=["readme-markdown"])
|
| 1765 |
+
except FileNotFoundError:
|
| 1766 |
+
gr.Markdown("README.md not found", elem_classes=["readme-markdown"])
|
| 1767 |
|
| 1768 |
# State for tool log
|
| 1769 |
tool_log_state = gr.State("*Waiting for tool calls...*")
|