agno 1.7.2__py3-none-any.whl → 1.7.4__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 (63) hide show
  1. agno/agent/agent.py +264 -155
  2. agno/api/schemas/agent.py +1 -0
  3. agno/api/schemas/team.py +1 -0
  4. agno/app/base.py +0 -22
  5. agno/app/discord/client.py +134 -56
  6. agno/app/fastapi/app.py +0 -11
  7. agno/app/playground/app.py +3 -24
  8. agno/app/playground/async_router.py +97 -28
  9. agno/app/playground/operator.py +25 -19
  10. agno/app/playground/schemas.py +1 -0
  11. agno/app/playground/sync_router.py +93 -26
  12. agno/document/reader/gcs/__init__.py +0 -0
  13. agno/document/reader/gcs/pdf_reader.py +44 -0
  14. agno/embedder/langdb.py +9 -5
  15. agno/knowledge/document.py +199 -8
  16. agno/knowledge/gcs/__init__.py +0 -0
  17. agno/knowledge/gcs/base.py +39 -0
  18. agno/knowledge/gcs/pdf.py +21 -0
  19. agno/models/langdb/langdb.py +8 -5
  20. agno/run/base.py +2 -0
  21. agno/run/response.py +4 -4
  22. agno/run/team.py +6 -6
  23. agno/run/v2/__init__.py +0 -0
  24. agno/run/v2/workflow.py +563 -0
  25. agno/storage/base.py +4 -4
  26. agno/storage/dynamodb.py +74 -10
  27. agno/storage/firestore.py +6 -1
  28. agno/storage/gcs_json.py +8 -2
  29. agno/storage/json.py +20 -5
  30. agno/storage/mongodb.py +14 -5
  31. agno/storage/mysql.py +56 -17
  32. agno/storage/postgres.py +55 -13
  33. agno/storage/redis.py +25 -5
  34. agno/storage/session/__init__.py +3 -1
  35. agno/storage/session/agent.py +3 -0
  36. agno/storage/session/team.py +3 -0
  37. agno/storage/session/v2/__init__.py +5 -0
  38. agno/storage/session/v2/workflow.py +89 -0
  39. agno/storage/singlestore.py +74 -12
  40. agno/storage/sqlite.py +64 -18
  41. agno/storage/yaml.py +26 -6
  42. agno/team/team.py +198 -243
  43. agno/tools/scrapegraph.py +8 -10
  44. agno/utils/log.py +12 -0
  45. agno/utils/message.py +5 -1
  46. agno/utils/openai.py +20 -5
  47. agno/utils/pprint.py +32 -8
  48. agno/workflow/v2/__init__.py +21 -0
  49. agno/workflow/v2/condition.py +554 -0
  50. agno/workflow/v2/loop.py +602 -0
  51. agno/workflow/v2/parallel.py +659 -0
  52. agno/workflow/v2/router.py +521 -0
  53. agno/workflow/v2/step.py +861 -0
  54. agno/workflow/v2/steps.py +465 -0
  55. agno/workflow/v2/types.py +347 -0
  56. agno/workflow/v2/workflow.py +3134 -0
  57. agno/workflow/workflow.py +15 -147
  58. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/METADATA +1 -1
  59. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/RECORD +63 -45
  60. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/WHEEL +0 -0
  61. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/entry_points.txt +0 -0
  62. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/licenses/LICENSE +0 -0
  63. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/top_level.txt +0 -0
agno/tools/scrapegraph.py CHANGED
@@ -45,7 +45,7 @@ class ScrapeGraphTools(Toolkit):
45
45
  super().__init__(name="scrapegraph_tools", tools=tools, **kwargs)
46
46
 
47
47
  def smartscraper(self, url: str, prompt: str) -> str:
48
- """Use this function to extract structured data from a webpage using LLM.
48
+ """Extract structured data from a webpage using LLM.
49
49
  Args:
50
50
  url (str): The URL to scrape
51
51
  prompt (str): Natural language prompt describing what to extract
@@ -59,7 +59,7 @@ class ScrapeGraphTools(Toolkit):
59
59
  return json.dumps({"error": str(e)})
60
60
 
61
61
  def markdownify(self, url: str) -> str:
