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.
- agno/agent/agent.py +134 -94
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2888 -0
- agno/db/mysql/mysql.py +17 -8
- agno/db/mysql/utils.py +139 -6
- agno/db/postgres/async_postgres.py +10 -5
- agno/db/postgres/postgres.py +7 -2
- agno/db/schemas/evals.py +1 -0
- agno/db/singlestore/singlestore.py +5 -1
- agno/db/sqlite/async_sqlite.py +3 -3
- agno/eval/__init__.py +10 -0
- agno/eval/accuracy.py +11 -8
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/utils.py +2 -1
- agno/exceptions.py +7 -0
- agno/knowledge/embedder/openai.py +8 -8
- agno/knowledge/knowledge.py +1142 -176
- agno/media.py +22 -6
- agno/models/aws/claude.py +8 -7
- agno/models/base.py +61 -2
- agno/models/deepseek/deepseek.py +67 -0
- agno/models/google/gemini.py +134 -51
- agno/models/google/utils.py +22 -0
- agno/models/message.py +5 -0
- agno/models/openai/chat.py +4 -0
- agno/os/app.py +64 -74
- agno/os/interfaces/a2a/router.py +3 -4
- agno/os/interfaces/agui/router.py +2 -0
- agno/os/router.py +3 -1607
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +581 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/evals.py +26 -6
- agno/os/routers/evals/schemas.py +34 -2
- agno/os/routers/evals/utils.py +77 -18
- agno/os/routers/knowledge/knowledge.py +1 -1
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +496 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +545 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +1 -559
- agno/os/utils.py +139 -2
- agno/team/team.py +87 -24
- agno/tools/file_generation.py +12 -6
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +37 -23
- agno/tools/shopify.py +1519 -0
- agno/tools/spotify.py +2 -5
- agno/utils/hooks.py +64 -5
- agno/utils/http.py +2 -2
- agno/utils/media.py +11 -1
- agno/utils/print_response/agent.py +8 -0
- agno/utils/print_response/team.py +8 -0
- agno/vectordb/pgvector/pgvector.py +88 -51
- agno/workflow/parallel.py +5 -3
- agno/workflow/step.py +14 -2
- agno/workflow/types.py +38 -2
- agno/workflow/workflow.py +12 -4
- {agno-2.3.8.dist-info → agno-2.3.10.dist-info}/METADATA +7 -2
- {agno-2.3.8.dist-info → agno-2.3.10.dist-info}/RECORD +66 -52
- {agno-2.3.8.dist-info → agno-2.3.10.dist-info}/WHEEL +0 -0
- {agno-2.3.8.dist-info → agno-2.3.10.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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,
|