agno 2.0.0rc2__py3-none-any.whl → 2.0.2__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 +78 -135
- agno/knowledge/knowledge.py +1 -1
- agno/knowledge/reranker/__init__.py +3 -0
- agno/media.py +269 -268
- agno/models/base.py +13 -48
- agno/models/google/gemini.py +11 -10
- agno/models/message.py +4 -4
- agno/models/ollama/chat.py +1 -1
- agno/models/openai/chat.py +33 -14
- agno/models/response.py +5 -5
- agno/os/app.py +8 -5
- agno/run/agent.py +28 -28
- agno/run/base.py +9 -19
- agno/run/team.py +24 -24
- agno/run/workflow.py +16 -16
- agno/team/team.py +72 -154
- agno/tools/brightdata.py +3 -3
- agno/tools/cartesia.py +3 -5
- agno/tools/dalle.py +7 -4
- agno/tools/desi_vocal.py +2 -2
- agno/tools/e2b.py +6 -6
- agno/tools/eleven_labs.py +3 -3
- agno/tools/fal.py +4 -4
- agno/tools/function.py +7 -7
- agno/tools/giphy.py +2 -2
- agno/tools/lumalab.py +3 -3
- agno/tools/models/azure_openai.py +2 -2
- agno/tools/models/gemini.py +3 -3
- agno/tools/models/groq.py +3 -5
- agno/tools/models/nebius.py +2 -2
- agno/tools/models_labs.py +5 -5
- agno/tools/openai.py +4 -9
- agno/tools/opencv.py +3 -3
- agno/tools/replicate.py +7 -7
- agno/utils/events.py +5 -5
- agno/utils/gemini.py +1 -1
- agno/utils/mcp.py +3 -3
- agno/utils/models/aws_claude.py +1 -1
- agno/utils/models/cohere.py +1 -1
- agno/utils/models/watsonx.py +1 -1
- agno/utils/openai.py +1 -1
- agno/vectordb/lancedb/lance_db.py +82 -25
- agno/workflow/step.py +7 -7
- agno/workflow/types.py +13 -13
- agno/workflow/workflow.py +28 -28
- {agno-2.0.0rc2.dist-info → agno-2.0.2.dist-info}/METADATA +140 -1
- {agno-2.0.0rc2.dist-info → agno-2.0.2.dist-info}/RECORD +50 -50
- agno-2.0.2.dist-info/licenses/LICENSE +201 -0
- agno-2.0.0rc2.dist-info/licenses/LICENSE +0 -375
- {agno-2.0.0rc2.dist-info → agno-2.0.2.dist-info}/WHEEL +0 -0
- {agno-2.0.0rc2.dist-info → agno-2.0.2.dist-info}/top_level.txt +0 -0
|
@@ -140,6 +140,29 @@ class LanceDb(VectorDb):
|
|
|
140
140
|
|
|
141
141
|
log_debug(f"Initialized LanceDb with table: '{self.table_name}'")
|
|
142
142
|
|
|
143
|
+
def _prepare_vector(self, embedding) -> List[float]:
|
|
144
|
+
"""Prepare vector embedding for insertion, ensuring correct dimensions and type."""
|
|
145
|
+
if embedding is not None:
|
|
146
|
+
# Convert to list of floats
|
|
147
|
+
vector = [float(x) for x in embedding]
|
|
148
|
+
|
|
149
|
+
# Ensure vector has correct dimensions if specified
|
|
150
|
+
if self.dimensions:
|
|
151
|
+
if len(vector) != self.dimensions:
|
|
152
|
+
if len(vector) > self.dimensions:
|
|
153
|
+
# Truncate if too long
|
|
154
|
+
vector = vector[: self.dimensions]
|
|
155
|
+
log_debug(f"Truncated vector from {len(embedding)} to {self.dimensions} dimensions")
|
|
156
|
+
else:
|
|
157
|
+
# Pad with zeros if too short
|
|
158
|
+
vector.extend([0.0] * (self.dimensions - len(vector)))
|
|
159
|
+
log_debug(f"Padded vector from {len(embedding)} to {self.dimensions} dimensions")
|
|
160
|
+
|
|
161
|
+
return vector
|
|
162
|
+
else:
|
|
163
|
+
# Fallback if embedding is None
|
|
164
|
+
return [0.0] * (self.dimensions or 1536)
|
|
165
|
+
|
|
143
166
|
async def _get_async_connection(self) -> lancedb.AsyncConnection:
|
|
144
167
|
"""Get or create an async connection to LanceDB."""
|
|
145
168
|
if self.async_connection is None:
|
|
@@ -174,22 +197,37 @@ class LanceDb(VectorDb):
|
|
|
174
197
|
async def async_create(self) -> None:
|
|
175
198
|
"""Create the table asynchronously if it does not exist."""
|
|
176
199
|
if not await self.async_exists():
|
|
177
|
-
|
|
178
|
-
|
|
200
|
+
try:
|
|
201
|
+
conn = await self._get_async_connection()
|
|
202
|
+
schema = self._base_schema()
|
|
179
203
|
|
|
180
|
-
|
|
181
|
-
|
|
204
|
+
log_debug(f"Creating table asynchronously: {self.table_name}")
|
|
205
|
+
self.async_table = await conn.create_table(
|
|
206
|
+
self.table_name, schema=schema, mode="overwrite", exist_ok=True
|
|
207
|
+
)
|
|
208
|
+
log_debug(f"Successfully created async table: {self.table_name}")
|
|
209
|
+
except Exception as e:
|
|
210
|
+
logger.error(f"Error creating async table: {e}")
|
|
211
|
+
# Try to fall back to sync table creation
|
|
212
|
+
try:
|
|
213
|
+
log_debug("Falling back to sync table creation")
|
|
214
|
+
self.table = self._init_table()
|
|
215
|
+
log_debug("Sync table created successfully")
|
|
216
|
+
except Exception as sync_e:
|
|
217
|
+
logger.error(f"Sync table creation also failed: {sync_e}")
|
|
218
|
+
raise
|
|
182
219
|
|
|
183
220
|
def _base_schema(self) -> pa.Schema:
|
|
221
|
+
# Use fixed-size list for vector field as required by LanceDB
|
|
222
|
+
if self.dimensions:
|
|
223
|
+
vector_field = pa.field(self._vector_col, pa.list_(pa.float32(), self.dimensions))
|
|
224
|
+
else:
|
|
225
|
+
# Fallback to dynamic list if dimensions not known (should be rare)
|
|
226
|
+
vector_field = pa.field(self._vector_col, pa.list_(pa.float32()))
|
|
227
|
+
|
|
184
228
|
return pa.schema(
|
|
185
229
|
[
|
|
186
|
-
|
|
187
|
-
self._vector_col,
|
|
188
|
-
pa.list_(
|
|
189
|
-
pa.float32(),
|
|
190
|
-
len(self.embedder.get_embedding("test")), # type: ignore
|
|
191
|
-
),
|
|
192
|
-
),
|
|
230
|
+
vector_field,
|
|
193
231
|
pa.field(self._id, pa.string()),
|
|
194
232
|
pa.field("payload", pa.string()),
|
|
195
233
|
]
|
|
@@ -278,7 +316,7 @@ class LanceDb(VectorDb):
|
|
|
278
316
|
data.append(
|
|
279
317
|
{
|
|
280
318
|
"id": doc_id,
|
|
281
|
-
"vector": document.embedding,
|
|
319
|
+
"vector": self._prepare_vector(document.embedding),
|
|
282
320
|
"payload": json.dumps(payload),
|
|
283
321
|
}
|
|
284
322
|
)
|
|
@@ -343,7 +381,7 @@ class LanceDb(VectorDb):
|
|
|
343
381
|
data.append(
|
|
344
382
|
{
|
|
345
383
|
"id": doc_id,
|
|
346
|
-
"vector": document.embedding,
|
|
384
|
+
"vector": self._prepare_vector(document.embedding),
|
|
347
385
|
"payload": json.dumps(payload),
|
|
348
386
|
}
|
|
349
387
|
)
|
|
@@ -356,6 +394,19 @@ class LanceDb(VectorDb):
|
|
|
356
394
|
try:
|
|
357
395
|
await self._get_async_connection()
|
|
358
396
|
|
|
397
|
+
# Ensure the async table is created before inserting
|
|
398
|
+
if self.async_table is None:
|
|
399
|
+
try:
|
|
400
|
+
await self.async_create()
|
|
401
|
+
except Exception as create_e:
|
|
402
|
+
logger.error(f"Failed to create async table: {create_e}")
|
|
403
|
+
# Continue to fallback logic below
|
|
404
|
+
|
|
405
|
+
if self.async_table is None:
|
|
406
|
+
# Fall back to sync insertion if async table creation failed
|
|
407
|
+
logger.warning("Async table not available, falling back to sync insertion")
|
|
408
|
+
return self.insert(content_hash, documents, filters)
|
|
409
|
+
|
|
359
410
|
if self.on_bad_vectors is not None:
|
|
360
411
|
await self.async_table.add(data, on_bad_vectors=self.on_bad_vectors, fill_value=self.fill_value) # type: ignore
|
|
361
412
|
else:
|
|
@@ -367,7 +418,14 @@ class LanceDb(VectorDb):
|
|
|
367
418
|
self._refresh_sync_connection()
|
|
368
419
|
except Exception as e:
|
|
369
420
|
logger.error(f"Error during async document insertion: {e}")
|
|
370
|
-
|
|
421
|
+
# Try falling back to sync insertion as a last resort
|
|
422
|
+
try:
|
|
423
|
+
logger.warning("Async insertion failed, attempting sync fallback")
|
|
424
|
+
self.insert(content_hash, documents, filters)
|
|
425
|
+
logger.info("Sync fallback successful")
|
|
426
|
+
except Exception as sync_e:
|
|
427
|
+
logger.error(f"Sync fallback also failed: {sync_e}")
|
|
428
|
+
raise e from sync_e
|
|
371
429
|
|
|
372
430
|
def upsert_available(self) -> bool:
|
|
373
431
|
"""Check if upsert is available in LanceDB."""
|
|
@@ -638,26 +696,25 @@ class LanceDb(VectorDb):
|
|
|
638
696
|
return await self.async_table.count_rows()
|
|
639
697
|
return 0
|
|
640
698
|
|
|
641
|
-
def _async_get_count_sync(self) -> int:
|
|
642
|
-
"""Helper method to run async_get_count in a new thread with its own event loop"""
|
|
643
|
-
import asyncio
|
|
644
|
-
|
|
645
|
-
return asyncio.run(self.async_get_count())
|
|
646
|
-
|
|
647
699
|
def get_count(self) -> int:
|
|
648
700
|
# If we have data in the async table but sync table isn't available, try to get count from async table
|
|
649
701
|
if self.async_table is not None:
|
|
650
702
|
try:
|
|
651
703
|
import asyncio
|
|
652
704
|
|
|
653
|
-
# Check if we're already in an
|
|
705
|
+
# Check if we're already in an event loop
|
|
654
706
|
try:
|
|
655
|
-
|
|
707
|
+
asyncio.get_running_loop()
|
|
708
|
+
# We're in an async context, can't use asyncio.run
|
|
709
|
+
log_debug("Already in async context, falling back to sync table for count")
|
|
656
710
|
except RuntimeError:
|
|
657
711
|
# No event loop running, safe to use asyncio.run
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
712
|
+
try:
|
|
713
|
+
return asyncio.run(self.async_get_count())
|
|
714
|
+
except Exception as e:
|
|
715
|
+
log_debug(f"Failed to get async count: {e}")
|
|
716
|
+
except Exception as e:
|
|
717
|
+
log_debug(f"Error in async count logic: {e}")
|
|
661
718
|
|
|
662
719
|
if self.exists() and self.table:
|
|
663
720
|
return self.table.count_rows()
|
agno/workflow/step.py
CHANGED
|
@@ -7,7 +7,7 @@ from uuid import uuid4
|
|
|
7
7
|
from pydantic import BaseModel
|
|
8
8
|
|
|
9
9
|
from agno.agent import Agent
|
|
10
|
-
from agno.media import Audio,
|
|
10
|
+
from agno.media import Audio, Image, Video
|
|
11
11
|
from agno.models.metrics import Metrics
|
|
12
12
|
from agno.run.agent import RunOutput
|
|
13
13
|
from agno.run.team import TeamRunOutput
|
|
@@ -931,20 +931,20 @@ class Step:
|
|
|
931
931
|
# Convert any other type to string
|
|
932
932
|
return RunOutput(content=str(result))
|
|
933
933
|
|
|
934
|
-
def _convert_audio_artifacts_to_audio(self, audio_artifacts: List[
|
|
934
|
+
def _convert_audio_artifacts_to_audio(self, audio_artifacts: List[Audio]) -> List[Audio]:
|
|
935
935
|
"""Convert AudioArtifact objects to Audio objects"""
|
|
936
936
|
audios = []
|
|
937
937
|
for audio_artifact in audio_artifacts:
|
|
938
938
|
if audio_artifact.url:
|
|
939
939
|
audios.append(Audio(url=audio_artifact.url))
|
|
940
|
-
elif audio_artifact.
|
|
941
|
-
audios.append(Audio(content=audio_artifact.
|
|
940
|
+
elif audio_artifact.content:
|
|
941
|
+
audios.append(Audio(content=audio_artifact.content))
|
|
942
942
|
else:
|
|
943
|
-
logger.warning(f"Skipping AudioArtifact with no URL or
|
|
943
|
+
logger.warning(f"Skipping AudioArtifact with no URL or content: {audio_artifact}")
|
|
944
944
|
continue
|
|
945
945
|
return audios
|
|
946
946
|
|
|
947
|
-
def _convert_image_artifacts_to_images(self, image_artifacts: List[
|
|
947
|
+
def _convert_image_artifacts_to_images(self, image_artifacts: List[Image]) -> List[Image]:
|
|
948
948
|
"""
|
|
949
949
|
Convert ImageArtifact objects to Image objects with proper content handling.
|
|
950
950
|
|
|
@@ -996,7 +996,7 @@ class Step:
|
|
|
996
996
|
|
|
997
997
|
return images
|
|
998
998
|
|
|
999
|
-
def _convert_video_artifacts_to_videos(self, video_artifacts: List[
|
|
999
|
+
def _convert_video_artifacts_to_videos(self, video_artifacts: List[Video]) -> List[Video]:
|
|
1000
1000
|
"""
|
|
1001
1001
|
Convert VideoArtifact objects to Video objects with proper content handling.
|
|
1002
1002
|
|
agno/workflow/types.py
CHANGED
|
@@ -5,7 +5,7 @@ from typing import Any, Dict, List, Optional, Union
|
|
|
5
5
|
from fastapi import WebSocket
|
|
6
6
|
from pydantic import BaseModel
|
|
7
7
|
|
|
8
|
-
from agno.media import
|
|
8
|
+
from agno.media import Audio, File, Image, Video
|
|
9
9
|
from agno.models.metrics import Metrics
|
|
10
10
|
from agno.utils.log import log_warning
|
|
11
11
|
|
|
@@ -19,9 +19,9 @@ class WorkflowExecutionInput:
|
|
|
19
19
|
additional_data: Optional[Dict[str, Any]] = None
|
|
20
20
|
|
|
21
21
|
# Media inputs
|
|
22
|
-
images: Optional[List[
|
|
23
|
-
videos: Optional[List[
|
|
24
|
-
audio: Optional[List[
|
|
22
|
+
images: Optional[List[Image]] = None
|
|
23
|
+
videos: Optional[List[Video]] = None
|
|
24
|
+
audio: Optional[List[Audio]] = None
|
|
25
25
|
files: Optional[List[File]] = None
|
|
26
26
|
|
|
27
27
|
def get_input_as_string(self) -> Optional[str]:
|
|
@@ -72,9 +72,9 @@ class StepInput:
|
|
|
72
72
|
additional_data: Optional[Dict[str, Any]] = None
|
|
73
73
|
|
|
74
74
|
# Media inputs
|
|
75
|
-
images: Optional[List[
|
|
76
|
-
videos: Optional[List[
|
|
77
|
-
audio: Optional[List[
|
|
75
|
+
images: Optional[List[Image]] = None
|
|
76
|
+
videos: Optional[List[Video]] = None
|
|
77
|
+
audio: Optional[List[Audio]] = None
|
|
78
78
|
files: Optional[List[File]] = None
|
|
79
79
|
|
|
80
80
|
def get_input_as_string(self) -> Optional[str]:
|
|
@@ -225,9 +225,9 @@ class StepOutput:
|
|
|
225
225
|
step_run_id: Optional[str] = None
|
|
226
226
|
|
|
227
227
|
# Media outputs
|
|
228
|
-
images: Optional[List[
|
|
229
|
-
videos: Optional[List[
|
|
230
|
-
audio: Optional[List[
|
|
228
|
+
images: Optional[List[Image]] = None
|
|
229
|
+
videos: Optional[List[Video]] = None
|
|
230
|
+
audio: Optional[List[Audio]] = None
|
|
231
231
|
files: Optional[List[File]] = None
|
|
232
232
|
|
|
233
233
|
# Metrics for this step execution
|
|
@@ -282,15 +282,15 @@ class StepOutput:
|
|
|
282
282
|
# Reconstruct media artifacts
|
|
283
283
|
images = data.get("images")
|
|
284
284
|
if images:
|
|
285
|
-
images = [
|
|
285
|
+
images = [Image.model_validate(img) for img in images]
|
|
286
286
|
|
|
287
287
|
videos = data.get("videos")
|
|
288
288
|
if videos:
|
|
289
|
-
videos = [
|
|
289
|
+
videos = [Video.model_validate(vid) for vid in videos]
|
|
290
290
|
|
|
291
291
|
audio = data.get("audio")
|
|
292
292
|
if audio:
|
|
293
|
-
audio = [
|
|
293
|
+
audio = [Audio.model_validate(aud) for aud in audio]
|
|
294
294
|
|
|
295
295
|
files = data.get("files")
|
|
296
296
|
if files:
|
agno/workflow/workflow.py
CHANGED
|
@@ -26,7 +26,7 @@ from pydantic import BaseModel
|
|
|
26
26
|
from agno.agent.agent import Agent
|
|
27
27
|
from agno.db.base import BaseDb, SessionType
|
|
28
28
|
from agno.exceptions import RunCancelledException
|
|
29
|
-
from agno.media import Audio,
|
|
29
|
+
from agno.media import Audio, File, Image, Video
|
|
30
30
|
from agno.models.message import Message
|
|
31
31
|
from agno.models.metrics import Metrics
|
|
32
32
|
from agno.run.agent import RunEvent
|
|
@@ -745,9 +745,9 @@ class Workflow:
|
|
|
745
745
|
self,
|
|
746
746
|
execution_input: WorkflowExecutionInput,
|
|
747
747
|
previous_step_outputs: Optional[Dict[str, StepOutput]] = None,
|
|
748
|
-
shared_images: Optional[List[
|
|
749
|
-
shared_videos: Optional[List[
|
|
750
|
-
shared_audio: Optional[List[
|
|
748
|
+
shared_images: Optional[List[Image]] = None,
|
|
749
|
+
shared_videos: Optional[List[Video]] = None,
|
|
750
|
+
shared_audio: Optional[List[Audio]] = None,
|
|
751
751
|
shared_files: Optional[List[File]] = None,
|
|
752
752
|
) -> StepInput:
|
|
753
753
|
"""Helper method to create StepInput with enhanced data flow support"""
|
|
@@ -890,12 +890,12 @@ class Workflow:
|
|
|
890
890
|
collected_step_outputs: List[Union[StepOutput, List[StepOutput]]] = []
|
|
891
891
|
previous_step_outputs: Dict[str, StepOutput] = {}
|
|
892
892
|
|
|
893
|
-
shared_images: List[
|
|
894
|
-
output_images: List[
|
|
895
|
-
shared_videos: List[
|
|
896
|
-
output_videos: List[
|
|
897
|
-
shared_audio: List[
|
|
898
|
-
output_audio: List[
|
|
893
|
+
shared_images: List[Image] = execution_input.images or []
|
|
894
|
+
output_images: List[Image] = (execution_input.images or []).copy() # Start with input images
|
|
895
|
+
shared_videos: List[Video] = execution_input.videos or []
|
|
896
|
+
output_videos: List[Video] = (execution_input.videos or []).copy() # Start with input videos
|
|
897
|
+
shared_audio: List[Audio] = execution_input.audio or []
|
|
898
|
+
output_audio: List[Audio] = (execution_input.audio or []).copy() # Start with input audio
|
|
899
899
|
shared_files: List[File] = execution_input.files or []
|
|
900
900
|
output_files: List[File] = (execution_input.files or []).copy() # Start with input files
|
|
901
901
|
|
|
@@ -1050,12 +1050,12 @@ class Workflow:
|
|
|
1050
1050
|
collected_step_outputs: List[Union[StepOutput, List[StepOutput]]] = []
|
|
1051
1051
|
previous_step_outputs: Dict[str, StepOutput] = {}
|
|
1052
1052
|
|
|
1053
|
-
shared_images: List[
|
|
1054
|
-
output_images: List[
|
|
1055
|
-
shared_videos: List[
|
|
1056
|
-
output_videos: List[
|
|
1057
|
-
shared_audio: List[
|
|
1058
|
-
output_audio: List[
|
|
1053
|
+
shared_images: List[Image] = execution_input.images or []
|
|
1054
|
+
output_images: List[Image] = (execution_input.images or []).copy() # Start with input images
|
|
1055
|
+
shared_videos: List[Video] = execution_input.videos or []
|
|
1056
|
+
output_videos: List[Video] = (execution_input.videos or []).copy() # Start with input videos
|
|
1057
|
+
shared_audio: List[Audio] = execution_input.audio or []
|
|
1058
|
+
output_audio: List[Audio] = (execution_input.audio or []).copy() # Start with input audio
|
|
1059
1059
|
shared_files: List[File] = execution_input.files or []
|
|
1060
1060
|
output_files: List[File] = (execution_input.files or []).copy() # Start with input files
|
|
1061
1061
|
|
|
@@ -1326,12 +1326,12 @@ class Workflow:
|
|
|
1326
1326
|
collected_step_outputs: List[Union[StepOutput, List[StepOutput]]] = []
|
|
1327
1327
|
previous_step_outputs: Dict[str, StepOutput] = {}
|
|
1328
1328
|
|
|
1329
|
-
shared_images: List[
|
|
1330
|
-
output_images: List[
|
|
1331
|
-
shared_videos: List[
|
|
1332
|
-
output_videos: List[
|
|
1333
|
-
shared_audio: List[
|
|
1334
|
-
output_audio: List[
|
|
1329
|
+
shared_images: List[Image] = execution_input.images or []
|
|
1330
|
+
output_images: List[Image] = (execution_input.images or []).copy() # Start with input images
|
|
1331
|
+
shared_videos: List[Video] = execution_input.videos or []
|
|
1332
|
+
output_videos: List[Video] = (execution_input.videos or []).copy() # Start with input videos
|
|
1333
|
+
shared_audio: List[Audio] = execution_input.audio or []
|
|
1334
|
+
output_audio: List[Audio] = (execution_input.audio or []).copy() # Start with input audio
|
|
1335
1335
|
shared_files: List[File] = execution_input.files or []
|
|
1336
1336
|
output_files: List[File] = (execution_input.files or []).copy() # Start with input files
|
|
1337
1337
|
|
|
@@ -1484,12 +1484,12 @@ class Workflow:
|
|
|
1484
1484
|
collected_step_outputs: List[Union[StepOutput, List[StepOutput]]] = []
|
|
1485
1485
|
previous_step_outputs: Dict[str, StepOutput] = {}
|
|
1486
1486
|
|
|
1487
|
-
shared_images: List[
|
|
1488
|
-
output_images: List[
|
|
1489
|
-
shared_videos: List[
|
|
1490
|
-
output_videos: List[
|
|
1491
|
-
shared_audio: List[
|
|
1492
|
-
output_audio: List[
|
|
1487
|
+
shared_images: List[Image] = execution_input.images or []
|
|
1488
|
+
output_images: List[Image] = (execution_input.images or []).copy() # Start with input images
|
|
1489
|
+
shared_videos: List[Video] = execution_input.videos or []
|
|
1490
|
+
output_videos: List[Video] = (execution_input.videos or []).copy() # Start with input videos
|
|
1491
|
+
shared_audio: List[Audio] = execution_input.audio or []
|
|
1492
|
+
output_audio: List[Audio] = (execution_input.audio or []).copy() # Start with input audio
|
|
1493
1493
|
shared_files: List[File] = execution_input.files or []
|
|
1494
1494
|
output_files: List[File] = (execution_input.files or []).copy() # Start with input files
|
|
1495
1495
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agno
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: Agno: a lightweight library for building Multi-Agent Systems
|
|
5
5
|
Author-email: Ashpreet Bedi <ashpreet@agno.com>
|
|
6
6
|
Project-URL: homepage, https://agno.com
|
|
@@ -353,3 +353,142 @@ Requires-Dist: agno[agui]; extra == "tests"
|
|
|
353
353
|
Requires-Dist: twine; extra == "tests"
|
|
354
354
|
Requires-Dist: build; extra == "tests"
|
|
355
355
|
Dynamic: license-file
|
|
356
|
+
|
|
357
|
+
<div align="center" id="top">
|
|
358
|
+
<a href="https://docs.agno.com">
|
|
359
|
+
<picture>
|
|
360
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://agno-public.s3.us-east-1.amazonaws.com/assets/logo-dark.svg">
|
|
361
|
+
<source media="(prefers-color-scheme: light)" srcset="https://agno-public.s3.us-east-1.amazonaws.com/assets/logo-light.svg">
|
|
362
|
+
<img src="https://agno-public.s3.us-east-1.amazonaws.com/assets/logo-light.svg" alt="Agno">
|
|
363
|
+
</picture>
|
|
364
|
+
</a>
|
|
365
|
+
</div>
|
|
366
|
+
<div align="center">
|
|
367
|
+
<a href="https://docs.agno.com">📚 Documentation</a> |
|
|
368
|
+
<a href="https://docs.agno.com/examples/introduction">💡 Examples</a> |
|
|
369
|
+
<a href="https://github.com/agno-agi/agno/stargazers">🌟 Star Us</a>
|
|
370
|
+
</div>
|
|
371
|
+
|
|
372
|
+
## What is Agno?
|
|
373
|
+
|
|
374
|
+
[Agno](https://docs.agno.com) is a high-performance runtime for multi-agent systems. Use it to build, run and manage secure multi-agent systems in your cloud.
|
|
375
|
+
|
|
376
|
+
Agno gives you the fastest framework for building agents with session management, memory, knowledge, human in the loop and MCP support. You can put agents together as an autonomous multi-agent team, or build step-based agentic workflows for full control over complex multi-step processes.
|
|
377
|
+
|
|
378
|
+
In 10 lines of code, we can build an Agent that will fetch the top stories from HackerNews and summarize them.
|
|
379
|
+
|
|
380
|
+
```python hackernews_agent.py
|
|
381
|
+
from agno.agent import Agent
|
|
382
|
+
from agno.models.anthropic import Claude
|
|
383
|
+
from agno.tools.hackernews import HackerNewsTools
|
|
384
|
+
|
|
385
|
+
agent = Agent(
|
|
386
|
+
model=Claude(id="claude-sonnet-4-0"),
|
|
387
|
+
tools=[HackerNewsTools()],
|
|
388
|
+
markdown=True,
|
|
389
|
+
)
|
|
390
|
+
agent.print_response("Summarize the top 5 stories on hackernews", stream=True)
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
But the real advantage of Agno is its [AgentOS](https://docs.agno.com/agent-os/introduction) runtime:
|
|
394
|
+
|
|
395
|
+
1. You get a pre-built FastAPI app for running your agentic system, meaning you start building your product on day one. This is a remarkable advantage over other solutions or rolling your own.
|
|
396
|
+
2. You also get a control plane which connects directly to your AgentOS for testing, monitoring and managing your system. This gives you unmatched visibility and control over your system.
|
|
397
|
+
3. Your AgentOS runs in your cloud and you get complete data privacy because no data ever leaves your system. This is incredible for security conscious enterprises that can't send traces to external services.
|
|
398
|
+
|
|
399
|
+
For organizations building agents, Agno provides the complete solution. You get the fastest framework for building agents (speed of development and execution), a pre-built FastAPI app that lets you build your product on day one, and a control plane for managing your system.
|
|
400
|
+
|
|
401
|
+
We bring a novel architecture that no other framework provides, your AgentOS runs securely in your cloud, and the control plane connects directly to it from your browser. You don't need to send data to external services or pay retention costs, you get complete privacy and control.
|
|
402
|
+
|
|
403
|
+
## Getting started
|
|
404
|
+
|
|
405
|
+
If you're new to Agno, follow our [quickstart](https://docs.agno.com/introduction/quickstart) to build your first Agent and run it using the AgentOS.
|
|
406
|
+
|
|
407
|
+
After that, checkout the [examples gallery](https://docs.agno.com/examples/introduction) and build real-world applications with Agno.
|
|
408
|
+
|
|
409
|
+
## Documentation, Community & More examples
|
|
410
|
+
|
|
411
|
+
- Docs: <a href="https://docs.agno.com" target="_blank" rel="noopener noreferrer">docs.agno.com</a>
|
|
412
|
+
- Cookbook: <a href="https://github.com/agno-agi/agno/tree/main/cookbook" target="_blank" rel="noopener noreferrer">Cookbook</a>
|
|
413
|
+
- Community forum: <a href="https://community.agno.com/" target="_blank" rel="noopener noreferrer">community.agno.com</a>
|
|
414
|
+
- Discord: <a href="https://discord.gg/4MtYHHrgA8" target="_blank" rel="noopener noreferrer">discord</a>
|
|
415
|
+
|
|
416
|
+
## Setup your coding agent to use Agno
|
|
417
|
+
|
|
418
|
+
For LLMs and AI assistants to understand and navigate Agno's documentation, we provide an [llms.txt](https://docs.agno.com/llms.txt) or [llms-full.txt](https://docs.agno.com/llms-full.txt) file.
|
|
419
|
+
|
|
420
|
+
This file is built for AI systems to efficiently parse and reference our documentation.
|
|
421
|
+
|
|
422
|
+
### IDE Integration
|
|
423
|
+
|
|
424
|
+
When building Agno agents, using Agno documentation as a source in your IDE is a great way to speed up your development. Here's how to integrate with Cursor:
|
|
425
|
+
|
|
426
|
+
1. In Cursor, go to the "Cursor Settings" menu.
|
|
427
|
+
2. Find the "Indexing & Docs" section.
|
|
428
|
+
3. Add `https://docs.agno.com/llms-full.txt` to the list of documentation URLs.
|
|
429
|
+
4. Save the changes.
|
|
430
|
+
|
|
431
|
+
Now, Cursor will have access to the Agno documentation. You can do the same with other IDEs like VSCode, Windsurf etc.
|
|
432
|
+
|
|
433
|
+
## Performance
|
|
434
|
+
|
|
435
|
+
At Agno, we're obsessed with performance. Why? because even simple AI workflows can spawn thousands of Agents. Scale that to a modest number of users and performance becomes a bottleneck. Agno is designed for building highly performant agentic systems:
|
|
436
|
+
|
|
437
|
+
- Agent instantiation: ~3μs on average
|
|
438
|
+
- Memory footprint: ~6.5Kib on average
|
|
439
|
+
|
|
440
|
+
> Tested on an Apple M4 Mackbook Pro.
|
|
441
|
+
|
|
442
|
+
While an Agent's run-time is bottlenecked by inference, we must do everything possible to minimize execution time, reduce memory usage, and parallelize tool calls. These numbers may seem trivial at first, but our experience shows that they add up even at a reasonably small scale.
|
|
443
|
+
|
|
444
|
+
### Instantiation time
|
|
445
|
+
|
|
446
|
+
Let's measure the time it takes for an Agent with 1 tool to start up. We'll run the evaluation 1000 times to get a baseline measurement.
|
|
447
|
+
|
|
448
|
+
You should run the evaluation yourself on your own machine, please, do not take these results at face value.
|
|
449
|
+
|
|
450
|
+
```shell
|
|
451
|
+
# Setup virtual environment
|
|
452
|
+
./scripts/perf_setup.sh
|
|
453
|
+
source .venvs/perfenv/bin/activate
|
|
454
|
+
# OR Install dependencies manually
|
|
455
|
+
# pip install openai agno langgraph langchain_openai
|
|
456
|
+
|
|
457
|
+
# Agno
|
|
458
|
+
python evals/performance/instantiation_with_tool.py
|
|
459
|
+
|
|
460
|
+
# LangGraph
|
|
461
|
+
python evals/performance/other/langgraph_instantiation.py
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
> The following evaluation is run on an Apple M4 Mackbook Pro. It also runs as a Github action on this repo.
|
|
465
|
+
|
|
466
|
+
LangGraph is on the right, **let's start it first and give it a head start**.
|
|
467
|
+
|
|
468
|
+
Agno is on the left, notice how it finishes before LangGraph gets 1/2 way through the runtime measurement, and hasn't even started the memory measurement. That's how fast Agno is.
|
|
469
|
+
|
|
470
|
+
https://github.com/user-attachments/assets/ba466d45-75dd-45ac-917b-0a56c5742e23
|
|
471
|
+
|
|
472
|
+
### Memory usage
|
|
473
|
+
|
|
474
|
+
To measure memory usage, we use the `tracemalloc` library. We first calculate a baseline memory usage by running an empty function, then run the Agent 1000x times and calculate the difference. This gives a (reasonably) isolated measurement of the memory usage of the Agent.
|
|
475
|
+
|
|
476
|
+
We recommend running the evaluation yourself on your own machine, and digging into the code to see how it works. If we've made a mistake, please let us know.
|
|
477
|
+
|
|
478
|
+
### Conclusion
|
|
479
|
+
|
|
480
|
+
Agno agents are designed for performance and while we do share some benchmarks against other frameworks, we should be mindful that accuracy and reliability are more important than speed.
|
|
481
|
+
|
|
482
|
+
Given that each framework is different and we won't be able to tune their performance like we do with Agno, for future benchmarks we'll only be comparing against ourselves.
|
|
483
|
+
|
|
484
|
+
## Contributions
|
|
485
|
+
|
|
486
|
+
We welcome contributions, read our [contributing guide](https://github.com/agno-agi/agno/blob/v2.0/CONTRIBUTING.md) to get started.
|
|
487
|
+
|
|
488
|
+
## Telemetry
|
|
489
|
+
|
|
490
|
+
Agno logs which model an agent used so we can prioritize updates to the most popular providers. You can disable this by setting `AGNO_TELEMETRY=false` in your environment.
|
|
491
|
+
|
|
492
|
+
<p align="left">
|
|
493
|
+
<a href="#top">⬆️ Back to Top</a>
|
|
494
|
+
</p>
|