agno 2.3.15__py3-none-any.whl → 2.3.17__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/__init__.py +2 -0
- agno/agent/agent.py +4 -53
- agno/agent/remote.py +351 -0
- agno/client/__init__.py +3 -0
- agno/client/os.py +2669 -0
- agno/db/base.py +20 -0
- agno/db/mongo/async_mongo.py +11 -0
- agno/db/mongo/mongo.py +10 -0
- agno/db/mysql/async_mysql.py +9 -0
- agno/db/mysql/mysql.py +9 -0
- agno/db/postgres/async_postgres.py +9 -0
- agno/db/postgres/postgres.py +9 -0
- agno/db/postgres/utils.py +3 -2
- agno/db/sqlite/async_sqlite.py +9 -0
- agno/db/sqlite/sqlite.py +11 -1
- agno/exceptions.py +23 -0
- agno/knowledge/chunking/semantic.py +123 -46
- agno/knowledge/reader/csv_reader.py +9 -14
- agno/knowledge/reader/docx_reader.py +2 -4
- agno/knowledge/reader/field_labeled_csv_reader.py +11 -17
- agno/knowledge/reader/json_reader.py +9 -17
- agno/knowledge/reader/markdown_reader.py +6 -6
- agno/knowledge/reader/pdf_reader.py +14 -11
- agno/knowledge/reader/pptx_reader.py +2 -4
- agno/knowledge/reader/s3_reader.py +2 -11
- agno/knowledge/reader/text_reader.py +6 -18
- agno/knowledge/reader/web_search_reader.py +4 -15
- agno/os/app.py +104 -23
- agno/os/auth.py +25 -1
- agno/os/interfaces/a2a/a2a.py +7 -6
- agno/os/interfaces/a2a/router.py +13 -13
- agno/os/interfaces/agui/agui.py +5 -3
- agno/os/interfaces/agui/router.py +23 -16
- agno/os/interfaces/base.py +7 -7
- agno/os/interfaces/slack/router.py +6 -6
- agno/os/interfaces/slack/slack.py +7 -7
- agno/os/interfaces/whatsapp/router.py +29 -6
- agno/os/interfaces/whatsapp/whatsapp.py +11 -8
- agno/os/managers.py +326 -0
- agno/os/mcp.py +651 -79
- agno/os/router.py +125 -18
- agno/os/routers/agents/router.py +65 -22
- agno/os/routers/agents/schema.py +16 -4
- agno/os/routers/database.py +5 -0
- agno/os/routers/evals/evals.py +93 -11
- agno/os/routers/evals/utils.py +6 -6
- agno/os/routers/knowledge/knowledge.py +104 -16
- agno/os/routers/memory/memory.py +124 -7
- agno/os/routers/metrics/metrics.py +21 -4
- agno/os/routers/session/session.py +141 -12
- agno/os/routers/teams/router.py +40 -14
- agno/os/routers/teams/schema.py +12 -4
- agno/os/routers/traces/traces.py +54 -4
- agno/os/routers/workflows/router.py +223 -117
- agno/os/routers/workflows/schema.py +65 -1
- agno/os/schema.py +38 -12
- agno/os/utils.py +87 -166
- agno/remote/__init__.py +3 -0
- agno/remote/base.py +484 -0
- agno/run/workflow.py +1 -0
- agno/team/__init__.py +2 -0
- agno/team/remote.py +287 -0
- agno/team/team.py +25 -54
- agno/tracing/exporter.py +10 -6
- agno/tracing/setup.py +2 -1
- agno/utils/agent.py +58 -1
- agno/utils/http.py +68 -20
- agno/utils/os.py +0 -0
- agno/utils/remote.py +23 -0
- agno/vectordb/chroma/chromadb.py +452 -16
- agno/vectordb/pgvector/pgvector.py +7 -0
- agno/vectordb/redis/redisdb.py +1 -1
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +2 -2
- agno/workflow/remote.py +222 -0
- agno/workflow/types.py +0 -73
- agno/workflow/workflow.py +119 -68
- {agno-2.3.15.dist-info → agno-2.3.17.dist-info}/METADATA +1 -1
- {agno-2.3.15.dist-info → agno-2.3.17.dist-info}/RECORD +82 -72
- {agno-2.3.15.dist-info → agno-2.3.17.dist-info}/WHEEL +0 -0
- {agno-2.3.15.dist-info → agno-2.3.17.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.15.dist-info → agno-2.3.17.dist-info}/top_level.txt +0 -0
agno/utils/http.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
|
+
import threading
|
|
3
4
|
from time import sleep
|
|
4
5
|
from typing import Optional
|
|
5
6
|
|
|
@@ -17,72 +18,116 @@ DEFAULT_BACKOFF_FACTOR = 2 # Exponential backoff: 1, 2, 4, 8...
|
|
|
17
18
|
_global_sync_client: Optional[httpx.Client] = None
|
|
18
19
|
_global_async_client: Optional[httpx.AsyncClient] = None
|
|
19
20
|
|
|
21
|
+
# Locks for thread-safe lazy initialization
|
|
22
|
+
_sync_client_lock = threading.Lock()
|
|
23
|
+
_async_client_lock = threading.Lock()
|
|
24
|
+
|
|
20
25
|
|
|
21
26
|
def get_default_sync_client() -> httpx.Client:
|
|
22
27
|
"""Get or create the global synchronous httpx client.
|
|
23
28
|
|
|
29
|
+
Thread-safe lazy initialization using double-checked locking.
|
|
30
|
+
|
|
31
|
+
Note: HTTP/2 is disabled for the sync client because HTTP/2's stream
|
|
32
|
+
multiplexing is not thread-safe when sharing a client across threads
|
|
33
|
+
(e.g., when using ThreadPoolExecutor). HTTP/1.1 uses connection pooling
|
|
34
|
+
where each connection handles one request at a time, which is thread-safe.
|
|
35
|
+
|
|
24
36
|
Returns:
|
|
25
37
|
A singleton httpx.Client instance with default limits.
|
|
26
38
|
"""
|
|
27
39
|
global _global_sync_client
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
|
|
41
|
+
if _global_sync_client is not None and not _global_sync_client.is_closed:
|
|
42
|
+
return _global_sync_client
|
|
43
|
+
|
|
44
|
+
with _sync_client_lock:
|
|
45
|
+
if _global_sync_client is None or _global_sync_client.is_closed:
|
|
46
|
+
_global_sync_client = httpx.Client(
|
|
47
|
+
limits=httpx.Limits(max_connections=1000, max_keepalive_connections=200),
|
|
48
|
+
http2=False, # Disabled for thread safety in multi-threaded contexts
|
|
49
|
+
follow_redirects=True,
|
|
50
|
+
)
|
|
32
51
|
return _global_sync_client
|
|
33
52
|
|
|
34
53
|
|
|
35
54
|
def get_default_async_client() -> httpx.AsyncClient:
|
|
36
55
|
"""Get or create the global asynchronous httpx client.
|
|
37
56
|
|
|
57
|
+
Thread-safe lazy initialization using double-checked locking.
|
|
58
|
+
|
|
59
|
+
Note: HTTP/2 is enabled for the async client because asyncio runs in a
|
|
60
|
+
single-threaded event loop where HTTP/2 stream multiplexing is safe.
|
|
61
|
+
|
|
38
62
|
Returns:
|
|
39
63
|
A singleton httpx.AsyncClient instance with default limits.
|
|
40
64
|
"""
|
|
41
65
|
global _global_async_client
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
66
|
+
|
|
67
|
+
if _global_async_client is not None and not _global_async_client.is_closed:
|
|
68
|
+
return _global_async_client
|
|
69
|
+
|
|
70
|
+
with _async_client_lock:
|
|
71
|
+
if _global_async_client is None or _global_async_client.is_closed:
|
|
72
|
+
_global_async_client = httpx.AsyncClient(
|
|
73
|
+
limits=httpx.Limits(max_connections=1000, max_keepalive_connections=200),
|
|
74
|
+
http2=True, # Safe in async context (single-threaded event loop)
|
|
75
|
+
follow_redirects=True,
|
|
76
|
+
)
|
|
46
77
|
return _global_async_client
|
|
47
78
|
|
|
48
79
|
|
|
49
80
|
def close_sync_client() -> None:
|
|
50
81
|
"""Closes the global sync httpx client.
|
|
51
82
|
|
|
52
|
-
Should be called during application shutdown.
|
|
83
|
+
Thread-safe. Should be called during application shutdown.
|
|
53
84
|
"""
|
|
54
85
|
global _global_sync_client
|
|
55
|
-
|
|
56
|
-
_global_sync_client.
|
|
86
|
+
with _sync_client_lock:
|
|
87
|
+
if _global_sync_client is not None and not _global_sync_client.is_closed:
|
|
88
|
+
_global_sync_client.close()
|
|
89
|
+
_global_sync_client = None
|
|
57
90
|
|
|
58
91
|
|
|
59
92
|
async def aclose_default_clients() -> None:
|
|
60
93
|
"""Asynchronously close the global httpx clients.
|
|
61
94
|
|
|
62
|
-
Should be called during application shutdown in async contexts.
|
|
95
|
+
Thread-safe. Should be called during application shutdown in async contexts.
|
|
63
96
|
"""
|
|
64
97
|
global _global_sync_client, _global_async_client
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
98
|
+
|
|
99
|
+
with _sync_client_lock:
|
|
100
|
+
if _global_sync_client is not None and not _global_sync_client.is_closed:
|
|
101
|
+
_global_sync_client.close()
|
|
102
|
+
_global_sync_client = None
|
|
103
|
+
|
|
104
|
+
with _async_client_lock:
|
|
105
|
+
if _global_async_client is not None and not _global_async_client.is_closed:
|
|
106
|
+
await _global_async_client.aclose()
|
|
107
|
+
_global_async_client = None
|
|
69
108
|
|
|
70
109
|
|
|
71
110
|
def set_default_sync_client(client: httpx.Client) -> None:
|
|
72
111
|
"""Set the global synchronous httpx client.
|
|
73
112
|
|
|
74
|
-
|
|
113
|
+
Thread-safe. Call before creating any model instances for best results,
|
|
114
|
+
though this can be called at any time.
|
|
75
115
|
|
|
76
116
|
Allows consumers to override the default httpx client with custom configuration
|
|
77
117
|
(e.g., custom limits, timeouts, proxies, SSL verification, etc.).
|
|
78
118
|
This is useful at application startup to customize how all models connect.
|
|
79
119
|
|
|
120
|
+
Warning: If using this client in multi-threaded contexts (e.g., ThreadPoolExecutor),
|
|
121
|
+
consider disabling HTTP/2 (http2=False) to avoid thread-safety issues with
|
|
122
|
+
HTTP/2 stream multiplexing.
|
|
123
|
+
|
|
80
124
|
Example:
|
|
81
125
|
>>> import httpx
|
|
82
126
|
>>> from agno.utils.http import set_default_sync_client
|
|
83
127
|
>>> custom_client = httpx.Client(
|
|
84
128
|
... limits=httpx.Limits(max_connections=500),
|
|
85
129
|
... timeout=httpx.Timeout(30.0),
|
|
130
|
+
... http2=False, # Recommended for multi-threaded use
|
|
86
131
|
... verify=False # for dev environments
|
|
87
132
|
... )
|
|
88
133
|
>>> set_default_sync_client(custom_client)
|
|
@@ -92,13 +137,15 @@ def set_default_sync_client(client: httpx.Client) -> None:
|
|
|
92
137
|
client: An httpx.Client instance to use as the global sync client.
|
|
93
138
|
"""
|
|
94
139
|
global _global_sync_client
|
|
95
|
-
|
|
140
|
+
with _sync_client_lock:
|
|
141
|
+
_global_sync_client = client
|
|
96
142
|
|
|
97
143
|
|
|
98
144
|
def set_default_async_client(client: httpx.AsyncClient) -> None:
|
|
99
145
|
"""Set the global asynchronous httpx client.
|
|
100
146
|
|
|
101
|
-
|
|
147
|
+
Thread-safe. Call before creating any model instances for best results,
|
|
148
|
+
though this can be called at any time.
|
|
102
149
|
|
|
103
150
|
Allows consumers to override the default async httpx client with custom configuration
|
|
104
151
|
(e.g., custom limits, timeouts, proxies, SSL verification, etc.).
|
|
@@ -119,7 +166,8 @@ def set_default_async_client(client: httpx.AsyncClient) -> None:
|
|
|
119
166
|
client: An httpx.AsyncClient instance to use as the global async client.
|
|
120
167
|
"""
|
|
121
168
|
global _global_async_client
|
|
122
|
-
|
|
169
|
+
with _async_client_lock:
|
|
170
|
+
_global_async_client = client
|
|
123
171
|
|
|
124
172
|
|
|
125
173
|
def fetch_with_retry(
|
agno/utils/os.py
ADDED
|
File without changes
|
agno/utils/remote.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, Dict, List, Union
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
from agno.models.message import Message
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def serialize_input(
|
|
10
|
+
input: Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]],
|
|
11
|
+
) -> str:
|
|
12
|
+
"""Serialize the input to a string."""
|
|
13
|
+
if isinstance(input, str):
|
|
14
|
+
return input
|
|
15
|
+
elif isinstance(input, dict):
|
|
16
|
+
return json.dumps(input)
|
|
17
|
+
elif isinstance(input, list):
|
|
18
|
+
if any(isinstance(item, Message) for item in input):
|
|
19
|
+
return json.dumps([item.to_dict() for item in input])
|
|
20
|
+
else:
|
|
21
|
+
return json.dumps(input)
|
|
22
|
+
elif isinstance(input, BaseModel):
|
|
23
|
+
return input.model_dump_json()
|