62
- """Use this function to convert a webpage to markdown format.
62
+ """Convert a webpage to markdown format.
63
63
  Args:
64
64
  url (str): The URL to convert
65
65
  Returns:
@@ -82,7 +82,7 @@ class ScrapeGraphTools(Toolkit):
82
82
  same_domain_only: bool = True,
83
83
  batch_size: int = 1,
84
84
  ) -> str:
85
- """Use this function to crawl a website and extract structured data using a schema.
85
+ """Crawl a website and extract structured data
86
86
  Args:
87
87
  url (str): The URL to crawl
88
88
  prompt (str): Natural language prompt describing what to extract
@@ -110,17 +110,15 @@ class ScrapeGraphTools(Toolkit):
110
110
  except Exception as e:
111
111
  return json.dumps({"error": str(e)})
112
112
 
113
- def searchscraper(self, url: str, prompt: str) -> str:
114
- """Use this function to search and extract information from a webpage using LLM.
113
+ def searchscraper(self, prompt: str) -> str:
114
+ """Search the web and extract information from the web.
115
115
  Args:
116
- url (str): The URL to search
117
- prompt (str): Natural language prompt describing what to search for
116
+ prompt (str): Search query
118
117
  Returns:
119
- The search results extracted from the webpage
118
+ JSON of the search results
120
119
  """
121
120
  try:
122
- response = self.client.searchscraper(website_url=url, user_prompt=prompt)
123
- # If response has a 'result' attribute, return it, else return the whole response
121
+ response = self.client.searchscraper(user_prompt=prompt)
124
122
  if hasattr(response, "result"):
125
123
  return json.dumps(response.result)
126
124
  elif isinstance(response, dict) and "result" in response:
agno/utils/log.py CHANGED
@@ -7,6 +7,7 @@ from rich.text import Text
7
7
 
8
8
  LOGGER_NAME = "agno"
9
9
  TEAM_LOGGER_NAME = f"{LOGGER_NAME}-team"
10
+ WORKFLOW_LOGGER_NAME = f"{LOGGER_NAME}-workflow"
10
11
 
11
12
  # Define custom styles for different log sources
12
13
  LOG_STYLES = {
@@ -18,6 +19,10 @@ LOG_STYLES = {
18
19
  "debug": "magenta",
19
20
  "info": "steel_blue1",
20
21
  },
22
+ "workflow": {
23
+ "debug": "sandy_brown",
24
+ "info": "orange3",
25
+ },
21
26
  }
22
27
 
23
28
 
@@ -92,6 +97,7 @@ def build_logger(logger_name: str, source_type: Optional[str] = None) -> Any:
92
97
 
93
98
  agent_logger: AgnoLogger = build_logger(LOGGER_NAME, source_type="agent")
94
99
  team_logger: AgnoLogger = build_logger(TEAM_LOGGER_NAME, source_type="team")
100
+ workflow_logger: AgnoLogger = build_logger(WORKFLOW_LOGGER_NAME, source_type="workflow")
95
101
 
96
102
  # Set the default logger to the agent logger
97
103
  logger: AgnoLogger = agent_logger
@@ -146,6 +152,12 @@ def use_agent_logger():
146
152
  logger = agent_logger
147
153
 
148
154
 
155
+ def use_workflow_logger():
156
+ """Switch the default logger to use workflow_logger"""
157
+ global logger
158
+ logger = workflow_logger
159
+
160
+
149
161
  def log_debug(msg, center: bool = False, symbol: str = "*", log_level: Literal[1, 2] = 1, *args, **kwargs):
150
162
  global logger
151
163
  global debug_on
agno/utils/message.py CHANGED
@@ -1,13 +1,17 @@
1
1
  from typing import Dict, List, Union
2
2
 
3
+ from pydantic import BaseModel
4
+
3
5
  from agno.models.message import Message
4
6
 
5
7
 
6
- def get_text_from_message(message: Union[List, Dict, str, Message]) -> str:
8
+ def get_text_from_message(message: Union[List, Dict, str, Message, BaseModel]) -> str:
7
9
  """Return the user texts from the message"""
8
10
 
9
11
  if isinstance(message, str):
10
12
  return message
13
+ if isinstance(message, BaseModel):
14
+ return message.model_dump_json(indent=2, exclude_none=True)
11
15
  if isinstance(message, list):
12
16
  text_messages = []
13
17
  if len(message) == 0:
agno/utils/openai.py CHANGED
@@ -93,11 +93,24 @@ def audio_to_message(audio: Sequence[Audio]) -> List[Dict[str, Any]]:
93
93
  return audio_messages
94
94
 
95
95
 
96
- def _process_bytes_image(image: bytes) -> Dict[str, Any]:
96
+ def _process_bytes_image(image: bytes, image_format: Optional[str] = None) -> Dict[str, Any]:
97
97
  """Process bytes image data."""
98
98
  base64_image = base64.b64encode(image).decode("utf-8")
99
- # Assuming JPEG if type not specified, could attempt detection
100
- image_url = f"data:image/jpeg;base64,{base64_image}"
99
+
100
+ # Use provided format or attempt detection, defaulting to JPEG
101
+ if image_format:
102
+ mime_type = f"image/{image_format.lower()}"
103
+ else:
104
+ # Try to detect the image format from the bytes
105
+ try:
106
+ import imghdr
107
+
108
+ detected_format = imghdr.what(None, h=image)
109
+ mime_type = f"image/{detected_format}" if detected_format else "image/jpeg"
110
+ except Exception:
111
+ mime_type = "image/jpeg"
112
+
113
+ image_url = f"data:{mime_type};base64,{base64_image}"
101
114
  return {"type": "image_url", "image_url": {"url": image_url}}
102
115
 
103
116
 
@@ -141,7 +154,8 @@ def process_image(image: Image) -> Optional[Dict[str, Any]]:
141
154
  image_payload = _process_image_path(image.filepath)
142
155
 
143
156
  elif image.content is not None:
144
- image_payload = _process_bytes_image(image.content)
157
+ # Pass the format from the Image object
158
+ image_payload = _process_bytes_image(image.content, image.format)
145
159
 
146
160
  else:
147
161
  log_warning(f"Unsupported image format or no data provided: {image}")
@@ -150,7 +164,8 @@ def process_image(image: Image) -> Optional[Dict[str, Any]]:
150
164
  if image_payload and image.detail: # Check if payload was created before adding detail
151
165
  # Ensure image_url key exists before trying to access its sub-dictionary
152
166
  if "image_url" not in image_payload:
153
- image_payload["image_url"] = {} # Initialize if missing (though unlikely based on helper funcs)
167
+ # Initialize if missing (though unlikely based on helper funcs)
168
+ image_payload["image_url"] = {}
154
169
  image_payload["image_url"]["detail"] = image.detail
155
170
 
156
171
  return image_payload
agno/utils/pprint.py CHANGED
@@ -5,13 +5,20 @@ from pydantic import BaseModel
5
5
 
6
6
  from agno.run.response import RunResponse, RunResponseEvent
7
7
  from agno.run.team import TeamRunResponse, TeamRunResponseEvent
8
- from agno.run.workflow import WorkflowRunResponseEvent
8
+ from agno.run.v2.workflow import WorkflowRunResponse, WorkflowRunResponseEvent
9
9
  from agno.utils.log import logger
10
10
  from agno.utils.timer import Timer
11
11
 
12
12
 
13
13
  def pprint_run_response(
14
- run_response: Union[RunResponse, Iterable[RunResponseEvent], TeamRunResponse, Iterable[TeamRunResponseEvent]],
14
+ run_response: Union[
15
+ RunResponse,
16
+ Iterable[RunResponseEvent],
17
+ TeamRunResponse,
18
+ Iterable[TeamRunResponseEvent],
19
+ WorkflowRunResponse,
20
+ Iterable[WorkflowRunResponseEvent],
21
+ ],
15
22
  markdown: bool = False,
16
23
  show_time: bool = False,
17
24
  ) -> None:
@@ -25,7 +32,11 @@ def pprint_run_response(
25
32
  from agno.cli.console import console
26
33
 
27
34
  # If run_response is a single RunResponse, wrap it in a list to make it iterable
28
- if isinstance(run_response, RunResponse) or isinstance(run_response, TeamRunResponse):
35
+ if (
36
+ isinstance(run_response, RunResponse)
37
+ or isinstance(run_response, TeamRunResponse)
38
+ or isinstance(run_response, WorkflowRunResponse)
39
+ ):
29
40
  single_response_content: Union[str, JSON, Markdown] = ""
30
41
  if isinstance(run_response.content, str):
31
42
  single_response_content = (
@@ -64,11 +75,13 @@ def pprint_run_response(
64
75
  ):
65
76
  if isinstance(resp.content, BaseModel):
66
77
  try:
67
- streaming_response_content = JSON(resp.content.model_dump_json(exclude_none=True), indent=2) # type: ignore
78
+ JSON(resp.content.model_dump_json(exclude_none=True), indent=2) # type: ignore
68
79
  except Exception as e:
69
80
  logger.warning(f"Failed to convert response to Markdown: {e}")
70
81
  else:
71
- streaming_response_content += resp.content
82
+ if isinstance(streaming_response_content, JSON):
83
+ streaming_response_content = streaming_response_content.text + "\n" # type: ignore
84
+ streaming_response_content += resp.content # type: ignore
72
85
 
73
86
  formatted_response = Markdown(streaming_response_content) if markdown else streaming_response_content # type: ignore
74
87
  table = Table(box=ROUNDED, border_style="blue", show_header=False)
@@ -81,7 +94,14 @@ def pprint_run_response(
81
94
 
82
95
 
83
96
  async def apprint_run_response(
84
- run_response: Union[RunResponse, AsyncIterable[RunResponse], TeamRunResponse, AsyncIterable[TeamRunResponse]],
97
+ run_response: Union[
98
+ RunResponse,
99
+ AsyncIterable[RunResponse],
100
+ TeamRunResponse,
101
+ AsyncIterable[TeamRunResponse],
102
+ WorkflowRunResponse,
103
+ AsyncIterable[WorkflowRunResponseEvent],
104
+ ],
85
105
  markdown: bool = False,
86
106
  show_time: bool = False,
87
107
  ) -> None:
@@ -95,7 +115,11 @@ async def apprint_run_response(
95
115
  from agno.cli.console import console
96
116
 
97
117
  # If run_response is a single RunResponse, wrap it in a list to make it iterable
98
- if isinstance(run_response, RunResponse) or isinstance(run_response, TeamRunResponse):
118
+ if (
119
+ isinstance(run_response, RunResponse)
120
+ or isinstance(run_response, TeamRunResponse)
121
+ or isinstance(run_response, WorkflowRunResponse)
122
+ ):
99
123
  single_response_content: Union[str, JSON, Markdown] = ""
100
124
  if isinstance(run_response.content, str):
101
125
  single_response_content = (
@@ -139,7 +163,7 @@ async def apprint_run_response(
139
163
  except Exception as e:
140
164
  logger.warning(f"Failed to convert response to Markdown: {e}")
141
165
  else:
142
- streaming_response_content += resp.content
166
+ streaming_response_content += resp.content # type: ignore
143
167
 
144
168
  formatted_response = Markdown(streaming_response_content) if markdown else streaming_response_content # type: ignore
145
169
  table = Table(box=ROUNDED, border_style="blue", show_header=False)
@@ -0,0 +1,21 @@
1
+ from agno.workflow.v2.condition import Condition
2
+ from agno.workflow.v2.loop import Loop
3
+ from agno.workflow.v2.parallel import Parallel
4
+ from agno.workflow.v2.router import Router
5
+ from agno.workflow.v2.step import Step
6
+ from agno.workflow.v2.steps import Steps
7
+ from agno.workflow.v2.types import StepInput, StepOutput, WorkflowExecutionInput
8
+ from agno.workflow.v2.workflow import Workflow
9
+
10
+ __all__ = [
11
+ "Workflow",
12
+ "Steps",
13
+ "Step",
14
+ "Loop",
15
+ "Parallel",
16
+ "Condition",
17
+ "Router",
18
+ "WorkflowExecutionInput",
19
+ "StepInput",
20
+ "StepOutput",
21
+ ]