agno 1.7.12__py3-none-any.whl → 1.8.0__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/app/agui/utils.py +1 -1
- agno/app/fastapi/async_router.py +13 -10
- agno/knowledge/gcs/pdf.py +2 -2
- agno/media.py +24 -3
- agno/models/google/gemini.py +16 -1
- agno/tools/duckduckgo.py +8 -16
- agno/tools/github.py +26 -14
- agno/tools/memori.py +387 -0
- agno/tools/scrapegraph.py +65 -0
- agno/vectordb/pgvector/pgvector.py +23 -39
- agno/workflow/v2/step.py +4 -0
- agno/workflow/v2/types.py +11 -1
- agno/workflow/v2/workflow.py +54 -1
- {agno-1.7.12.dist-info → agno-1.8.0.dist-info}/METADATA +7 -4
- {agno-1.7.12.dist-info → agno-1.8.0.dist-info}/RECORD +19 -18
- {agno-1.7.12.dist-info → agno-1.8.0.dist-info}/WHEEL +0 -0
- {agno-1.7.12.dist-info → agno-1.8.0.dist-info}/entry_points.txt +0 -0
- {agno-1.7.12.dist-info → agno-1.8.0.dist-info}/licenses/LICENSE +0 -0
- {agno-1.7.12.dist-info → agno-1.8.0.dist-info}/top_level.txt +0 -0
agno/tools/scrapegraph.py
CHANGED
|
@@ -22,6 +22,7 @@ class ScrapeGraphTools(Toolkit):
|
|
|
22
22
|
markdownify: bool = False,
|
|
23
23
|
crawl: bool = False,
|
|
24
24
|
searchscraper: bool = False,
|
|
25
|
+
agentic_crawler: bool = False,
|
|
25
26
|
**kwargs,
|
|
26
27
|
):
|
|
27
28
|
self.api_key: Optional[str] = api_key or os.getenv("SGAI_API_KEY")
|
|
@@ -41,6 +42,8 @@ class ScrapeGraphTools(Toolkit):
|
|
|
41
42
|
tools.append(self.crawl)
|
|
42
43
|
if searchscraper:
|
|
43
44
|
tools.append(self.searchscraper)
|
|
45
|
+
if agentic_crawler:
|
|
46
|
+
tools.append(self.agentic_crawler)
|
|
44
47
|
|
|
45
48
|
super().__init__(name="scrapegraph_tools", tools=tools, **kwargs)
|
|
46
49
|
|
|
@@ -110,6 +113,68 @@ class ScrapeGraphTools(Toolkit):
|
|
|
110
113
|
except Exception as e:
|
|
111
114
|
return json.dumps({"error": str(e)})
|
|
112
115
|
|
|
116
|
+
def agentic_crawler(
|
|
117
|
+
self,
|
|
118
|
+
url: str,
|
|
119
|
+
steps: List[str],
|
|
120
|
+
use_session: bool = True,
|
|
121
|
+
user_prompt: Optional[str] = None,
|
|
122
|
+
output_schema: Optional[dict] = None,
|
|
123
|
+
ai_extraction: bool = False,
|
|
124
|
+
) -> str:
|
|
125
|
+
"""Perform agentic crawling with automated browser actions and optional AI extraction.
|
|
126
|
+
|
|
127
|
+
This tool can:
|
|
128
|
+
1. Navigate to a website
|
|
129
|
+
2. Perform a series of automated actions (like filling forms, clicking buttons)
|
|
130
|
+
3. Extract the resulting HTML content as markdown
|
|
131
|
+
4. Optionally use AI to extract structured data
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
url (str): The URL to scrape
|
|
135
|
+
steps (List[str]): List of steps to perform on the webpage (e.g., ["Type email in input box", "click login"])
|
|
136
|
+
use_session (bool): Whether to use session for the scraping (default: True)
|
|
137
|
+
user_prompt (Optional[str]): Prompt for AI extraction (only used when ai_extraction=True)
|
|
138
|
+
output_schema (Optional[dict]): Schema for structured data extraction (only used when ai_extraction=True)
|
|
139
|
+
ai_extraction (bool): Whether to use AI for data extraction from the scraped content (default: False)
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
JSON string containing the scraping results, including request_id, status, and extracted data
|
|
143
|
+
"""
|
|
144
|
+
try:
|
|
145
|
+
# Validate required parameters for AI extraction
|
|
146
|
+
if ai_extraction and not user_prompt:
|
|
147
|
+
return json.dumps({"error": "user_prompt is required when ai_extraction=True"})
|
|
148
|
+
|
|
149
|
+
# Validate URL format
|
|
150
|
+
if not url.strip():
|
|
151
|
+
return json.dumps({"error": "URL cannot be empty"})
|
|
152
|
+
if not (url.startswith("http://") or url.startswith("https://")):
|
|
153
|
+
return json.dumps({"error": "Invalid URL - must start with http:// or https://"})
|
|
154
|
+
|
|
155
|
+
# Validate steps
|
|
156
|
+
if not steps:
|
|
157
|
+
return json.dumps({"error": "Steps cannot be empty"})
|
|
158
|
+
if any(not step.strip() for step in steps):
|
|
159
|
+
return json.dumps({"error": "All steps must contain valid instructions"})
|
|
160
|
+
|
|
161
|
+
# Prepare parameters for the API call
|
|
162
|
+
params = {"url": url, "steps": steps, "use_session": use_session, "ai_extraction": ai_extraction}
|
|
163
|
+
|
|
164
|
+
# Add optional parameters only if they are provided
|
|
165
|
+
if user_prompt:
|
|
166
|
+
params["user_prompt"] = user_prompt
|
|
167
|
+
if output_schema:
|
|
168
|
+
params["output_schema"] = output_schema
|
|
169
|
+
|
|
170
|
+
# Call the agentic scraper API
|
|
171
|
+
response = self.client.agenticscraper(**params)
|
|
172
|
+
|
|
173
|
+
return json.dumps(response, indent=2)
|
|
174
|
+
|
|
175
|
+
except Exception as e:
|
|
176
|
+
return json.dumps({"error": str(e)})
|
|
177
|
+
|
|
113
178
|
def searchscraper(self, prompt: str) -> str:
|
|
114
179
|
"""Search the web and extract information from the web.
|
|
115
180
|
Args:
|
|
@@ -310,26 +310,7 @@ class PgVector(VectorDb):
|
|
|
310
310
|
batch_records = []
|
|
311
311
|
for doc in batch_docs:
|
|
312
312
|
try:
|
|
313
|
-
|
|
314
|
-
cleaned_content = self._clean_content(doc.content)
|
|
315
|
-
content_hash = safe_content_hash(doc.content)
|
|
316
|
-
_id = doc.id or content_hash
|
|
317
|
-
|
|
318
|
-
meta_data = doc.meta_data or {}
|
|
319
|
-
if filters:
|
|
320
|
-
meta_data.update(filters)
|
|
321
|
-
|
|
322
|
-
record = {
|
|
323
|
-
"id": _id,
|
|
324
|
-
"name": doc.name,
|
|
325
|
-
"meta_data": doc.meta_data,
|
|
326
|
-
"filters": filters,
|
|
327
|
-
"content": cleaned_content,
|
|
328
|
-
"embedding": doc.embedding,
|
|
329
|
-
"usage": doc.usage,
|
|
330
|
-
"content_hash": content_hash,
|
|
331
|
-
}
|
|
332
|
-
batch_records.append(record)
|
|
313
|
+
batch_records.append(self._get_document_record(doc, filters))
|
|
333
314
|
except Exception as e:
|
|
334
315
|
logger.error(f"Error processing document '{doc.name}': {e}")
|
|
335
316
|
|
|
@@ -383,25 +364,7 @@ class PgVector(VectorDb):
|
|
|
383
364
|
batch_records = []
|
|
384
365
|
for doc in batch_docs:
|
|
385
366
|
try:
|
|
386
|
-
|
|
387
|
-
cleaned_content = self._clean_content(doc.content)
|
|
388
|
-
content_hash = safe_content_hash(doc.content)
|
|
389
|
-
|
|
390
|
-
meta_data = doc.meta_data or {}
|
|
391
|
-
if filters:
|
|
392
|
-
meta_data.update(filters)
|
|
393
|
-
|
|
394
|
-
record = {
|
|
395
|
-
"id": content_hash, # use content_hash as a reproducible id to avoid duplicates while upsert
|
|
396
|
-
"name": doc.name,
|
|
397
|
-
"meta_data": doc.meta_data,
|
|
398
|
-
"filters": filters,
|
|
399
|
-
"content": cleaned_content,
|
|
400
|
-
"embedding": doc.embedding,
|
|
401
|
-
"usage": doc.usage,
|
|
402
|
-
"content_hash": content_hash,
|
|
403
|
-
}
|
|
404
|
-
batch_records.append(record)
|
|
367
|
+
batch_records.append(self._get_document_record(doc, filters))
|
|
405
368
|
except Exception as e:
|
|
406
369
|
logger.error(f"Error processing document '{doc.name}': {e}")
|
|
407
370
|
|
|
@@ -430,6 +393,27 @@ class PgVector(VectorDb):
|
|
|
430
393
|
logger.error(f"Error upserting documents: {e}")
|
|
431
394
|
raise
|
|
432
395
|
|
|
396
|
+
def _get_document_record(self, doc: Document, filters: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
397
|
+
doc.embed(embedder=self.embedder)
|
|
398
|
+
cleaned_content = self._clean_content(doc.content)
|
|
399
|
+
content_hash = safe_content_hash(doc.content)
|
|
400
|
+
_id = doc.id or content_hash
|
|
401
|
+
|
|
402
|
+
meta_data = doc.meta_data or {}
|
|
403
|
+
if filters:
|
|
404
|
+
meta_data.update(filters)
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
"id": _id,
|
|
408
|
+
"name": doc.name,
|
|
409
|
+
"meta_data": meta_data,
|
|
410
|
+
"filters": filters,
|
|
411
|
+
"content": cleaned_content,
|
|
412
|
+
"embedding": doc.embedding,
|
|
413
|
+
"usage": doc.usage,
|
|
414
|
+
"content_hash": content_hash,
|
|
415
|
+
}
|
|
416
|
+
|
|
433
417
|
async def async_upsert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
|
|
434
418
|
"""Upsert documents asynchronously by running in a thread."""
|
|
435
419
|
await asyncio.to_thread(self.upsert, documents, filters)
|
agno/workflow/v2/step.py
CHANGED
|
@@ -239,6 +239,7 @@ class Step:
|
|
|
239
239
|
images=images,
|
|
240
240
|
videos=videos,
|
|
241
241
|
audio=audios,
|
|
242
|
+
files=step_input.files,
|
|
242
243
|
session_id=session_id,
|
|
243
244
|
user_id=user_id,
|
|
244
245
|
)
|
|
@@ -363,6 +364,7 @@ class Step:
|
|
|
363
364
|
images=images,
|
|
364
365
|
videos=videos,
|
|
365
366
|
audio=audios,
|
|
367
|
+
files=step_input.files,
|
|
366
368
|
session_id=session_id,
|
|
367
369
|
user_id=user_id,
|
|
368
370
|
stream=True,
|
|
@@ -514,6 +516,7 @@ class Step:
|
|
|
514
516
|
images=images,
|
|
515
517
|
videos=videos,
|
|
516
518
|
audio=audios,
|
|
519
|
+
files=step_input.files,
|
|
517
520
|
session_id=session_id,
|
|
518
521
|
user_id=user_id,
|
|
519
522
|
)
|
|
@@ -656,6 +659,7 @@ class Step:
|
|
|
656
659
|
images=images,
|
|
657
660
|
videos=videos,
|
|
658
661
|
audio=audios,
|
|
662
|
+
files=step_input.files,
|
|
659
663
|
session_id=session_id,
|
|
660
664
|
user_id=user_id,
|
|
661
665
|
stream=True,
|
agno/workflow/v2/types.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import Any, Dict, List, Optional, Union
|
|
|
3
3
|
|
|
4
4
|
from pydantic import BaseModel
|
|
5
5
|
|
|
6
|
-
from agno.media import AudioArtifact, ImageArtifact, VideoArtifact
|
|
6
|
+
from agno.media import AudioArtifact, File, ImageArtifact, VideoArtifact
|
|
7
7
|
from agno.run.response import RunResponse
|
|
8
8
|
from agno.run.team import TeamRunResponse
|
|
9
9
|
|
|
@@ -20,6 +20,7 @@ class WorkflowExecutionInput:
|
|
|
20
20
|
images: Optional[List[ImageArtifact]] = None
|
|
21
21
|
videos: Optional[List[VideoArtifact]] = None
|
|
22
22
|
audio: Optional[List[AudioArtifact]] = None
|
|
23
|
+
files: Optional[List[File]] = None
|
|
23
24
|
|
|
24
25
|
def get_message_as_string(self) -> Optional[str]:
|
|
25
26
|
"""Convert message to string representation"""
|
|
@@ -72,6 +73,7 @@ class StepInput:
|
|
|
72
73
|
images: Optional[List[ImageArtifact]] = None
|
|
73
74
|
videos: Optional[List[VideoArtifact]] = None
|
|
74
75
|
audio: Optional[List[AudioArtifact]] = None
|
|
76
|
+
files: Optional[List[File]] = None
|
|
75
77
|
|
|
76
78
|
def get_message_as_string(self) -> Optional[str]:
|
|
77
79
|
"""Convert message to string representation"""
|
|
@@ -173,6 +175,7 @@ class StepInput:
|
|
|
173
175
|
"images": [img.to_dict() for img in self.images] if self.images else None,
|
|
174
176
|
"videos": [vid.to_dict() for vid in self.videos] if self.videos else None,
|
|
175
177
|
"audio": [aud.to_dict() for aud in self.audio] if self.audio else None,
|
|
178
|
+
"files": [file for file in self.files] if self.files else None,
|
|
176
179
|
}
|
|
177
180
|
|
|
178
181
|
|
|
@@ -198,6 +201,7 @@ class StepOutput:
|
|
|
198
201
|
images: Optional[List[ImageArtifact]] = None
|
|
199
202
|
videos: Optional[List[VideoArtifact]] = None
|
|
200
203
|
audio: Optional[List[AudioArtifact]] = None
|
|
204
|
+
files: Optional[List[File]] = None
|
|
201
205
|
|
|
202
206
|
# Metrics for this step execution
|
|
203
207
|
metrics: Optional[Dict[str, Any]] = None
|
|
@@ -229,6 +233,7 @@ class StepOutput:
|
|
|
229
233
|
"success": self.success,
|
|
230
234
|
"error": self.error,
|
|
231
235
|
"stop": self.stop,
|
|
236
|
+
"files": [file for file in self.files] if self.files else None,
|
|
232
237
|
}
|
|
233
238
|
|
|
234
239
|
@classmethod
|
|
@@ -260,6 +265,10 @@ class StepOutput:
|
|
|
260
265
|
if audio:
|
|
261
266
|
audio = [AudioArtifact.model_validate(aud) for aud in audio]
|
|
262
267
|
|
|
268
|
+
files = data.get("files")
|
|
269
|
+
if files:
|
|
270
|
+
files = [File.model_validate(file) for file in files]
|
|
271
|
+
|
|
263
272
|
return cls(
|
|
264
273
|
content=data.get("content"),
|
|
265
274
|
response=response,
|
|
@@ -270,6 +279,7 @@ class StepOutput:
|
|
|
270
279
|
success=data.get("success", True),
|
|
271
280
|
error=data.get("error"),
|
|
272
281
|
stop=data.get("stop", False),
|
|
282
|
+
files=files,
|
|
273
283
|
)
|
|
274
284
|
|
|
275
285
|
|
agno/workflow/v2/workflow.py
CHANGED
|
@@ -20,7 +20,7 @@ from uuid import uuid4
|
|
|
20
20
|
from pydantic import BaseModel
|
|
21
21
|
|
|
22
22
|
from agno.agent.agent import Agent
|
|
23
|
-
from agno.media import Audio, AudioArtifact, Image, ImageArtifact, Video, VideoArtifact
|
|
23
|
+
from agno.media import Audio, AudioArtifact, File, Image, ImageArtifact, Video, VideoArtifact
|
|
24
24
|
from agno.run.base import RunStatus
|
|
25
25
|
from agno.run.v2.workflow import (
|
|
26
26
|
ConditionExecutionCompletedEvent,
|
|
@@ -326,6 +326,7 @@ class Workflow:
|
|
|
326
326
|
shared_images: Optional[List[ImageArtifact]] = None,
|
|
327
327
|
shared_videos: Optional[List[VideoArtifact]] = None,
|
|
328
328
|
shared_audio: Optional[List[AudioArtifact]] = None,
|
|
329
|
+
shared_files: Optional[List[File]] = None,
|
|
329
330
|
) -> StepInput:
|
|
330
331
|
"""Helper method to create StepInput with enhanced data flow support"""
|
|
331
332
|
|
|
@@ -343,6 +344,7 @@ class Workflow:
|
|
|
343
344
|
images=shared_images or [],
|
|
344
345
|
videos=shared_videos or [],
|
|
345
346
|
audio=shared_audio or [],
|
|
347
|
+
files=shared_files or [],
|
|
346
348
|
)
|
|
347
349
|
|
|
348
350
|
def _get_step_count(self) -> int:
|
|
@@ -473,6 +475,8 @@ class Workflow:
|
|
|
473
475
|
output_videos: List[VideoArtifact] = (execution_input.videos or []).copy() # Start with input videos
|
|
474
476
|
shared_audio: List[AudioArtifact] = execution_input.audio or []
|
|
475
477
|
output_audio: List[AudioArtifact] = (execution_input.audio or []).copy() # Start with input audio
|
|
478
|
+
shared_files: List[File] = execution_input.files or []
|
|
479
|
+
output_files: List[File] = (execution_input.files or []).copy() # Start with input files
|
|
476
480
|
|
|
477
481
|
for i, step in enumerate(self.steps): # type: ignore[arg-type]
|
|
478
482
|
step_name = getattr(step, "name", f"step_{i + 1}")
|
|
@@ -485,6 +489,7 @@ class Workflow:
|
|
|
485
489
|
shared_images=shared_images,
|
|
486
490
|
shared_videos=shared_videos,
|
|
487
491
|
shared_audio=shared_audio,
|
|
492
|
+
shared_files=shared_files,
|
|
488
493
|
)
|
|
489
494
|
|
|
490
495
|
step_output = step.execute(step_input, session_id=self.session_id, user_id=self.user_id) # type: ignore[union-attr]
|
|
@@ -511,16 +516,20 @@ class Workflow:
|
|
|
511
516
|
shared_images.extend(output.images or [])
|
|
512
517
|
shared_videos.extend(output.videos or [])
|
|
513
518
|
shared_audio.extend(output.audio or [])
|
|
519
|
+
shared_files.extend(output.files or [])
|
|
514
520
|
output_images.extend(output.images or [])
|
|
515
521
|
output_videos.extend(output.videos or [])
|
|
516
522
|
output_audio.extend(output.audio or [])
|
|
523
|
+
output_files.extend(output.files or [])
|
|
517
524
|
else:
|
|
518
525
|
shared_images.extend(step_output.images or [])
|
|
519
526
|
shared_videos.extend(step_output.videos or [])
|
|
520
527
|
shared_audio.extend(step_output.audio or [])
|
|
528
|
+
shared_files.extend(step_output.files or [])
|
|
521
529
|
output_images.extend(step_output.images or [])
|
|
522
530
|
output_videos.extend(step_output.videos or [])
|
|
523
531
|
output_audio.extend(step_output.audio or [])
|
|
532
|
+
output_files.extend(step_output.files or [])
|
|
524
533
|
|
|
525
534
|
collected_step_outputs.append(step_output)
|
|
526
535
|
|
|
@@ -608,6 +617,8 @@ class Workflow:
|
|
|
608
617
|
output_videos: List[VideoArtifact] = (execution_input.videos or []).copy() # Start with input videos
|
|
609
618
|
shared_audio: List[AudioArtifact] = execution_input.audio or []
|
|
610
619
|
output_audio: List[AudioArtifact] = (execution_input.audio or []).copy() # Start with input audio
|
|
620
|
+
shared_files: List[File] = execution_input.files or []
|
|
621
|
+
output_files: List[File] = (execution_input.files or []).copy() # Start with input files
|
|
611
622
|
|
|
612
623
|
early_termination = False
|
|
613
624
|
|
|
@@ -622,6 +633,7 @@ class Workflow:
|
|
|
622
633
|
shared_images=shared_images,
|
|
623
634
|
shared_videos=shared_videos,
|
|
624
635
|
shared_audio=shared_audio,
|
|
636
|
+
shared_files=shared_files,
|
|
625
637
|
)
|
|
626
638
|
|
|
627
639
|
# Execute step with streaming and yield all events
|
|
@@ -652,9 +664,11 @@ class Workflow:
|
|
|
652
664
|
shared_images.extend(step_output.images or [])
|
|
653
665
|
shared_videos.extend(step_output.videos or [])
|
|
654
666
|
shared_audio.extend(step_output.audio or [])
|
|
667
|
+
shared_files.extend(step_output.files or [])
|
|
655
668
|
output_images.extend(step_output.images or [])
|
|
656
669
|
output_videos.extend(step_output.videos or [])
|
|
657
670
|
output_audio.extend(step_output.audio or [])
|
|
671
|
+
output_files.extend(step_output.files or [])
|
|
658
672
|
|
|
659
673
|
# Only yield StepOutputEvent for function executors, not for agents/teams
|
|
660
674
|
if getattr(step, "executor_type", None) == "function":
|
|
@@ -668,9 +682,11 @@ class Workflow:
|
|
|
668
682
|
shared_images.extend(step_output.images or [])
|
|
669
683
|
shared_videos.extend(step_output.videos or [])
|
|
670
684
|
shared_audio.extend(step_output.audio or [])
|
|
685
|
+
shared_files.extend(step_output.files or [])
|
|
671
686
|
output_images.extend(step_output.images or [])
|
|
672
687
|
output_videos.extend(step_output.videos or [])
|
|
673
688
|
output_audio.extend(step_output.audio or [])
|
|
689
|
+
output_files.extend(step_output.files or [])
|
|
674
690
|
|
|
675
691
|
# Only yield StepOutputEvent for generator functions, not for agents/teams
|
|
676
692
|
if getattr(step, "executor_type", None) == "function":
|
|
@@ -836,6 +852,8 @@ class Workflow:
|
|
|
836
852
|
output_videos: List[VideoArtifact] = (execution_input.videos or []).copy() # Start with input videos
|
|
837
853
|
shared_audio: List[AudioArtifact] = execution_input.audio or []
|
|
838
854
|
output_audio: List[AudioArtifact] = (execution_input.audio or []).copy() # Start with input audio
|
|
855
|
+
shared_files: List[File] = execution_input.files or []
|
|
856
|
+
output_files: List[File] = (execution_input.files or []).copy() # Start with input files
|
|
839
857
|
|
|
840
858
|
for i, step in enumerate(self.steps): # type: ignore[arg-type]
|
|
841
859
|
step_name = getattr(step, "name", f"step_{i + 1}")
|
|
@@ -848,6 +866,7 @@ class Workflow:
|
|
|
848
866
|
shared_images=shared_images,
|
|
849
867
|
shared_videos=shared_videos,
|
|
850
868
|
shared_audio=shared_audio,
|
|
869
|
+
shared_files=shared_files,
|
|
851
870
|
)
|
|
852
871
|
|
|
853
872
|
step_output = await step.aexecute(step_input, session_id=self.session_id, user_id=self.user_id) # type: ignore[union-attr]
|
|
@@ -873,16 +892,20 @@ class Workflow:
|
|
|
873
892
|
shared_images.extend(output.images or [])
|
|
874
893
|
shared_videos.extend(output.videos or [])
|
|
875
894
|
shared_audio.extend(output.audio or [])
|
|
895
|
+
shared_files.extend(output.files or [])
|
|
876
896
|
output_images.extend(output.images or [])
|
|
877
897
|
output_videos.extend(output.videos or [])
|
|
878
898
|
output_audio.extend(output.audio or [])
|
|
899
|
+
output_files.extend(output.files or [])
|
|
879
900
|
else:
|
|
880
901
|
shared_images.extend(step_output.images or [])
|
|
881
902
|
shared_videos.extend(step_output.videos or [])
|
|
882
903
|
shared_audio.extend(step_output.audio or [])
|
|
904
|
+
shared_files.extend(step_output.files or [])
|
|
883
905
|
output_images.extend(step_output.images or [])
|
|
884
906
|
output_videos.extend(step_output.videos or [])
|
|
885
907
|
output_audio.extend(step_output.audio or [])
|
|
908
|
+
output_files.extend(step_output.files or [])
|
|
886
909
|
|
|
887
910
|
collected_step_outputs.append(step_output)
|
|
888
911
|
|
|
@@ -976,6 +999,8 @@ class Workflow:
|
|
|
976
999
|
output_videos: List[VideoArtifact] = (execution_input.videos or []).copy() # Start with input videos
|
|
977
1000
|
shared_audio: List[AudioArtifact] = execution_input.audio or []
|
|
978
1001
|
output_audio: List[AudioArtifact] = (execution_input.audio or []).copy() # Start with input audio
|
|
1002
|
+
shared_files: List[File] = execution_input.files or []
|
|
1003
|
+
output_files: List[File] = (execution_input.files or []).copy() # Start with input files
|
|
979
1004
|
|
|
980
1005
|
early_termination = False
|
|
981
1006
|
|
|
@@ -990,6 +1015,7 @@ class Workflow:
|
|
|
990
1015
|
shared_images=shared_images,
|
|
991
1016
|
shared_videos=shared_videos,
|
|
992
1017
|
shared_audio=shared_audio,
|
|
1018
|
+
shared_files=shared_files,
|
|
993
1019
|
)
|
|
994
1020
|
|
|
995
1021
|
# Execute step with streaming and yield all events
|
|
@@ -1019,9 +1045,11 @@ class Workflow:
|
|
|
1019
1045
|
shared_images.extend(step_output.images or [])
|
|
1020
1046
|
shared_videos.extend(step_output.videos or [])
|
|
1021
1047
|
shared_audio.extend(step_output.audio or [])
|
|
1048
|
+
shared_files.extend(step_output.files or [])
|
|
1022
1049
|
output_images.extend(step_output.images or [])
|
|
1023
1050
|
output_videos.extend(step_output.videos or [])
|
|
1024
1051
|
output_audio.extend(step_output.audio or [])
|
|
1052
|
+
output_files.extend(step_output.files or [])
|
|
1025
1053
|
|
|
1026
1054
|
if getattr(step, "executor_type", None) == "function":
|
|
1027
1055
|
yield step_output_event
|
|
@@ -1034,9 +1062,11 @@ class Workflow:
|
|
|
1034
1062
|
shared_images.extend(step_output.images or [])
|
|
1035
1063
|
shared_videos.extend(step_output.videos or [])
|
|
1036
1064
|
shared_audio.extend(step_output.audio or [])
|
|
1065
|
+
shared_files.extend(step_output.files or [])
|
|
1037
1066
|
output_images.extend(step_output.images or [])
|
|
1038
1067
|
output_videos.extend(step_output.videos or [])
|
|
1039
1068
|
output_audio.extend(step_output.audio or [])
|
|
1069
|
+
output_files.extend(step_output.files or [])
|
|
1040
1070
|
|
|
1041
1071
|
# Only yield StepOutputEvent for generator functions, not for agents/teams
|
|
1042
1072
|
if getattr(step, "executor_type", None) == "function":
|
|
@@ -1225,6 +1255,7 @@ class Workflow:
|
|
|
1225
1255
|
audio: Optional[List[Audio]] = None,
|
|
1226
1256
|
images: Optional[List[Image]] = None,
|
|
1227
1257
|
videos: Optional[List[Video]] = None,
|
|
1258
|
+
files: Optional[List[File]] = None,
|
|
1228
1259
|
stream: Literal[False] = False,
|
|
1229
1260
|
stream_intermediate_steps: Optional[bool] = None,
|
|
1230
1261
|
background: Optional[bool] = False,
|
|
@@ -1240,6 +1271,7 @@ class Workflow:
|
|
|
1240
1271
|
audio: Optional[List[Audio]] = None,
|
|
1241
1272
|
images: Optional[List[Image]] = None,
|
|
1242
1273
|
videos: Optional[List[Video]] = None,
|
|
1274
|
+
files: Optional[List[File]] = None,
|
|
1243
1275
|
stream: Literal[True] = True,
|
|
1244
1276
|
stream_intermediate_steps: Optional[bool] = None,
|
|
1245
1277
|
background: Optional[bool] = False,
|
|
@@ -1254,6 +1286,7 @@ class Workflow:
|
|
|
1254
1286
|
audio: Optional[List[Audio]] = None,
|
|
1255
1287
|
images: Optional[List[Image]] = None,
|
|
1256
1288
|
videos: Optional[List[Video]] = None,
|
|
1289
|
+
files: Optional[List[File]] = None,
|
|
1257
1290
|
stream: bool = False,
|
|
1258
1291
|
stream_intermediate_steps: Optional[bool] = None,
|
|
1259
1292
|
background: Optional[bool] = False,
|
|
@@ -1315,6 +1348,7 @@ class Workflow:
|
|
|
1315
1348
|
audio=audio, # type: ignore
|
|
1316
1349
|
images=images, # type: ignore
|
|
1317
1350
|
videos=videos, # type: ignore
|
|
1351
|
+
files=files, # type: ignore
|
|
1318
1352
|
)
|
|
1319
1353
|
log_debug(
|
|
1320
1354
|
f"Created pipeline input with session state keys: {list(self.workflow_session_state.keys()) if self.workflow_session_state else 'None'}"
|
|
@@ -1342,6 +1376,7 @@ class Workflow:
|
|
|
1342
1376
|
audio: Optional[List[Audio]] = None,
|
|
1343
1377
|
images: Optional[List[Image]] = None,
|
|
1344
1378
|
videos: Optional[List[Video]] = None,
|
|
1379
|
+
files: Optional[List[File]] = None,
|
|
1345
1380
|
stream: Literal[False] = False,
|
|
1346
1381
|
stream_intermediate_steps: Optional[bool] = None,
|
|
1347
1382
|
background: Optional[bool] = False,
|
|
@@ -1357,6 +1392,7 @@ class Workflow:
|
|
|
1357
1392
|
audio: Optional[List[Audio]] = None,
|
|
1358
1393
|
images: Optional[List[Image]] = None,
|
|
1359
1394
|
videos: Optional[List[Video]] = None,
|
|
1395
|
+
files: Optional[List[File]] = None,
|
|
1360
1396
|
stream: Literal[True] = True,
|
|
1361
1397
|
stream_intermediate_steps: Optional[bool] = None,
|
|
1362
1398
|
background: Optional[bool] = False,
|
|
@@ -1371,6 +1407,7 @@ class Workflow:
|
|
|
1371
1407
|
audio: Optional[List[Audio]] = None,
|
|
1372
1408
|
images: Optional[List[Image]] = None,
|
|
1373
1409
|
videos: Optional[List[Video]] = None,
|
|
1410
|
+
files: Optional[List[File]] = None,
|
|
1374
1411
|
stream: bool = False,
|
|
1375
1412
|
stream_intermediate_steps: Optional[bool] = False,
|
|
1376
1413
|
background: Optional[bool] = False,
|
|
@@ -1440,6 +1477,7 @@ class Workflow:
|
|
|
1440
1477
|
audio=audio, # type: ignore
|
|
1441
1478
|
images=images, # type: ignore
|
|
1442
1479
|
videos=videos, # type: ignore
|
|
1480
|
+
files=files, # type: ignore
|
|
1443
1481
|
)
|
|
1444
1482
|
log_debug(
|
|
1445
1483
|
f"Created async pipeline input with session state keys: {list(self.workflow_session_state.keys()) if self.workflow_session_state else 'None'}"
|
|
@@ -1629,6 +1667,7 @@ class Workflow:
|
|
|
1629
1667
|
audio: Optional[List[Audio]] = None,
|
|
1630
1668
|
images: Optional[List[Image]] = None,
|
|
1631
1669
|
videos: Optional[List[Video]] = None,
|
|
1670
|
+
files: Optional[List[File]] = None,
|
|
1632
1671
|
stream: bool = False,
|
|
1633
1672
|
stream_intermediate_steps: bool = False,
|
|
1634
1673
|
markdown: bool = False,
|
|
@@ -1647,6 +1686,7 @@ class Workflow:
|
|
|
1647
1686
|
audio: Audio input
|
|
1648
1687
|
images: Image input
|
|
1649
1688
|
videos: Video input
|
|
1689
|
+
files: File input
|
|
1650
1690
|
stream: Whether to stream the response content
|
|
1651
1691
|
stream_intermediate_steps: Whether to stream intermediate steps
|
|
1652
1692
|
markdown: Whether to render content as markdown
|
|
@@ -1667,6 +1707,7 @@ class Workflow:
|
|
|
1667
1707
|
audio=audio,
|
|
1668
1708
|
images=images,
|
|
1669
1709
|
videos=videos,
|
|
1710
|
+
files=files,
|
|
1670
1711
|
stream_intermediate_steps=stream_intermediate_steps,
|
|
1671
1712
|
markdown=markdown,
|
|
1672
1713
|
show_time=show_time,
|
|
@@ -1683,6 +1724,7 @@ class Workflow:
|
|
|
1683
1724
|
audio=audio,
|
|
1684
1725
|
images=images,
|
|
1685
1726
|
videos=videos,
|
|
1727
|
+
files=files,
|
|
1686
1728
|
markdown=markdown,
|
|
1687
1729
|
show_time=show_time,
|
|
1688
1730
|
show_step_details=show_step_details,
|
|
@@ -1699,6 +1741,7 @@ class Workflow:
|
|
|
1699
1741
|
audio: Optional[List[Audio]] = None,
|
|
1700
1742
|
images: Optional[List[Image]] = None,
|
|
1701
1743
|
videos: Optional[List[Video]] = None,
|
|
1744
|
+
files: Optional[List[File]] = None,
|
|
1702
1745
|
markdown: bool = False,
|
|
1703
1746
|
show_time: bool = True,
|
|
1704
1747
|
show_step_details: bool = True,
|
|
@@ -1775,6 +1818,7 @@ class Workflow:
|
|
|
1775
1818
|
audio=audio,
|
|
1776
1819
|
images=images,
|
|
1777
1820
|
videos=videos,
|
|
1821
|
+
files=files,
|
|
1778
1822
|
**kwargs,
|
|
1779
1823
|
) # type: ignore
|
|
1780
1824
|
|
|
@@ -1855,6 +1899,7 @@ class Workflow:
|
|
|
1855
1899
|
audio: Optional[List[Audio]] = None,
|
|
1856
1900
|
images: Optional[List[Image]] = None,
|
|
1857
1901
|
videos: Optional[List[Video]] = None,
|
|
1902
|
+
files: Optional[List[File]] = None,
|
|
1858
1903
|
stream_intermediate_steps: bool = False,
|
|
1859
1904
|
markdown: bool = False,
|
|
1860
1905
|
show_time: bool = True,
|
|
@@ -1984,6 +2029,7 @@ class Workflow:
|
|
|
1984
2029
|
audio=audio,
|
|
1985
2030
|
images=images,
|
|
1986
2031
|
videos=videos,
|
|
2032
|
+
files=files,
|
|
1987
2033
|
stream=True,
|
|
1988
2034
|
stream_intermediate_steps=stream_intermediate_steps,
|
|
1989
2035
|
**kwargs,
|
|
@@ -2405,6 +2451,7 @@ class Workflow:
|
|
|
2405
2451
|
audio: Optional[List[Audio]] = None,
|
|
2406
2452
|
images: Optional[List[Image]] = None,
|
|
2407
2453
|
videos: Optional[List[Video]] = None,
|
|
2454
|
+
files: Optional[List[File]] = None,
|
|
2408
2455
|
stream: bool = False,
|
|
2409
2456
|
stream_intermediate_steps: bool = False,
|
|
2410
2457
|
markdown: bool = False,
|
|
@@ -2439,6 +2486,7 @@ class Workflow:
|
|
|
2439
2486
|
audio=audio,
|
|
2440
2487
|
images=images,
|
|
2441
2488
|
videos=videos,
|
|
2489
|
+
files=files,
|
|
2442
2490
|
stream_intermediate_steps=stream_intermediate_steps,
|
|
2443
2491
|
markdown=markdown,
|
|
2444
2492
|
show_time=show_time,
|
|
@@ -2455,6 +2503,7 @@ class Workflow:
|
|
|
2455
2503
|
audio=audio,
|
|
2456
2504
|
images=images,
|
|
2457
2505
|
videos=videos,
|
|
2506
|
+
files=files,
|
|
2458
2507
|
markdown=markdown,
|
|
2459
2508
|
show_time=show_time,
|
|
2460
2509
|
show_step_details=show_step_details,
|
|
@@ -2471,6 +2520,7 @@ class Workflow:
|
|
|
2471
2520
|
audio: Optional[List[Audio]] = None,
|
|
2472
2521
|
images: Optional[List[Image]] = None,
|
|
2473
2522
|
videos: Optional[List[Video]] = None,
|
|
2523
|
+
files: Optional[List[File]] = None,
|
|
2474
2524
|
markdown: bool = False,
|
|
2475
2525
|
show_time: bool = True,
|
|
2476
2526
|
show_step_details: bool = True,
|
|
@@ -2547,6 +2597,7 @@ class Workflow:
|
|
|
2547
2597
|
audio=audio,
|
|
2548
2598
|
images=images,
|
|
2549
2599
|
videos=videos,
|
|
2600
|
+
files=files,
|
|
2550
2601
|
**kwargs,
|
|
2551
2602
|
) # type: ignore
|
|
2552
2603
|
|
|
@@ -2628,6 +2679,7 @@ class Workflow:
|
|
|
2628
2679
|
audio: Optional[List[Audio]] = None,
|
|
2629
2680
|
images: Optional[List[Image]] = None,
|
|
2630
2681
|
videos: Optional[List[Video]] = None,
|
|
2682
|
+
files: Optional[List[File]] = None,
|
|
2631
2683
|
stream_intermediate_steps: bool = False,
|
|
2632
2684
|
markdown: bool = False,
|
|
2633
2685
|
show_time: bool = True,
|
|
@@ -2757,6 +2809,7 @@ class Workflow:
|
|
|
2757
2809
|
audio=audio,
|
|
2758
2810
|
images=images,
|
|
2759
2811
|
videos=videos,
|
|
2812
|
+
files=files,
|
|
2760
2813
|
stream=True,
|
|
2761
2814
|
stream_intermediate_steps=stream_intermediate_steps,
|
|
2762
2815
|
**kwargs,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agno
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
4
4
|
Summary: Agno: a lightweight library for building Multi-Agent Systems
|
|
5
5
|
Author-email: Ashpreet Bedi <ashpreet@agno.com>
|
|
6
6
|
License: Copyright (c) Agno, Inc.
|
|
@@ -424,7 +424,7 @@ Requires-Dist: fastapi; extra == "dev"
|
|
|
424
424
|
Requires-Dist: uvicorn; extra == "dev"
|
|
425
425
|
Provides-Extra: integration-tests
|
|
426
426
|
Requires-Dist: exa_py; extra == "integration-tests"
|
|
427
|
-
Requires-Dist:
|
|
427
|
+
Requires-Dist: ddgs; extra == "integration-tests"
|
|
428
428
|
Requires-Dist: yfinance; extra == "integration-tests"
|
|
429
429
|
Requires-Dist: sqlalchemy; extra == "integration-tests"
|
|
430
430
|
Requires-Dist: Pillow; extra == "integration-tests"
|
|
@@ -493,7 +493,7 @@ Requires-Dist: cartesia; extra == "cartesia"
|
|
|
493
493
|
Provides-Extra: confluence
|
|
494
494
|
Requires-Dist: atlassian-python-api; extra == "confluence"
|
|
495
495
|
Provides-Extra: ddg
|
|
496
|
-
Requires-Dist:
|
|
496
|
+
Requires-Dist: ddgs; extra == "ddg"
|
|
497
497
|
Provides-Extra: duckdb
|
|
498
498
|
Requires-Dist: duckdb; extra == "duckdb"
|
|
499
499
|
Provides-Extra: elevenlabs
|
|
@@ -525,6 +525,8 @@ Provides-Extra: mcp
|
|
|
525
525
|
Requires-Dist: mcp; extra == "mcp"
|
|
526
526
|
Provides-Extra: mem0
|
|
527
527
|
Requires-Dist: mem0ai; extra == "mem0"
|
|
528
|
+
Provides-Extra: memori
|
|
529
|
+
Requires-Dist: memorisdk; extra == "memori"
|
|
528
530
|
Provides-Extra: newspaper
|
|
529
531
|
Requires-Dist: newspaper4k; extra == "newspaper"
|
|
530
532
|
Requires-Dist: lxml_html_clean; extra == "newspaper"
|
|
@@ -664,6 +666,7 @@ Requires-Dist: agno[confluence]; extra == "tools"
|
|
|
664
666
|
Requires-Dist: agno[oxylabs]; extra == "tools"
|
|
665
667
|
Requires-Dist: agno[zep]; extra == "tools"
|
|
666
668
|
Requires-Dist: agno[mem0]; extra == "tools"
|
|
669
|
+
Requires-Dist: agno[memori]; extra == "tools"
|
|
667
670
|
Requires-Dist: agno[google_bigquery]; extra == "tools"
|
|
668
671
|
Requires-Dist: agno[psycopg]; extra == "tools"
|
|
669
672
|
Requires-Dist: agno[trafilatura]; extra == "tools"
|
|
@@ -881,7 +884,7 @@ agent_team.print_response("What's the market outlook and financial performance o
|
|
|
881
884
|
Install dependencies and run the Agent team:
|
|
882
885
|
|
|
883
886
|
```shell
|
|
884
|
-
pip install
|
|
887
|
+
pip install ddgs yfinance
|
|
885
888
|
|
|
886
889
|
python agent_team.py
|
|
887
890
|
```
|