agno 2.3.8__py3-none-any.whl → 2.3.10__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 (66) hide show
  1. agno/agent/agent.py +134 -94
  2. agno/db/mysql/__init__.py +2 -1
  3. agno/db/mysql/async_mysql.py +2888 -0
  4. agno/db/mysql/mysql.py +17 -8
  5. agno/db/mysql/utils.py +139 -6
  6. agno/db/postgres/async_postgres.py +10 -5
  7. agno/db/postgres/postgres.py +7 -2
  8. agno/db/schemas/evals.py +1 -0
  9. agno/db/singlestore/singlestore.py +5 -1
  10. agno/db/sqlite/async_sqlite.py +3 -3
  11. agno/eval/__init__.py +10 -0
  12. agno/eval/accuracy.py +11 -8
  13. agno/eval/agent_as_judge.py +861 -0
  14. agno/eval/base.py +29 -0
  15. agno/eval/utils.py +2 -1
  16. agno/exceptions.py +7 -0
  17. agno/knowledge/embedder/openai.py +8 -8
  18. agno/knowledge/knowledge.py +1142 -176
  19. agno/media.py +22 -6
  20. agno/models/aws/claude.py +8 -7
  21. agno/models/base.py +61 -2
  22. agno/models/deepseek/deepseek.py +67 -0
  23. agno/models/google/gemini.py +134 -51
  24. agno/models/google/utils.py +22 -0
  25. agno/models/message.py +5 -0
  26. agno/models/openai/chat.py +4 -0
  27. agno/os/app.py +64 -74
  28. agno/os/interfaces/a2a/router.py +3 -4
  29. agno/os/interfaces/agui/router.py +2 -0
  30. agno/os/router.py +3 -1607
  31. agno/os/routers/agents/__init__.py +3 -0
  32. agno/os/routers/agents/router.py +581 -0
  33. agno/os/routers/agents/schema.py +261 -0
  34. agno/os/routers/evals/evals.py +26 -6
  35. agno/os/routers/evals/schemas.py +34 -2
  36. agno/os/routers/evals/utils.py +77 -18
  37. agno/os/routers/knowledge/knowledge.py +1 -1
  38. agno/os/routers/teams/__init__.py +3 -0
  39. agno/os/routers/teams/router.py +496 -0
  40. agno/os/routers/teams/schema.py +257 -0
  41. agno/os/routers/workflows/__init__.py +3 -0
  42. agno/os/routers/workflows/router.py +545 -0
  43. agno/os/routers/workflows/schema.py +75 -0
  44. agno/os/schema.py +1 -559
  45. agno/os/utils.py +139 -2
  46. agno/team/team.py +87 -24
  47. agno/tools/file_generation.py +12 -6
  48. agno/tools/firecrawl.py +15 -7
  49. agno/tools/function.py +37 -23
  50. agno/tools/shopify.py +1519 -0
  51. agno/tools/spotify.py +2 -5
  52. agno/utils/hooks.py +64 -5
  53. agno/utils/http.py +2 -2
  54. agno/utils/media.py +11 -1
  55. agno/utils/print_response/agent.py +8 -0
  56. agno/utils/print_response/team.py +8 -0
  57. agno/vectordb/pgvector/pgvector.py +88 -51
  58. agno/workflow/parallel.py +5 -3
  59. agno/workflow/step.py +14 -2
  60. agno/workflow/types.py +38 -2
  61. agno/workflow/workflow.py +12 -4
  62. {agno-2.3.8.dist-info → agno-2.3.10.dist-info}/METADATA +7 -2
  63. {agno-2.3.8.dist-info → agno-2.3.10.dist-info}/RECORD +66 -52
  64. {agno-2.3.8.dist-info → agno-2.3.10.dist-info}/WHEEL +0 -0
  65. {agno-2.3.8.dist-info → agno-2.3.10.dist-info}/licenses/LICENSE +0 -0
  66. {agno-2.3.8.dist-info → agno-2.3.10.dist-info}/top_level.txt +0 -0
agno/tools/spotify.py CHANGED
@@ -13,14 +13,11 @@ Required scopes:
13
13
  import json
14
14
  from typing import Any, List, Optional
15
15
 
16
+ import httpx
17
+
16
18
  from agno.tools import Toolkit
17
19
  from agno.utils.log import log_debug
18
20
 
