Top banner with Quest + OSU NLP logos; hero heading; categorized examples
Browse files- app.py +221 -35
- assets/osu-nlp-logo.png +0 -0
app.py
CHANGED
|
@@ -88,7 +88,10 @@ TOOL_RESPONSE_TEMPLATE = """<tool_response>
|
|
| 88 |
|
| 89 |
SEARCH_CACHE: Dict[str, Dict[str, Any]] = {}
|
| 90 |
VISIT_CACHE: Dict[str, Dict[str, Any]] = {}
|
| 91 |
-
|
|
|
|
|
|
|
|
|
|
| 92 |
|
| 93 |
# Gradio 5 default themes pull greys from the neutral palette; Base + explicit overrides keep
|
| 94 |
# the whole UI on a strict blue/white scheme without unwanted slate panels.
|
|
@@ -269,12 +272,121 @@ gradio-app > div {
|
|
| 269 |
|
| 270 |
[class*="gradio-container"] *::selection { background: rgba(37,99,235,0.18); }
|
| 271 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
/* --- LEFT/RIGHT layout --- */
|
| 273 |
.layout-gap { gap: 24px !important; align-items: flex-start; }
|
| 274 |
.right-stack > * { margin-bottom: 12px; }
|
| 275 |
.action-row { gap: 10px !important; margin-top: 12px; }
|
| 276 |
.action-row button { min-width: 0; flex: 1; }
|
| 277 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
/* --- LOGO --- */
|
| 279 |
.banner-logo-image,
|
| 280 |
.banner-logo-image .image-container,
|
|
@@ -646,6 +758,33 @@ gradio-app > div {
|
|
| 646 |
.example-note { color: #4a6a8c; font-size: 12px; margin: 0 0 10px 0; }
|
| 647 |
.example-buttons { display: grid; gap: 10px; margin-top: 4px; }
|
| 648 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 649 |
/* --- gr.Examples component (currently unused but defensively styled) --- */
|
| 650 |
[class*="gradio-container"] [data-testid="block-examples"] {
|
| 651 |
background: #f0f6ff !important;
|
|
@@ -1057,19 +1196,83 @@ def run_ui(
|
|
| 1057 |
return f"Error: {exc}", json.dumps({"error": str(exc)}, ensure_ascii=False, indent=2)
|
| 1058 |
|
| 1059 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1060 |
with gr.Blocks(
|
| 1061 |
-
title="
|
| 1062 |
theme=APP_THEME,
|
| 1063 |
css=CUSTOM_CSS,
|
| 1064 |
fill_width=True,
|
| 1065 |
) as demo:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1066 |
with gr.Row(elem_classes="layout-gap"):
|
| 1067 |
with gr.Column(scale=6, min_width=420):
|
| 1068 |
with gr.Group(elem_classes="section-card"):
|
| 1069 |
-
gr.HTML('<div class="section-heading">
|
| 1070 |
question = gr.Textbox(
|
| 1071 |
show_label=False,
|
| 1072 |
-
placeholder="Ask anything you want to research...",
|
| 1073 |
lines=6,
|
| 1074 |
)
|
| 1075 |
with gr.Row(elem_classes="action-row"):
|
|
@@ -1079,10 +1282,14 @@ with gr.Blocks(
|
|
| 1079 |
|
| 1080 |
with gr.Group(elem_classes="section-card"):
|
| 1081 |
gr.HTML('<div class="section-heading">Try Examples</div>')
|
|
|
|
|
|
|
|
|
|
| 1082 |
with gr.Column(elem_classes="example-buttons"):
|
| 1083 |
-
|
| 1084 |
-
|
| 1085 |
-
|
|
|
|
| 1086 |
|
| 1087 |
with gr.Group(elem_classes="section-card"):
|
| 1088 |
with gr.Tabs():
|
|
@@ -1091,19 +1298,7 @@ with gr.Blocks(
|
|
| 1091 |
with gr.TabItem("Record"):
|
| 1092 |
trace = gr.Code(label="Execution Trace (JSON)", language="json")
|
| 1093 |
|
| 1094 |
-
with gr.Column(scale=4, min_width=
|
| 1095 |
-
with gr.Group(elem_classes=["section-card", "no-frame"]):
|
| 1096 |
-
gr.Image(
|
| 1097 |
-
value=LOGO_PATH,
|
| 1098 |
-
show_label=False,
|
| 1099 |
-
container=False,
|
| 1100 |
-
interactive=False,
|
| 1101 |
-
show_download_button=False,
|
| 1102 |
-
show_fullscreen_button=False,
|
| 1103 |
-
show_share_button=False,
|
| 1104 |
-
elem_classes="banner-logo-image",
|
| 1105 |
-
)
|
| 1106 |
-
|
| 1107 |
with gr.Group(elem_classes=["section-card", "no-frame"]):
|
| 1108 |
gr.HTML(
|
| 1109 |
f"""
|
|
@@ -1151,21 +1346,12 @@ with gr.Blocks(
|
|
| 1151 |
inputs=[question, model, max_turns, max_search_results, temperature],
|
| 1152 |
outputs=[answer, trace],
|
| 1153 |
)
|
| 1154 |
-
|
| 1155 |
-
|
| 1156 |
-
|
| 1157 |
-
|
| 1158 |
-
|
| 1159 |
-
|
| 1160 |
-
fn=lambda: "Compare 3 open-source coding agents with pros/cons.",
|
| 1161 |
-
inputs=[],
|
| 1162 |
-
outputs=[question],
|
| 1163 |
-
)
|
| 1164 |
-
ex3_btn.click(
|
| 1165 |
-
fn=lambda: "RAG vs fine-tuning for legal QA: which and why?",
|
| 1166 |
-
inputs=[],
|
| 1167 |
-
outputs=[question],
|
| 1168 |
-
)
|
| 1169 |
stop_btn.click(fn=None, cancels=[run_event])
|
| 1170 |
clear_btn.click(
|
| 1171 |
fn=lambda: ("", "", "{}"),
|
|
|
|
| 88 |
|
| 89 |
SEARCH_CACHE: Dict[str, Dict[str, Any]] = {}
|
| 90 |
VISIT_CACHE: Dict[str, Dict[str, Any]] = {}
|
| 91 |
+
ASSETS_DIR = Path(__file__).resolve().parent / "assets"
|
| 92 |
+
LOGO_PATH = str(ASSETS_DIR / "quest-logo.png")
|
| 93 |
+
OSU_NLP_LOGO_PATH = str(ASSETS_DIR / "osu-nlp-logo.png")
|
| 94 |
+
OSU_NLP_URL = "https://nlp.osu.edu/"
|
| 95 |
|
| 96 |
# Gradio 5 default themes pull greys from the neutral palette; Base + explicit overrides keep
|
| 97 |
# the whole UI on a strict blue/white scheme without unwanted slate panels.
|
|
|
|
| 272 |
|
| 273 |
[class*="gradio-container"] *::selection { background: rgba(37,99,235,0.18); }
|
| 274 |
|
| 275 |
+
/* --- TOP BANNER --- */
|
| 276 |
+
.top-banner {
|
| 277 |
+
align-items: center !important;
|
| 278 |
+
padding: 28px 0 12px 0;
|
| 279 |
+
margin-bottom: 8px;
|
| 280 |
+
gap: 0 !important;
|
| 281 |
+
}
|
| 282 |
+
.top-banner .banner-side { min-width: 0; }
|
| 283 |
+
.top-banner .banner-center {
|
| 284 |
+
display: flex !important;
|
| 285 |
+
flex-direction: column !important;
|
| 286 |
+
align-items: center !important;
|
| 287 |
+
gap: 8px !important;
|
| 288 |
+
}
|
| 289 |
+
.banner-quest-logo img {
|
| 290 |
+
width: auto !important;
|
| 291 |
+
max-width: 520px !important;
|
| 292 |
+
max-height: 160px !important;
|
| 293 |
+
height: auto !important;
|
| 294 |
+
object-fit: contain !important;
|
| 295 |
+
border-radius: 0 !important;
|
| 296 |
+
background: transparent !important;
|
| 297 |
+
border: none !important;
|
| 298 |
+
box-shadow: none !important;
|
| 299 |
+
margin: 0 auto !important;
|
| 300 |
+
display: block !important;
|
| 301 |
+
}
|
| 302 |
+
.banner-quest-logo,
|
| 303 |
+
.banner-quest-logo .image-container,
|
| 304 |
+
.banner-quest-logo .image-frame,
|
| 305 |
+
.banner-quest-logo > div,
|
| 306 |
+
.banner-quest-logo button {
|
| 307 |
+
background: transparent !important;
|
| 308 |
+
border: none !important;
|
| 309 |
+
box-shadow: none !important;
|
| 310 |
+
}
|
| 311 |
+
.banner-quest-logo .icon-button-wrapper,
|
| 312 |
+
.banner-quest-logo [aria-label*="hare" i],
|
| 313 |
+
.banner-quest-logo [aria-label*="ownload" i],
|
| 314 |
+
.banner-quest-logo [aria-label*="ullscreen" i] {
|
| 315 |
+
display: none !important;
|
| 316 |
+
}
|
| 317 |
+
.banner-subtitle {
|
| 318 |
+
color: #4a6a8c;
|
| 319 |
+
font-size: 15px;
|
| 320 |
+
font-weight: 500;
|
| 321 |
+
letter-spacing: 0.01em;
|
| 322 |
+
text-align: center;
|
| 323 |
+
margin: 0;
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
.banner-right { justify-content: flex-end; display: flex; }
|
| 327 |
+
.osu-nlp-mark {
|
| 328 |
+
display: flex !important;
|
| 329 |
+
flex-wrap: nowrap !important;
|
| 330 |
+
align-items: center !important;
|
| 331 |
+
justify-content: flex-end !important;
|
| 332 |
+
gap: 12px !important;
|
| 333 |
+
padding: 4px 4px 4px 12px;
|
| 334 |
+
width: auto !important;
|
| 335 |
+
background: transparent !important;
|
| 336 |
+
}
|
| 337 |
+
.osu-nlp-logo,
|
| 338 |
+
.osu-nlp-logo .image-container,
|
| 339 |
+
.osu-nlp-logo .image-frame,
|
| 340 |
+
.osu-nlp-logo > div,
|
| 341 |
+
.osu-nlp-logo button {
|
| 342 |
+
background: transparent !important;
|
| 343 |
+
border: none !important;
|
| 344 |
+
box-shadow: none !important;
|
| 345 |
+
width: auto !important;
|
| 346 |
+
max-width: 52px !important;
|
| 347 |
+
}
|
| 348 |
+
.osu-nlp-logo img {
|
| 349 |
+
width: 52px !important;
|
| 350 |
+
height: 52px !important;
|
| 351 |
+
object-fit: contain !important;
|
| 352 |
+
background: transparent !important;
|
| 353 |
+
border: none !important;
|
| 354 |
+
box-shadow: none !important;
|
| 355 |
+
border-radius: 0 !important;
|
| 356 |
+
}
|
| 357 |
+
.osu-nlp-logo .icon-button-wrapper,
|
| 358 |
+
.osu-nlp-logo [aria-label*="hare" i],
|
| 359 |
+
.osu-nlp-logo [aria-label*="ownload" i],
|
| 360 |
+
.osu-nlp-logo [aria-label*="ullscreen" i] {
|
| 361 |
+
display: none !important;
|
| 362 |
+
}
|
| 363 |
+
.osu-nlp-caption {
|
| 364 |
+
font-size: 12px !important;
|
| 365 |
+
line-height: 1.25 !important;
|
| 366 |
+
font-weight: 600 !important;
|
| 367 |
+
color: #1d4ed8 !important;
|
| 368 |
+
text-decoration: none !important;
|
| 369 |
+
white-space: nowrap !important;
|
| 370 |
+
}
|
| 371 |
+
.osu-nlp-caption:hover {
|
| 372 |
+
color: #1e3a8a !important;
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
/* --- LEFT/RIGHT layout --- */
|
| 376 |
.layout-gap { gap: 24px !important; align-items: flex-start; }
|
| 377 |
.right-stack > * { margin-bottom: 12px; }
|
| 378 |
.action-row { gap: 10px !important; margin-top: 12px; }
|
| 379 |
.action-row button { min-width: 0; flex: 1; }
|
| 380 |
|
| 381 |
+
.hero-heading {
|
| 382 |
+
font-size: 1.1rem !important;
|
| 383 |
+
font-weight: 700 !important;
|
| 384 |
+
letter-spacing: 0.005em !important;
|
| 385 |
+
text-transform: none !important;
|
| 386 |
+
color: #0f2744 !important;
|
| 387 |
+
margin-bottom: 14px !important;
|
| 388 |
+
}
|
| 389 |
+
|
| 390 |
/* --- LOGO --- */
|
| 391 |
.banner-logo-image,
|
| 392 |
.banner-logo-image .image-container,
|
|
|
|
| 758 |
.example-note { color: #4a6a8c; font-size: 12px; margin: 0 0 10px 0; }
|
| 759 |
.example-buttons { display: grid; gap: 10px; margin-top: 4px; }
|
| 760 |
|
| 761 |
+
/* Each Example button shows "<emoji> <Category> β <query text>". Left-align the
|
| 762 |
+
label so multi-line examples read like cards, and tint the category prefix in
|
| 763 |
+
blue via a CSS gradient (more robust across Gradio versions than inline HTML). */
|
| 764 |
+
[class*="gradio-container"] .example-btn {
|
| 765 |
+
text-align: left !important;
|
| 766 |
+
justify-content: flex-start !important;
|
| 767 |
+
white-space: normal !important;
|
| 768 |
+
line-height: 1.5 !important;
|
| 769 |
+
padding: 14px 16px !important;
|
| 770 |
+
font-size: 14px !important;
|
| 771 |
+
color: #0f2744 !important;
|
| 772 |
+
background: linear-gradient(180deg, #ffffff 0%, #f7fbff 100%) !important;
|
| 773 |
+
border: 1px solid #bfdbfe !important;
|
| 774 |
+
border-radius: 14px !important;
|
| 775 |
+
box-shadow: 0 1px 2px rgba(15,39,68,0.03) !important;
|
| 776 |
+
}
|
| 777 |
+
[class*="gradio-container"] .example-btn:hover {
|
| 778 |
+
background: #eff6ff !important;
|
| 779 |
+
border-color: #93c5fd !important;
|
| 780 |
+
box-shadow: 0 4px 14px rgba(37,99,235,0.12) !important;
|
| 781 |
+
}
|
| 782 |
+
[class*="gradio-container"] .example-btn > * {
|
| 783 |
+
color: inherit !important;
|
| 784 |
+
white-space: normal !important;
|
| 785 |
+
display: inline !important;
|
| 786 |
+
}
|
| 787 |
+
|
| 788 |
/* --- gr.Examples component (currently unused but defensively styled) --- */
|
| 789 |
[class*="gradio-container"] [data-testid="block-examples"] {
|
| 790 |
background: #f0f6ff !important;
|
|
|
|
| 1196 |
return f"Error: {exc}", json.dumps({"error": str(exc)}, ensure_ascii=False, indent=2)
|
| 1197 |
|
| 1198 |
|
| 1199 |
+
EXAMPLES = [
|
| 1200 |
+
{
|
| 1201 |
+
"category": "Fixed facts",
|
| 1202 |
+
"icon": "π―",
|
| 1203 |
+
"text": "Which university first awarded a PhD in computer science, and in what year?",
|
| 1204 |
+
},
|
| 1205 |
+
{
|
| 1206 |
+
"category": "Time-varying",
|
| 1207 |
+
"icon": "π",
|
| 1208 |
+
"text": "What is the current CEO of OpenAI, and when did they take the role?",
|
| 1209 |
+
},
|
| 1210 |
+
{
|
| 1211 |
+
"category": "Multi-constraints",
|
| 1212 |
+
"icon": "π§©",
|
| 1213 |
+
"text": "Find a 2-day Tokyo itinerary under $250 focused on museums and vegetarian food.",
|
| 1214 |
+
},
|
| 1215 |
+
{
|
| 1216 |
+
"category": "Long-form research report",
|
| 1217 |
+
"icon": "π",
|
| 1218 |
+
"text": "Compare three leading open-source coding agents with their strengths, weaknesses, and recent benchmark results.",
|
| 1219 |
+
},
|
| 1220 |
+
]
|
| 1221 |
+
|
| 1222 |
+
|
| 1223 |
+
def _example_label(ex: Dict[str, str]) -> str:
|
| 1224 |
+
return f"{ex['icon']} {ex['category']} β {ex['text']}"
|
| 1225 |
+
|
| 1226 |
+
|
| 1227 |
with gr.Blocks(
|
| 1228 |
+
title="Quest Β· Deep Research by OSU NLP",
|
| 1229 |
theme=APP_THEME,
|
| 1230 |
css=CUSTOM_CSS,
|
| 1231 |
fill_width=True,
|
| 1232 |
) as demo:
|
| 1233 |
+
# --- Top banner with the Quest logo centered and the OSU NLP mark on the right ---
|
| 1234 |
+
with gr.Row(elem_classes="top-banner"):
|
| 1235 |
+
with gr.Column(scale=1, elem_classes="banner-side"):
|
| 1236 |
+
pass
|
| 1237 |
+
with gr.Column(scale=4, elem_classes="banner-center"):
|
| 1238 |
+
gr.Image(
|
| 1239 |
+
value=LOGO_PATH,
|
| 1240 |
+
show_label=False,
|
| 1241 |
+
container=False,
|
| 1242 |
+
interactive=False,
|
| 1243 |
+
show_download_button=False,
|
| 1244 |
+
show_fullscreen_button=False,
|
| 1245 |
+
show_share_button=False,
|
| 1246 |
+
elem_classes="banner-quest-logo",
|
| 1247 |
+
)
|
| 1248 |
+
gr.HTML(
|
| 1249 |
+
'<div class="banner-subtitle">A deep research agent for multi-source investigation</div>'
|
| 1250 |
+
)
|
| 1251 |
+
with gr.Column(scale=1, elem_classes="banner-side banner-right"):
|
| 1252 |
+
with gr.Row(elem_classes="osu-nlp-mark"):
|
| 1253 |
+
gr.Image(
|
| 1254 |
+
value=OSU_NLP_LOGO_PATH,
|
| 1255 |
+
show_label=False,
|
| 1256 |
+
container=False,
|
| 1257 |
+
interactive=False,
|
| 1258 |
+
show_download_button=False,
|
| 1259 |
+
show_fullscreen_button=False,
|
| 1260 |
+
show_share_button=False,
|
| 1261 |
+
elem_classes="osu-nlp-logo",
|
| 1262 |
+
)
|
| 1263 |
+
gr.HTML(
|
| 1264 |
+
f'<a class="osu-nlp-caption" href="{OSU_NLP_URL}" target="_blank" rel="noopener noreferrer">'
|
| 1265 |
+
'The Ohio State University<br/>NLP Group</a>'
|
| 1266 |
+
)
|
| 1267 |
+
|
| 1268 |
+
# --- Main two-column layout ---
|
| 1269 |
with gr.Row(elem_classes="layout-gap"):
|
| 1270 |
with gr.Column(scale=6, min_width=420):
|
| 1271 |
with gr.Group(elem_classes="section-card"):
|
| 1272 |
+
gr.HTML('<div class="section-heading hero-heading">What can I search for you?</div>')
|
| 1273 |
question = gr.Textbox(
|
| 1274 |
show_label=False,
|
| 1275 |
+
placeholder="Ask anything you want to research in depth...",
|
| 1276 |
lines=6,
|
| 1277 |
)
|
| 1278 |
with gr.Row(elem_classes="action-row"):
|
|
|
|
| 1282 |
|
| 1283 |
with gr.Group(elem_classes="section-card"):
|
| 1284 |
gr.HTML('<div class="section-heading">Try Examples</div>')
|
| 1285 |
+
gr.HTML(
|
| 1286 |
+
'<div class="example-note">Each example shows the kind of query it represents. Click one to auto-fill.</div>'
|
| 1287 |
+
)
|
| 1288 |
with gr.Column(elem_classes="example-buttons"):
|
| 1289 |
+
example_buttons = [
|
| 1290 |
+
gr.Button(_example_label(ex), variant="secondary", elem_classes="example-btn")
|
| 1291 |
+
for ex in EXAMPLES
|
| 1292 |
+
]
|
| 1293 |
|
| 1294 |
with gr.Group(elem_classes="section-card"):
|
| 1295 |
with gr.Tabs():
|
|
|
|
| 1298 |
with gr.TabItem("Record"):
|
| 1299 |
trace = gr.Code(label="Execution Trace (JSON)", language="json")
|
| 1300 |
|
| 1301 |
+
with gr.Column(scale=4, min_width=340, elem_classes="right-stack"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1302 |
with gr.Group(elem_classes=["section-card", "no-frame"]):
|
| 1303 |
gr.HTML(
|
| 1304 |
f"""
|
|
|
|
| 1346 |
inputs=[question, model, max_turns, max_search_results, temperature],
|
| 1347 |
outputs=[answer, trace],
|
| 1348 |
)
|
| 1349 |
+
for btn, ex in zip(example_buttons, EXAMPLES):
|
| 1350 |
+
btn.click(
|
| 1351 |
+
fn=(lambda text=ex["text"]: text),
|
| 1352 |
+
inputs=[],
|
| 1353 |
+
outputs=[question],
|
| 1354 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1355 |
stop_btn.click(fn=None, cancels=[run_event])
|
| 1356 |
clear_btn.click(
|
| 1357 |
fn=lambda: ("", "", "{}"),
|
assets/osu-nlp-logo.png
ADDED
|