agno 2.0.0a1__py3-none-any.whl → 2.0.0rc2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. agno/agent/agent.py +416 -41
  2. agno/api/agent.py +2 -2
  3. agno/api/evals.py +2 -2
  4. agno/api/os.py +1 -1
  5. agno/api/settings.py +2 -2
  6. agno/api/team.py +2 -2
  7. agno/db/dynamo/dynamo.py +0 -6
  8. agno/db/firestore/firestore.py +0 -6
  9. agno/db/in_memory/in_memory_db.py +0 -6
  10. agno/db/json/json_db.py +0 -6
  11. agno/db/mongo/mongo.py +8 -9
  12. agno/db/mysql/utils.py +0 -1
  13. agno/db/postgres/postgres.py +0 -10
  14. agno/db/postgres/utils.py +0 -1
  15. agno/db/redis/redis.py +0 -4
  16. agno/db/singlestore/singlestore.py +0 -10
  17. agno/db/singlestore/utils.py +0 -1
  18. agno/db/sqlite/sqlite.py +0 -4
  19. agno/db/sqlite/utils.py +0 -1
  20. agno/eval/accuracy.py +12 -5
  21. agno/integrations/discord/client.py +5 -1
  22. agno/knowledge/chunking/strategy.py +14 -14
  23. agno/knowledge/embedder/aws_bedrock.py +2 -2
  24. agno/knowledge/knowledge.py +156 -120
  25. agno/knowledge/reader/arxiv_reader.py +5 -5
  26. agno/knowledge/reader/csv_reader.py +6 -77
  27. agno/knowledge/reader/docx_reader.py +5 -5
  28. agno/knowledge/reader/firecrawl_reader.py +5 -5
  29. agno/knowledge/reader/json_reader.py +5 -5
  30. agno/knowledge/reader/markdown_reader.py +31 -9
  31. agno/knowledge/reader/pdf_reader.py +10 -123
  32. agno/knowledge/reader/reader_factory.py +65 -72
  33. agno/knowledge/reader/s3_reader.py +44 -114
  34. agno/knowledge/reader/text_reader.py +5 -5
  35. agno/knowledge/reader/url_reader.py +75 -31
  36. agno/knowledge/reader/web_search_reader.py +6 -29
  37. agno/knowledge/reader/website_reader.py +5 -5
  38. agno/knowledge/reader/wikipedia_reader.py +5 -5
  39. agno/knowledge/reader/youtube_reader.py +6 -6
  40. agno/knowledge/utils.py +10 -10
  41. agno/models/anthropic/claude.py +2 -49
  42. agno/models/aws/bedrock.py +3 -7
  43. agno/models/base.py +37 -6
  44. agno/models/message.py +7 -6
  45. agno/os/app.py +168 -64
  46. agno/os/interfaces/agui/agui.py +1 -1
  47. agno/os/interfaces/agui/utils.py +16 -9
  48. agno/os/interfaces/slack/slack.py +2 -3
  49. agno/os/interfaces/whatsapp/whatsapp.py +2 -3
  50. agno/os/mcp.py +235 -0
  51. agno/os/router.py +576 -19
  52. agno/os/routers/evals/evals.py +201 -12
  53. agno/os/routers/knowledge/knowledge.py +455 -18
  54. agno/os/routers/memory/memory.py +260 -29
  55. agno/os/routers/metrics/metrics.py +127 -7
  56. agno/os/routers/session/session.py +398 -25
  57. agno/os/schema.py +55 -2
  58. agno/os/settings.py +0 -1
  59. agno/run/agent.py +96 -2
  60. agno/run/cancel.py +0 -2
  61. agno/run/team.py +93 -2
  62. agno/run/workflow.py +25 -12
  63. agno/team/team.py +863 -1053
  64. agno/tools/function.py +65 -7
  65. agno/tools/linear.py +1 -1
  66. agno/tools/mcp.py +1 -2
  67. agno/utils/gemini.py +31 -1
  68. agno/utils/log.py +52 -2
  69. agno/utils/mcp.py +55 -3
  70. agno/utils/models/claude.py +41 -0
  71. agno/utils/print_response/team.py +177 -73
  72. agno/utils/streamlit.py +481 -0
  73. agno/workflow/workflow.py +17 -1
  74. {agno-2.0.0a1.dist-info → agno-2.0.0rc2.dist-info}/METADATA +1 -1
  75. {agno-2.0.0a1.dist-info → agno-2.0.0rc2.dist-info}/RECORD +78 -77
  76. agno/knowledge/reader/gcs_reader.py +0 -67
  77. {agno-2.0.0a1.dist-info → agno-2.0.0rc2.dist-info}/WHEEL +0 -0
  78. {agno-2.0.0a1.dist-info → agno-2.0.0rc2.dist-info}/licenses/LICENSE +0 -0
  79. {agno-2.0.0a1.dist-info → agno-2.0.0rc2.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@ from agno.media import Audio, File, Image, Video
6
6
  from agno.models.message import Message
7
7
  from agno.models.response import ToolExecution
8
8
  from agno.reasoning.step import ReasoningStep
9
- from agno.run.agent import RunEvent, RunOutput, ToolCallCompletedEvent
9
+ from agno.run.agent import RunOutput
10
10
  from agno.run.team import TeamRunEvent, TeamRunOutput, TeamRunOutputEvent
11
11
  from agno.utils.log import log_warning
12
12
  from agno.utils.message import get_text_from_message
@@ -184,7 +184,7 @@ def print_response(
184
184
  elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
185
185
  show_markdown = member_markdown.get(member_response.team_id, False)
186
186
 
187
- member_response_content: Union[str, JSON, Markdown] = team._parse_response_content( # type: ignore
187
+ member_response_content: Union[str, JSON, Markdown] = _parse_response_content( # type: ignore
188
188
  member_response,
189
189
  tags_to_include_in_markdown,
190
190
  show_markdown=show_markdown,
@@ -247,7 +247,7 @@ def print_response(
247
247
  panels.append(team_tool_calls_panel)
248
248
  live_console.update(Group(*panels))
249
249
 
250
- response_content_batch: Union[str, JSON, Markdown] = team._parse_response_content( # type: ignore
250
+ response_content_batch: Union[str, JSON, Markdown] = _parse_response_content( # type: ignore
251
251
  run_response, tags_to_include_in_markdown, show_markdown=team_markdown
252
252
  )
253
253
 
@@ -388,6 +388,7 @@ def print_response_stream(
388
388
  dependencies=dependencies,
389
389
  metadata=metadata,
390
390
  debug_mode=debug_mode,
391
+ yield_run_response=True,
391
392
  **kwargs,
392
393
  )
393
394
 
@@ -397,9 +398,8 @@ def print_response_stream(
397
398
  # Dict to track member response panels by member_id
398
399
  member_response_panels = {}
399
400
 
400
- run_id = None
401
+ final_run_response = None
401
402
  for resp in stream_resp:
402
- run_id = resp.run_id
403
403
  if team_markdown is None:
404
404
  if markdown:
405
405
  team_markdown = True
@@ -409,6 +409,10 @@ def print_response_stream(
409
409
  if team.output_schema is not None:
410
410
  team_markdown = False
411
411
 
412
+ if isinstance(resp, TeamRunOutput):
413
+ final_run_response = resp
414
+ continue
415
+
412
416
  if isinstance(resp, tuple(get_args(TeamRunOutputEvent))):
413
417
  if resp.event == TeamRunEvent.run_content:
414
418
  if isinstance(resp.content, str):
@@ -424,8 +428,8 @@ def print_response_stream(
424
428
  reasoning_steps = resp.reasoning_steps # type: ignore
425
429
 
426
430
  # Collect team tool calls, avoiding duplicates
427
- if isinstance(resp, ToolCallCompletedEvent) and resp.tool:
428
- tool = resp.tool
431
+ if resp.event == TeamRunEvent.tool_call_completed and resp.tool: # type: ignore
432
+ tool = resp.tool # type: ignore
429
433
  # Generate a unique ID for this tool call
430
434
  if tool.tool_call_id:
431
435
  tool_id = tool.tool_call_id
@@ -537,7 +541,7 @@ def print_response_stream(
537
541
  if markdown:
538
542
  show_markdown = True
539
543
 
540
- member_response_content = team._parse_response_content(
544
+ member_response_content = _parse_response_content(
541
545
  member_response,
542
546
  tags_to_include_in_markdown,
543
547
  show_markdown=show_markdown,
@@ -597,7 +601,7 @@ def print_response_stream(
597
601
  live_console.update(Group(*panels))
598
602
 
599
603
  response_timer.stop()
600
- run_response = team.get_run_output(run_id=run_id) # type: ignore
604
+ run_response = final_run_response
601
605
 
602
606
  # Add citations
603
607
  if hasattr(resp, "citations") and resp.citations is not None and resp.citations.urls is not None:
@@ -725,7 +729,7 @@ def print_response_stream(
725
729
  elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
726
730
  show_markdown = member_markdown.get(member_response.team_id, False)
727
731
 
728
- member_response_content = team._parse_response_content( # type: ignore
732
+ member_response_content = _parse_response_content( # type: ignore
729
733
  member_response,
730
734
  tags_to_include_in_markdown,
731
735
  show_markdown=show_markdown,
@@ -985,7 +989,7 @@ async def aprint_response(
985
989
  elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
986
990
  show_markdown = member_markdown.get(member_response.team_id, False)
987
991
 
988
- member_response_content: Union[str, JSON, Markdown] = team._parse_response_content( # type: ignore
992
+ member_response_content: Union[str, JSON, Markdown] = _parse_response_content( # type: ignore
989
993
  member_response,
990
994
  tags_to_include_in_markdown,
991
995
  show_markdown=show_markdown,
@@ -1046,7 +1050,7 @@ async def aprint_response(
1046
1050
  panels.append(team_tool_calls_panel)
1047
1051
  live_console.update(Group(*panels))
1048
1052
 
1049
- response_content_batch: Union[str, JSON, Markdown] = team._parse_response_content( # type: ignore
1053
+ response_content_batch: Union[str, JSON, Markdown] = _parse_response_content( # type: ignore
1050
1054
  run_response, tags_to_include_in_markdown, show_markdown=team_markdown
1051
1055
  )
1052
1056
 
@@ -1175,7 +1179,10 @@ async def aprint_response_stream(
1175
1179
  team_markdown = None
1176
1180
  member_markdown = {}
1177
1181
 
1178
- run_id = None
1182
+ # Dict to track member response panels by member_id
1183
+ member_response_panels = {}
1184
+
1185
+ final_run_response = None
1179
1186
  async for resp in team.arun( # type: ignore
1180
1187
  input=input,
1181
1188
  audio=audio,
@@ -1192,9 +1199,9 @@ async def aprint_response_stream(
1192
1199
  dependencies=dependencies,
1193
1200
  metadata=metadata,
1194
1201
  debug_mode=debug_mode,
1202
+ yield_run_response=True,
1195
1203
  **kwargs,
1196
1204
  ):
1197
- run_id = resp.run_id
1198
1205
  if team_markdown is None:
1199
1206
  if markdown:
1200
1207
  team_markdown = True
@@ -1204,6 +1211,10 @@ async def aprint_response_stream(
1204
1211
  if team.output_schema is not None:
1205
1212
  team_markdown = False
1206
1213
 
1214
+ if isinstance(resp, TeamRunOutput):
1215
+ final_run_response = resp
1216
+ continue
1217
+
1207
1218
  if isinstance(resp, tuple(get_args(TeamRunOutputEvent))):
1208
1219
  if resp.event == TeamRunEvent.run_content:
1209
1220
  if isinstance(resp.content, str):
@@ -1219,8 +1230,8 @@ async def aprint_response_stream(
1219
1230
  reasoning_steps = resp.reasoning_steps # type: ignore
1220
1231
 
1221
1232
  # Collect team tool calls, avoiding duplicates
1222
- if isinstance(resp, ToolCallCompletedEvent) and resp.tool:
1223
- tool = resp.tool
1233
+ if resp.event == TeamRunEvent.tool_call_completed and resp.tool: # type: ignore
1234
+ tool = resp.tool # type: ignore
1224
1235
  # Generate a unique ID for this tool call
1225
1236
  if tool.tool_call_id is not None:
1226
1237
  tool_id = tool.tool_call_id
@@ -1259,7 +1270,7 @@ async def aprint_response_stream(
1259
1270
  response_content_stream = Markdown(escaped_content)
1260
1271
 
1261
1272
  # Create new panels for each chunk
1262
- panels = [status]
1273
+ panels = []
1263
1274
 
1264
1275
  if input and show_message:
1265
1276
  render = True
@@ -1271,8 +1282,6 @@ async def aprint_response_stream(
1271
1282
  border_style="cyan",
1272
1283
  )
1273
1284
  panels.append(message_panel)
1274
- if render:
1275
- live_console.update(Group(*panels))
1276
1285
 
1277
1286
  if len(reasoning_steps) > 0 and show_reasoning:
1278
1287
  render = True
@@ -1280,8 +1289,6 @@ async def aprint_response_stream(
1280
1289
  for i, step in enumerate(reasoning_steps, 1):
1281
1290
  reasoning_panel = build_reasoning_step_panel(i, step, show_full_reasoning)
1282
1291
  panels.append(reasoning_panel)
1283
- if render:
1284
- live_console.update(Group(*panels))
1285
1292
 
1286
1293
  if len(_response_reasoning_content) > 0:
1287
1294
  render = True
@@ -1292,29 +1299,95 @@ async def aprint_response_stream(
1292
1299
  border_style="green",
1293
1300
  )
1294
1301
  panels.append(thinking_panel)
1295
- if render:
1296
- live_console.update(Group(*panels))
1302
+ elif _response_content == "":
1303
+ # Keep showing status if no content yet
1304
+ panels.append(status)
1297
1305
 
1298
- # Add tool calls panel if available
1299
- if (
1300
- resp is not None
1301
- and hasattr(resp, "tool")
1302
- and resp.tool is not None
1303
- and resp.event == RunEvent.tool_call_started
1304
- ):
1305
- render = True
1306
- # Create bullet points for each tool call
1307
- tool_calls_content = Text()
1308
- formatted_tool_call = format_tool_calls([resp.tool])
1309
- tool_calls_content.append(f"• {formatted_tool_call}\n")
1310
-
1311
- tool_calls_panel = create_panel(
1312
- content=tool_calls_content.plain.rstrip(),
1313
- title="Tool Calls",
1314
- border_style="yellow",
1315
- )
1316
- panels.append(tool_calls_panel)
1306
+ # Process member responses and their tool calls
1307
+ for member_response in resp.member_responses if hasattr(resp, "member_responses") else []:
1308
+ member_id = None
1309
+ member_name = "Team Member"
1310
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
1311
+ member_id = member_response.agent_id
1312
+ member_name = team._get_member_name(member_id)
1313
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
1314
+ member_id = member_response.team_id
1315
+
1316
+ member_name = team._get_member_name(member_id)
1317
+
1318
+ # If we have tool calls for this member, display them
1319
+ if member_id in member_tool_calls and member_tool_calls[member_id]:
1320
+ formatted_calls = format_tool_calls(member_tool_calls[member_id])
1321
+ if formatted_calls:
1322
+ console_width = console.width if console else 80
1323
+ panel_width = console_width + 30
1324
+
1325
+ lines = []
1326
+ for call in formatted_calls:
1327
+ wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
1328
+ lines.append(wrapped_call)
1329
+
1330
+ tool_calls_text = "\n\n".join(lines)
1331
+
1332
+ member_tool_calls_panel = create_panel(
1333
+ content=tool_calls_text,
1334
+ title=f"{member_name} Tool Calls",
1335
+ border_style="yellow",
1336
+ )
1337
+ panels.append(member_tool_calls_panel)
1338
+
1339
+ # Process member response content
1340
+ if team.show_members_responses and member_id is not None:
1341
+ show_markdown = False
1342
+ if markdown:
1343
+ show_markdown = True
1344
+
1345
+ member_response_content = _parse_response_content(
1346
+ member_response,
1347
+ tags_to_include_in_markdown,
1348
+ show_markdown=show_markdown,
1349
+ )
1350
+
1351
+ member_response_panel = create_panel(
1352
+ content=member_response_content,
1353
+ title=f"{member_name} Response",
1354
+ border_style="magenta",
1355
+ )
1356
+
1357
+ panels.append(member_response_panel)
1358
+
1359
+ # Store for reference
1360
+ if member_id is not None:
1361
+ member_response_panels[member_id] = member_response_panel
1362
+
1363
+ # Add team tool calls panel if available (before the team response)
1364
+ if team_tool_calls:
1365
+ formatted_calls = format_tool_calls(team_tool_calls)
1366
+ if formatted_calls:
1367
+ console_width = console.width if console else 80
1368
+ panel_width = console_width + 30
1369
+
1370
+ lines = []
1371
+ # Create a set to track already added calls by their string representation
1372
+ added_calls = set()
1373
+ for call in formatted_calls:
1374
+ if call not in added_calls:
1375
+ added_calls.add(call)
1376
+ # Wrap the call text to fit within the panel
1377
+ wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
1378
+ lines.append(wrapped_call)
1379
+
1380
+ # Join with blank lines between items
1381
+ tool_calls_text = "\n\n".join(lines)
1382
+
1383
+ team_tool_calls_panel = create_panel(
1384
+ content=tool_calls_text,
1385
+ title="Team Tool Calls",
1386
+ border_style="yellow",
1387
+ )
1388
+ panels.append(team_tool_calls_panel)
1317
1389
 
1390
+ # Add the team response panel at the end
1318
1391
  if response_content_stream:
1319
1392
  render = True
1320
1393
  # Create panel for response
@@ -1324,11 +1397,13 @@ async def aprint_response_stream(
1324
1397
  border_style="blue",
1325
1398
  )
1326
1399
  panels.append(response_panel)
1327
- if render:
1400
+
1401
+ if render or len(panels) > 0:
1328
1402
  live_console.update(Group(*panels))
1403
+
1329
1404
  response_timer.stop()
1330
1405
 
1331
- run_response = team.get_run_output(run_id=run_id) # type: ignore
1406
+ run_response = final_run_response
1332
1407
 
1333
1408
  # Add citations
1334
1409
  if hasattr(resp, "citations") and resp.citations is not None and resp.citations.urls is not None:
@@ -1415,6 +1490,7 @@ async def aprint_response_stream(
1415
1490
  elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
1416
1491
  member_id = member_response.team_id
1417
1492
 
1493
+ # Print tool calls
1418
1494
  if member_id:
1419
1495
  # First add tool calls if any
1420
1496
  if member_id in member_tool_calls and member_tool_calls[member_id]:
@@ -1472,39 +1548,39 @@ async def aprint_response_stream(
1472
1548
  elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
1473
1549
  show_markdown = member_markdown.get(member_response.team_id, False)
1474
1550
 
1475
- member_response_content = team._parse_response_content( # type: ignore
1476
- member_response,
1477
- tags_to_include_in_markdown,
1478
- show_markdown=show_markdown,
1479
- )
1551
+ member_response_content = _parse_response_content( # type: ignore
1552
+ member_response,
1553
+ tags_to_include_in_markdown,
1554
+ show_markdown=show_markdown,
1555
+ )
1480
1556
 
1481
- member_name = "Team Member"
1482
- if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
1483
- member_name = team._get_member_name(member_response.agent_id)
1484
- elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
1485
- member_name = team._get_member_name(member_response.team_id)
1557
+ member_name = "Team Member"
1558
+ if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
1559
+ member_name = team._get_member_name(member_response.agent_id)
1560
+ elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
1561
+ member_name = team._get_member_name(member_response.team_id)
1486
1562
 
1487
- member_response_panel = create_panel(
1488
- content=member_response_content,
1489
- title=f"{member_name} Response",
1490
- border_style="magenta",
1491
- )
1492
- final_panels.append(member_response_panel)
1563
+ member_response_panel = create_panel(
1564
+ content=member_response_content,
1565
+ title=f"{member_name} Response",
1566
+ border_style="magenta",
1567
+ )
1568
+ final_panels.append(member_response_panel)
1493
1569
 
1494
- # Add citations if any
1495
- if member_response.citations is not None and member_response.citations.urls is not None:
1496
- md_content = "\n".join(
1497
- f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
1498
- for i, citation in enumerate(member_response.citations.urls)
1499
- if citation.url # Only include citations with valid URLs
1570
+ # Add citations if any
1571
+ if member_response.citations is not None and member_response.citations.urls is not None:
1572
+ md_content = "\n".join(
1573
+ f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
1574
+ for i, citation in enumerate(member_response.citations.urls)
1575
+ if citation.url # Only include citations with valid URLs
1576
+ )
1577
+ if md_content: # Only create panel if there are citations
1578
+ citations_panel = create_panel(
1579
+ content=Markdown(md_content),
1580
+ title="Citations",
1581
+ border_style="magenta",
1500
1582
  )
1501
- if md_content: # Only create panel if there are citations
1502
- citations_panel = create_panel(
1503
- content=Markdown(md_content),
1504
- title="Citations",
1505
- border_style="magenta",
1506
- )
1507
- final_panels.append(citations_panel)
1583
+ final_panels.append(citations_panel)
1508
1584
 
1509
1585
  # Add team tool calls before team response
1510
1586
  if team_tool_calls:
@@ -1563,3 +1639,31 @@ async def aprint_response_stream(
1563
1639
 
1564
1640
  # Final update with correctly ordered panels
1565
1641
  live_console.update(Group(*final_panels))
1642
+
1643
+
1644
+ def _parse_response_content(
1645
+ run_response: Union[TeamRunOutput, RunOutput],
1646
+ tags_to_include_in_markdown: Set[str],
1647
+ show_markdown: bool = True,
1648
+ ) -> Any:
1649
+ from rich.json import JSON
1650
+ from rich.markdown import Markdown
1651
+
1652
+ if isinstance(run_response.content, str):
1653
+ if show_markdown:
1654
+ escaped_content = escape_markdown_tags(run_response.content, tags_to_include_in_markdown)
1655
+ return Markdown(escaped_content)
1656
+ else:
1657
+ return run_response.get_content_as_string(indent=4)
1658
+ elif isinstance(run_response.content, BaseModel):
1659
+ try:
1660
+ return JSON(run_response.content.model_dump_json(exclude_none=True), indent=2)
1661
+ except Exception as e:
1662
+ log_warning(f"Failed to convert response to JSON: {e}")
1663
+ else:
1664
+ import json
1665
+
1666
+ try:
1667
+ return JSON(json.dumps(run_response.content), indent=4)
1668
+ except Exception as e:
1669
+ log_warning(f"Failed to convert response to JSON: {e}")