19
- try:
20
- import httpx
21
- except ImportError:
22
- raise ImportError("`httpx` not installed. Please install using `pip install httpx`")
23
-
24
21
 
25
22
  class SpotifyTools(Toolkit):
26
23
  """
agno/utils/hooks.py CHANGED
@@ -1,5 +1,8 @@
1
1
  from copy import deepcopy
2
- from typing import Any, Callable, Dict, List, Optional, Union
2
+ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
3
+
4
+ if TYPE_CHECKING:
5
+ from agno.eval.base import BaseEval
3
6
 
4
7
  from agno.guardrails.base import BaseGuardrail
5
8
  from agno.hooks.decorator import HOOK_RUN_IN_BACKGROUND_ATTR
@@ -53,16 +56,17 @@ def should_run_hook_in_background(hook: Callable[..., Any]) -> bool:
53
56
  return getattr(hook, HOOK_RUN_IN_BACKGROUND_ATTR, False)
54
57
 
55
58
 
56
- def normalize_hooks(
57
- hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail]]],
59
+ def normalize_pre_hooks(
60
+ hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, "BaseEval"]]],
58
61
  async_mode: bool = False,
59
62
  ) -> Optional[List[Callable[..., Any]]]:
60
- """Normalize hooks to a list format
63
+ """Normalize pre-hooks to a list format.
61
64
 
62
65
  Args:
63
- hooks: List of hook functions or hook instances
66
+ hooks: List of hook functions, guardrails, or eval instances
64
67
  async_mode: Whether to use async versions of methods
65
68
  """
69
+ from agno.eval.base import BaseEval
66
70
 
67
71
  result_hooks: List[Callable[..., Any]] = []
68
72
 
@@ -73,6 +77,61 @@ def normalize_hooks(
73
77
  result_hooks.append(hook.async_check)
74
78
  else:
75
79
  result_hooks.append(hook.check)
80
+ elif isinstance(hook, BaseEval):
81
+ # Extract pre_check method
82
+ method = hook.async_pre_check if async_mode else hook.pre_check
83
+
84
+ from functools import partial
85
+
86
+ wrapped = partial(method)
87
+ wrapped.__name__ = method.__name__ # type: ignore
88
+ setattr(wrapped, HOOK_RUN_IN_BACKGROUND_ATTR, getattr(hook, "run_in_background", False))
89
+ result_hooks.append(wrapped)
90
+ else:
91
+ # Check if the hook is async and used within sync methods
92
+ if not async_mode:
93
+ import asyncio
94
+
95
+ if asyncio.iscoroutinefunction(hook):
96
+ raise ValueError(
97
+ f"Cannot use {hook.__name__} (an async hook) with `run()`. Use `arun()` instead."
98
+ )
99
+
100
+ result_hooks.append(hook)
101
+ return result_hooks if result_hooks else None
102
+
103
+
104
+ def normalize_post_hooks(
105
+ hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, "BaseEval"]]],
106
+ async_mode: bool = False,
107
+ ) -> Optional[List[Callable[..., Any]]]:
108
+ """Normalize post-hooks to a list format.
109
+
110
+ Args:
111
+ hooks: List of hook functions, guardrails, or eval instances
112
+ async_mode: Whether to use async versions of methods
113
+ """
114
+ from agno.eval.base import BaseEval
115
+
116
+ result_hooks: List[Callable[..., Any]] = []
117
+
118
+ if hooks is not None:
119
+ for hook in hooks:
120
+ if isinstance(hook, BaseGuardrail):
121
+ if async_mode:
122
+ result_hooks.append(hook.async_check)
123
+ else:
124
+ result_hooks.append(hook.check)
125
+ elif isinstance(hook, BaseEval):
126
+ # Extract post_check method
127
+ method = hook.async_post_check if async_mode else hook.post_check # type: ignore[assignment]
128
+
129
+ from functools import partial
130
+
131
+ wrapped = partial(method)
132
+ wrapped.__name__ = method.__name__ # type: ignore
133
+ setattr(wrapped, HOOK_RUN_IN_BACKGROUND_ATTR, getattr(hook, "run_in_background", False))
134
+ result_hooks.append(wrapped)
76
135
  else:
77
136
  # Check if the hook is async and used within sync methods
78
137
  if not async_mode:
