|
7 | 7 |
|
8 | 8 | from client import AgentClient
|
9 | 9 | from schema import ChatHistory, ChatMessage
|
| 10 | +from schema.task_data import TaskData |
10 | 11 |
|
11 | 12 | # A Streamlit app for interacting with the langgraph agent via a simple chat interface.
|
12 | 13 | # The app has three main functions which are all run async:
|
@@ -82,6 +83,7 @@ async def main() -> None:
|
82 | 83 | options=[
|
83 | 84 | "research-assistant",
|
84 | 85 | "chatbot",
|
| 86 | + "bg-task-agent", |
85 | 87 | ],
|
86 | 88 | )
|
87 | 89 | use_streaming = st.toggle("Stream results", value=True)
|
@@ -266,6 +268,61 @@ async def draw_messages(
|
266 | 268 | status.write(tool_result.content)
|
267 | 269 | status.update(state="complete")
|
268 | 270 |
|
| 271 | + case "custom": |
| 272 | + # This is an implementation of the TaskData example for CustomData. |
| 273 | + # An agent can write a CustomData object to the message stream, and |
| 274 | + # it's passed to the client for rendering. To see this in practice, |
| 275 | + # run the app with the `bg-task-agent` agent. |
| 276 | + |
| 277 | + # This is provided as an example, you may want to write your own |
| 278 | + # CustomData types and handlers. This section will be skipped for |
| 279 | + # any other agents that don't send CustomData. |
| 280 | + task_data = TaskData.model_validate(msg.custom_data) |
| 281 | + |
| 282 | + # If we're rendering new messages, store the message in session state |
| 283 | + if is_new: |
| 284 | + st.session_state.messages.append(msg) |
| 285 | + |
| 286 | + # If the last message type was not Task, create a new chat message |
| 287 | + # and container for task messages |
| 288 | + if last_message_type != "task": |
| 289 | + last_message_type = "task" |
| 290 | + st.session_state.last_message = st.chat_message( |
| 291 | + name="task", avatar=":material/manufacturing:" |
| 292 | + ) |
| 293 | + with st.session_state.last_message: |
| 294 | + status = st.status("") |
| 295 | + current_task_data: dict[str, TaskData] = {} |
| 296 | + |
| 297 | + status_str = f"Task **{task_data.name}** " |
| 298 | + match task_data.state: |
| 299 | + case "new": |
| 300 | + status_str += "has :blue[started]. Input:" |
| 301 | + case "running": |
| 302 | + status_str += "wrote:" |
| 303 | + case "complete": |
| 304 | + if task_data.result == "success": |
| 305 | + status_str += ":green[completed successfully]. Output:" |
| 306 | + else: |
| 307 | + status_str += ":red[ended with error]. Output:" |
| 308 | + status.write(status_str) |
| 309 | + status.write(task_data.data) |
| 310 | + status.write("---") |
| 311 | + if task_data.run_id not in current_task_data: |
| 312 | + # Status label always shows the last newly started task |
| 313 | + status.update(label=f"""Task: {task_data.name}""") |
| 314 | + current_task_data[task_data.run_id] = task_data |
| 315 | + # Status is "running" until all tasks have completed |
| 316 | + if not any(entry.completed() for entry in current_task_data.values()): |
| 317 | + state = "running" |
| 318 | + # Status is "error" if any task has errored |
| 319 | + elif any(entry.completed_with_error() for entry in current_task_data.values()): |
| 320 | + state = "error" |
| 321 | + # Status is "complete" if all tasks have completed successfully |
| 322 | + else: |
| 323 | + state = "complete" |
| 324 | + status.update(state=state) |
| 325 | + |
269 | 326 | # In case of an unexpected message type, log an error and stop
|
270 | 327 | case _:
|
271 | 328 | st.error(f"Unexpected ChatMessage type: {msg.type}")
|
|
0 commit comments