Improve research app progress and timeline layout
Browse files- fastmcp_research_app.py +28 -12
fastmcp_research_app.py
CHANGED
|
@@ -31,7 +31,7 @@ from starlette.middleware import Middleware
|
|
| 31 |
from prefab_ui.actions import SetInterval, SetState
|
| 32 |
from prefab_ui.actions.mcp import CallTool
|
| 33 |
from prefab_ui.app import PrefabApp
|
| 34 |
-
from prefab_ui.components import Badge, Card, CardContent, CardHeader, Column, Heading, If, Muted, Progress, Row, Text
|
| 35 |
from prefab_ui.components.control_flow import ForEach
|
| 36 |
from prefab_ui.rx import RESULT, Rx, STATE
|
| 37 |
|
|
@@ -112,6 +112,9 @@ class ResearchJob:
|
|
| 112 |
"topic": self.topic,
|
| 113 |
"status": self.status,
|
| 114 |
"events": list(self.events),
|
|
|
|
|
|
|
|
|
|
| 115 |
"result": self.result,
|
| 116 |
"error": self.error,
|
| 117 |
"done": self.status in {"completed", "failed"},
|
|
@@ -224,7 +227,7 @@ async def main() -> None:
|
|
| 224 |
|
| 225 |
@app.ui(
|
| 226 |
name="research",
|
| 227 |
-
title="Research Agent",
|
| 228 |
description="Open a live research app for a Hugging Face ecosystem research task.",
|
| 229 |
)
|
| 230 |
async def research(
|
|
@@ -274,31 +277,44 @@ async def main() -> None:
|
|
| 274 |
],
|
| 275 |
):
|
| 276 |
with Row(justify="between", align="center"):
|
| 277 |
-
Heading("Research Agent")
|
| 278 |
Badge(STATE.job.status, variant="outline")
|
| 279 |
Muted(STATE.topic)
|
| 280 |
|
| 281 |
with Card():
|
| 282 |
with CardHeader():
|
| 283 |
-
Text("
|
| 284 |
with CardContent():
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
Text(STATE.job.result, css_class="whitespace-pre-wrap")
|
| 291 |
-
Muted("Research is running; this view refreshes automatically.")
|
| 292 |
|
| 293 |
with Card():
|
| 294 |
with CardHeader():
|
| 295 |
Text("Timeline")
|
|
|
|
| 296 |
with CardContent():
|
| 297 |
-
with ForEach("job.
|
| 298 |
with Row(gap=2, align="start"):
|
| 299 |
Badge(event.kind, variant="outline")
|
| 300 |
Text(event.message)
|
| 301 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
return ui
|
| 303 |
|
| 304 |
@app.tool()
|
|
|
|
| 31 |
from prefab_ui.actions import SetInterval, SetState
|
| 32 |
from prefab_ui.actions.mcp import CallTool
|
| 33 |
from prefab_ui.app import PrefabApp
|
| 34 |
+
from prefab_ui.components import Badge, Card, CardContent, CardHeader, Column, Heading, If, Loader, Markdown, Muted, Progress, Row, Text
|
| 35 |
from prefab_ui.components.control_flow import ForEach
|
| 36 |
from prefab_ui.rx import RESULT, Rx, STATE
|
| 37 |
|
|
|
|
| 112 |
"topic": self.topic,
|
| 113 |
"status": self.status,
|
| 114 |
"events": list(self.events),
|
| 115 |
+
"timeline_events": list(self.events[-24:]),
|
| 116 |
+
"event_count": len(self.events),
|
| 117 |
+
"activity_progress": 100 if self.status in {"completed", "failed"} else int(((time() - self.created_at) * 12) % 100),
|
| 118 |
"result": self.result,
|
| 119 |
"error": self.error,
|
| 120 |
"done": self.status in {"completed", "failed"},
|
|
|
|
| 227 |
|
| 228 |
@app.ui(
|
| 229 |
name="research",
|
| 230 |
+
title="🤗 Research Agent",
|
| 231 |
description="Open a live research app for a Hugging Face ecosystem research task.",
|
| 232 |
)
|
| 233 |
async def research(
|
|
|
|
| 277 |
],
|
| 278 |
):
|
| 279 |
with Row(justify="between", align="center"):
|
| 280 |
+
Heading("🤗 Research Agent")
|
| 281 |
Badge(STATE.job.status, variant="outline")
|
| 282 |
Muted(STATE.topic)
|
| 283 |
|
| 284 |
with Card():
|
| 285 |
with CardHeader():
|
| 286 |
+
Text("Activity")
|
| 287 |
with CardContent():
|
| 288 |
+
with Row(gap=3, align="center"):
|
| 289 |
+
Loader(variant="bars", size="sm")
|
| 290 |
+
Badge(STATE.job.status, variant="outline")
|
| 291 |
+
Muted("Working; the number of steps is not known in advance.")
|
| 292 |
+
Progress(value=STATE.job.activity_progress, max=100, gradient=True, size="sm")
|
|
|
|
|
|
|
| 293 |
|
| 294 |
with Card():
|
| 295 |
with CardHeader():
|
| 296 |
Text("Timeline")
|
| 297 |
+
Muted("Showing the latest 24 events; older events roll off the visible list.")
|
| 298 |
with CardContent():
|
| 299 |
+
with ForEach("job.timeline_events") as event:
|
| 300 |
with Row(gap=2, align="start"):
|
| 301 |
Badge(event.kind, variant="outline")
|
| 302 |
Text(event.message)
|
| 303 |
|
| 304 |
+
with If(STATE.job.error):
|
| 305 |
+
with Card():
|
| 306 |
+
with CardHeader():
|
| 307 |
+
Text("Error")
|
| 308 |
+
with CardContent():
|
| 309 |
+
Text(STATE.job.error, css_class="text-red-600")
|
| 310 |
+
|
| 311 |
+
with If(STATE.job.result):
|
| 312 |
+
with Card():
|
| 313 |
+
with CardHeader():
|
| 314 |
+
Text("Final result")
|
| 315 |
+
with CardContent():
|
| 316 |
+
Markdown(STATE.job.result, css_class="whitespace-pre-wrap")
|
| 317 |
+
|
| 318 |
return ui
|
| 319 |
|
| 320 |
@app.tool()
|