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.
Files changed (82) hide show
  1. agno/agent/__init__.py +2 -0
  2. agno/agent/agent.py +4 -53
  3. agno/agent/remote.py +351 -0
  4. agno/client/__init__.py +3 -0
  5. agno/client/os.py +2669 -0
  6. agno/db/base.py +20 -0
  7. agno/db/mongo/async_mongo.py +11 -0
  8. agno/db/mongo/mongo.py +10 -0
  9. agno/db/mysql/async_mysql.py +9 -0
  10. agno/db/mysql/mysql.py +9 -0
  11. agno/db/postgres/async_postgres.py +9 -0
  12. agno/db/postgres/postgres.py +9 -0
  13. agno/db/postgres/utils.py +3 -2
  14. agno/db/sqlite/async_sqlite.py +9 -0
  15. agno/db/sqlite/sqlite.py +11 -1
  16. agno/exceptions.py +23 -0
  17. agno/knowledge/chunking/semantic.py +123 -46
  18. agno/knowledge/reader/csv_reader.py +9 -14
  19. agno/knowledge/reader/docx_reader.py +2 -4
  20. agno/knowledge/reader/field_labeled_csv_reader.py +11 -17
  21. agno/knowledge/reader/json_reader.py +9 -17
  22. agno/knowledge/reader/markdown_reader.py +6 -6
  23. agno/knowledge/reader/pdf_reader.py +14 -11
  24. agno/knowledge/reader/pptx_reader.py +2 -4
  25. agno/knowledge/reader/s3_reader.py +2 -11
  26. agno/knowledge/reader/text_reader.py +6 -18
  27. agno/knowledge/reader/web_search_reader.py +4 -15
  28. agno/os/app.py +104 -23
  29. agno/os/auth.py +25 -1
  30. agno/os/interfaces/a2a/a2a.py +7 -6
  31. agno/os/interfaces/a2a/router.py +13 -13
  32. agno/os/interfaces/agui/agui.py +5 -3
  33. agno/os/interfaces/agui/router.py +23 -16
  34. agno/os/interfaces/base.py +7 -7
  35. agno/os/interfaces/slack/router.py +6 -6
  36. agno/os/interfaces/slack/slack.py +7 -7
  37. agno/os/interfaces/whatsapp/router.py +29 -6
  38. agno/os/interfaces/whatsapp/whatsapp.py +11 -8
  39. agno/os/managers.py +326 -0
  40. agno/os/mcp.py +651 -79
  41. agno/os/router.py +125 -18
  42. agno/os/routers/agents/router.py +65 -22
  43. agno/os/routers/agents/schema.py +16 -4
  44. agno/os/routers/database.py +5 -0
  45. agno/os/routers/evals/evals.py +93 -11
  46. agno/os/routers/evals/utils.py +6 -6
  47. agno/os/routers/knowledge/knowledge.py +104 -16
  48. agno/os/routers/memory/memory.py +124 -7
  49. agno/os/routers/metrics/metrics.py +21 -4
  50. agno/os/routers/session/session.py +141 -12
  51. agno/os/routers/teams/router.py +40 -14
  52. agno/os/routers/teams/schema.py +12 -4
  53. agno/os/routers/traces/traces.py +54 -4
  54. agno/os/routers/workflows/router.py +223 -117
  55. agno/os/routers/workflows/schema.py +65 -1
  56. agno/os/schema.py +38 -12
  57. agno/os/utils.py +87 -166
  58. agno/remote/__init__.py +3 -0
  59. agno/remote/base.py +484 -0
  60. agno/run/workflow.py +1 -0
  61. agno/team/__init__.py +2 -0
  62. agno/team/remote.py +287 -0
  63. agno/team/team.py +25 -54
  64. agno/tracing/exporter.py +10 -6
  65. agno/tracing/setup.py +2 -1
  66. agno/utils/agent.py +58 -1
  67. agno/utils/http.py +68 -20
  68. agno/utils/os.py +0 -0
  69. agno/utils/remote.py +23 -0
  70. agno/vectordb/chroma/chromadb.py +452 -16
  71. agno/vectordb/pgvector/pgvector.py +7 -0
  72. agno/vectordb/redis/redisdb.py +1 -1
  73. agno/workflow/__init__.py +2 -0
  74. agno/workflow/agent.py +2 -2
  75. agno/workflow/remote.py +222 -0
  76. agno/workflow/types.py +0 -73
  77. agno/workflow/workflow.py +119 -68
  78. {agno-2.3.15.dist-info → agno-2.3.17.dist-info}/METADATA +1 -1
  79. {agno-2.3.15.dist-info → agno-2.3.17.dist-info}/RECORD +82 -72
  80. {agno-2.3.15.dist-info → agno-2.3.17.dist-info}/WHEEL +0 -0
  81. {agno-2.3.15.dist-info → agno-2.3.17.dist-info}/licenses/LICENSE +0 -0
  82. {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
- if _global_sync_client is None or _global_sync_client.is_closed:
29
- _global_sync_client = httpx.Client(
30
- limits=httpx.Limits(max_connections=1000, max_keepalive_connections=200), http2=True, follow_redirects=True
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
- if _global_async_client is None or _global_async_client.is_closed:
43
- _global_async_client = httpx.AsyncClient(
44
- limits=httpx.Limits(max_connections=1000, max_keepalive_connections=200), http2=True, follow_redirects=True
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
- if _global_sync_client is not None and not _global_sync_client.is_closed:
56
- _global_sync_client.close()
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
- if _global_sync_client is not None and not _global_sync_client.is_closed:
66
- _global_sync_client.close()
67
- if _global_async_client is not None and not _global_async_client.is_closed:
68
- await _global_async_client.aclose()
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
- IMPORTANT: Call before creating any model instances. Models cache clients on first use.
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
- _global_sync_client = client
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
- IMPORTANT: Call before creating any model instances. Models cache clients on first use.
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
- _global_async_client = client
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()