agno/utils/http.py CHANGED
@@ -140,7 +140,7 @@ def fetch_with_retry(
140
140
  logger.error(f"Failed to fetch {url} after {max_retries} attempts: {e}")
141
141
  raise
142
142
  wait_time = backoff_factor**attempt
143
- logger.warning(f"Request failed (attempt {attempt + 1}), retrying in {wait_time} seconds...")
143
+ logger.warning("Connection error.")
144
144
  sleep(wait_time)
145
145
  except httpx.HTTPStatusError as e:
146
146
  logger.error(f"HTTP error for {url}: {e.response.status_code} - {e.response.text}")
@@ -176,7 +176,7 @@ async def async_fetch_with_retry(
176
176
  logger.error(f"Failed to fetch {url} after {max_retries} attempts: {e}")
177
177
  raise
178
178
  wait_time = backoff_factor**attempt
179
- logger.warning(f"Request failed (attempt {attempt + 1}), retrying in {wait_time} seconds...")
179
+ logger.warning("Connection error.")
180
180
  await asyncio.sleep(wait_time)
181
181
  except httpx.HTTPStatusError as e:
182
182
  logger.error(f"HTTP error for {url}: {e.response.status_code} - {e.response.text}")
agno/utils/media.py CHANGED
@@ -291,7 +291,7 @@ def reconstruct_file_from_dict(file_data):
291
291
  if isinstance(file_data, dict):
292
292
  # If content is base64 string, decode it back to bytes
293
293
  if "content" in file_data and isinstance(file_data["content"], str):
294
- return File.from_base64(
294
+ file_obj = File.from_base64(
295
295
  file_data["content"],
296
296
  id=file_data.get("id"),
297
297
  mime_type=file_data.get("mime_type"),
@@ -299,6 +299,16 @@ def reconstruct_file_from_dict(file_data):
299
299
  name=file_data.get("name"),
300
300
  format=file_data.get("format"),
301
301
  )
302
+ # Preserve additional fields that from_base64 doesn't handle
303
+ if file_data.get("size") is not None:
304
+ file_obj.size = file_data.get("size")
305
+ if file_data.get("file_type") is not None:
306
+ file_obj.file_type = file_data.get("file_type")
307
+ if file_data.get("filepath") is not None:
308
+ file_obj.filepath = file_data.get("filepath")
309
+ if file_data.get("url") is not None:
310
+ file_obj.url = file_data.get("url")
311
+ return file_obj
302
312
  else:
303
313
  # Regular file (filepath/url)
304
314
  return File(**file_data)
@@ -31,6 +31,7 @@ def print_response_stream(
31
31
  session_id: Optional[str] = None,
32
32
  session_state: Optional[Dict[str, Any]] = None,
33
33
  user_id: Optional[str] = None,
34
+ run_id: Optional[str] = None,
34
35
  audio: Optional[Sequence[Audio]] = None,
35
36
  images: Optional[Sequence[Image]] = None,
36
37
  videos: Optional[Sequence[Video]] = None,
@@ -86,6 +87,7 @@ def print_response_stream(
86
87
  session_id=session_id,
87
88
  session_state=session_state,
88
89
  user_id=user_id,
90
+ run_id=run_id,
89
91
  audio=audio,
90
92
  images=images,
91
93
  videos=videos,
@@ -222,6 +224,7 @@ async def aprint_response_stream(
222
224
  session_id: Optional[str] = None,
223
225
  session_state: Optional[Dict[str, Any]] = None,
224
226
  user_id: Optional[str] = None,
227
+ run_id: Optional[str] = None,
225
228
  audio: Optional[Sequence[Audio]] = None,
226
229
  images: Optional[Sequence[Image]] = None,
227
230
  videos: Optional[Sequence[Video]] = None,
@@ -275,6 +278,7 @@ async def aprint_response_stream(
275
278
  session_id=session_id,
276
279
  session_state=session_state,
277
280
  user_id=user_id,
281
+ run_id=run_id,
278
282
  audio=audio,
279
283
  images=images,
280
284
  videos=videos,
@@ -512,6 +516,7 @@ def print_response(
512
516
  session_id: Optional[str] = None,
513
517
  session_state: Optional[Dict[str, Any]] = None,
514
518
  user_id: Optional[str] = None,
519
+ run_id: Optional[str] = None,
515
520
  audio: Optional[Sequence[Audio]] = None,
516
521
  images: Optional[Sequence[Image]] = None,
517
522
  videos: Optional[Sequence[Video]] = None,
@@ -571,6 +576,7 @@ def print_response(
571
576
  session_id=session_id,
572
577
  session_state=session_state,
573
578
  user_id=user_id,
579
+ run_id=run_id,
574
580
  audio=audio,
575
581
  images=images,
576
582
  videos=videos,
@@ -645,6 +651,7 @@ async def aprint_response(
645
651
  session_id: Optional[str] = None,
646
652
  session_state: Optional[Dict[str, Any]] = None,
647
653
  user_id: Optional[str] = None,
654
+ run_id: Optional[str] = None,
648
655
  audio: Optional[Sequence[Audio]] = None,
649
656
  images: Optional[Sequence[Image]] = None,
650
657
  videos: Optional[Sequence[Video]] = None,
@@ -704,6 +711,7 @@ async def aprint_response(
704
711
  session_id=session_id,
705
712
  session_state=session_state,
706
713
  user_id=user_id,
714
+ run_id=run_id,
707
715
  audio=audio,
708
716
  images=images,
709
717
  videos=videos,
@@ -30,6 +30,7 @@ def print_response(
30
30
  session_id: Optional[str] = None,
31
31
  session_state: Optional[Dict[str, Any]] = None,
32
32
  user_id: Optional[str] = None,
33
+ run_id: Optional[str] = None,
33
34
  audio: Optional[Sequence[Audio]] = None,
34
35
  images: Optional[Sequence[Image]] = None,
35
36
  videos: Optional[Sequence[Video]] = None,
@@ -81,6 +82,7 @@ def print_response(
81
82
  # Run the agent
82
83
  run_response: TeamRunOutput = team.run( # type: ignore
83
84
  input=input,
85
+ run_id=run_id,
84
86
  images=images,
85
87
  audio=audio,
86
88
  videos=videos,
@@ -339,6 +341,7 @@ def print_response_stream(
339
341
  session_id: Optional[str] = None,
340
342
  session_state: Optional[Dict[str, Any]] = None,
341
343
  user_id: Optional[str] = None,
344
+ run_id: Optional[str] = None,
342
345
  audio: Optional[Sequence[Audio]] = None,
343
346
  images: Optional[Sequence[Image]] = None,
344
347
  videos: Optional[Sequence[Video]] = None,
@@ -417,6 +420,7 @@ def print_response_stream(
417
420
  session_id=session_id,
418
421
  session_state=session_state,
419
422
  user_id=user_id,
423
+ run_id=run_id,
420
424
  knowledge_filters=knowledge_filters,
421
425
  add_history_to_context=add_history_to_context,
422
426
  dependencies=dependencies,
@@ -893,6 +897,7 @@ async def aprint_response(
893
897
  session_id: Optional[str] = None,
894
898
  session_state: Optional[Dict[str, Any]] = None,
895
899
  user_id: Optional[str] = None,
900
+ run_id: Optional[str] = None,
896
901
  audio: Optional[Sequence[Audio]] = None,
897
902
  images: Optional[Sequence[Image]] = None,
898
903
  videos: Optional[Sequence[Video]] = None,
@@ -944,6 +949,7 @@ async def aprint_response(
944
949
  # Run the agent
945
950
  run_response: TeamRunOutput = await team.arun( # type: ignore
946
951
  input=input,
952
+ run_id=run_id,
947
953
  images=images,
948
954
  audio=audio,
949
955
  videos=videos,
@@ -1200,6 +1206,7 @@ async def aprint_response_stream(
1200
1206
  session_id: Optional[str] = None,
1201
1207
  session_state: Optional[Dict[str, Any]] = None,
1202
1208
  user_id: Optional[str] = None,
1209
+ run_id: Optional[str] = None,
1203
1210
  audio: Optional[Sequence[Audio]] = None,
1204
1211
  images: Optional[Sequence[Image]] = None,
1205
1212
  videos: Optional[Sequence[Video]] = None,
@@ -1288,6 +1295,7 @@ async def aprint_response_stream(
1288
1295
  session_id=session_id,
1289
1296
  session_state=session_state,
1290
1297
  user_id=user_id,
1298
+ run_id=run_id,
1291
1299
  knowledge_filters=knowledge_filters,
1292
1300
  add_history_to_context=add_history_to_context,
1293
1301
  add_dependencies_to_context=add_dependencies_to_context,