agno 2.1.4__py3-none-any.whl → 2.1.6__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 (95) hide show
  1. agno/agent/agent.py +1775 -538
  2. agno/db/async_postgres/__init__.py +3 -0
  3. agno/db/async_postgres/async_postgres.py +1668 -0
  4. agno/db/async_postgres/schemas.py +124 -0
  5. agno/db/async_postgres/utils.py +289 -0
  6. agno/db/base.py +237 -2
  7. agno/db/dynamo/dynamo.py +2 -2
  8. agno/db/firestore/firestore.py +2 -2
  9. agno/db/firestore/utils.py +4 -2
  10. agno/db/gcs_json/gcs_json_db.py +2 -2
  11. agno/db/in_memory/in_memory_db.py +2 -2
  12. agno/db/json/json_db.py +2 -2
  13. agno/db/migrations/v1_to_v2.py +43 -13
  14. agno/db/mongo/mongo.py +14 -6
  15. agno/db/mongo/utils.py +0 -4
  16. agno/db/mysql/mysql.py +23 -13
  17. agno/db/postgres/postgres.py +17 -6
  18. agno/db/redis/redis.py +2 -2
  19. agno/db/singlestore/singlestore.py +19 -10
  20. agno/db/sqlite/sqlite.py +22 -12
  21. agno/db/sqlite/utils.py +8 -3
  22. agno/db/surrealdb/__init__.py +3 -0
  23. agno/db/surrealdb/metrics.py +292 -0
  24. agno/db/surrealdb/models.py +259 -0
  25. agno/db/surrealdb/queries.py +71 -0
  26. agno/db/surrealdb/surrealdb.py +1193 -0
  27. agno/db/surrealdb/utils.py +87 -0
  28. agno/eval/accuracy.py +50 -43
  29. agno/eval/performance.py +6 -3
  30. agno/eval/reliability.py +6 -3
  31. agno/eval/utils.py +33 -16
  32. agno/exceptions.py +8 -2
  33. agno/knowledge/knowledge.py +260 -46
  34. agno/knowledge/reader/pdf_reader.py +4 -6
  35. agno/knowledge/reader/reader_factory.py +2 -3
  36. agno/memory/manager.py +254 -46
  37. agno/models/anthropic/claude.py +37 -0
  38. agno/os/app.py +8 -7
  39. agno/os/interfaces/a2a/router.py +3 -5
  40. agno/os/interfaces/agui/router.py +4 -1
  41. agno/os/interfaces/agui/utils.py +27 -6
  42. agno/os/interfaces/slack/router.py +2 -4
  43. agno/os/mcp.py +98 -41
  44. agno/os/router.py +23 -0
  45. agno/os/routers/evals/evals.py +52 -20
  46. agno/os/routers/evals/utils.py +14 -14
  47. agno/os/routers/knowledge/knowledge.py +130 -9
  48. agno/os/routers/knowledge/schemas.py +57 -0
  49. agno/os/routers/memory/memory.py +116 -44
  50. agno/os/routers/metrics/metrics.py +16 -6
  51. agno/os/routers/session/session.py +65 -22
  52. agno/os/schema.py +36 -0
  53. agno/os/utils.py +64 -11
  54. agno/reasoning/anthropic.py +80 -0
  55. agno/reasoning/gemini.py +73 -0
  56. agno/reasoning/openai.py +5 -0
  57. agno/reasoning/vertexai.py +76 -0
  58. agno/session/workflow.py +3 -3
  59. agno/team/team.py +968 -179
  60. agno/tools/googlesheets.py +20 -5
  61. agno/tools/mcp_toolbox.py +3 -3
  62. agno/tools/scrapegraph.py +1 -1
  63. agno/utils/models/claude.py +3 -1
  64. agno/utils/streamlit.py +1 -1
  65. agno/vectordb/base.py +22 -1
  66. agno/vectordb/cassandra/cassandra.py +9 -0
  67. agno/vectordb/chroma/chromadb.py +26 -6
  68. agno/vectordb/clickhouse/clickhousedb.py +9 -1
  69. agno/vectordb/couchbase/couchbase.py +11 -0
  70. agno/vectordb/lancedb/lance_db.py +20 -0
  71. agno/vectordb/langchaindb/langchaindb.py +11 -0
  72. agno/vectordb/lightrag/lightrag.py +9 -0
  73. agno/vectordb/llamaindex/llamaindexdb.py +15 -1
  74. agno/vectordb/milvus/milvus.py +23 -0
  75. agno/vectordb/mongodb/mongodb.py +22 -0
  76. agno/vectordb/pgvector/pgvector.py +19 -0
  77. agno/vectordb/pineconedb/pineconedb.py +35 -4
  78. agno/vectordb/qdrant/qdrant.py +24 -0
  79. agno/vectordb/singlestore/singlestore.py +25 -17
  80. agno/vectordb/surrealdb/surrealdb.py +18 -2
  81. agno/vectordb/upstashdb/upstashdb.py +26 -1
  82. agno/vectordb/weaviate/weaviate.py +18 -0
  83. agno/workflow/condition.py +4 -0
  84. agno/workflow/loop.py +4 -0
  85. agno/workflow/parallel.py +4 -0
  86. agno/workflow/router.py +4 -0
  87. agno/workflow/step.py +30 -14
  88. agno/workflow/steps.py +4 -0
  89. agno/workflow/types.py +2 -2
  90. agno/workflow/workflow.py +328 -61
  91. {agno-2.1.4.dist-info → agno-2.1.6.dist-info}/METADATA +100 -41
  92. {agno-2.1.4.dist-info → agno-2.1.6.dist-info}/RECORD +95 -82
  93. {agno-2.1.4.dist-info → agno-2.1.6.dist-info}/WHEEL +0 -0
  94. {agno-2.1.4.dist-info → agno-2.1.6.dist-info}/licenses/LICENSE +0 -0
  95. {agno-2.1.4.dist-info → agno-2.1.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,87 @@
1
+ import dataclasses
2
+ from typing import Any, Optional, Sequence, TypeVar, Union, cast
3
+
4
+ from surrealdb import BlockingHttpSurrealConnection, BlockingWsSurrealConnection, Surreal
5
+
6
+ from agno.utils.log import logger
7
+
8
+ RecordType = TypeVar("RecordType")
9
+
10
+
11
+ def build_client(
12
+ url: str, creds: dict[str, str], ns: str, db: str
13
+ ) -> Union[BlockingWsSurrealConnection, BlockingHttpSurrealConnection]:
14
+ client = Surreal(url=url)
15
+ client.signin(creds)
16
+ client.use(namespace=ns, database=db)
17
+ return client
18
+
19
+
20
+ def _query_aux(
21
+ client: Union[BlockingWsSurrealConnection, BlockingHttpSurrealConnection],
22
+ query: str,
23
+ vars: dict[str, Any],
24
+ ) -> Union[list, dict, str, int]:
25
+ try:
26
+ response = client.query(query, vars)
27
+ except Exception as e:
28
+ msg = f"!! Query execution error: {query} with {vars}, Error: {e}"
29
+ logger.error(msg)
30
+ raise RuntimeError(msg)
31
+ return response
32
+
33
+
34
+ def query(
35
+ client: Union[BlockingWsSurrealConnection, BlockingHttpSurrealConnection],
36
+ query: str,
37
+ vars: dict[str, Any],
38
+ record_type: type[RecordType],
39
+ ) -> Sequence[RecordType]:
40
+ response = _query_aux(client, query, vars)
41
+ if isinstance(response, list):
42
+ if dataclasses.is_dataclass(record_type) and hasattr(record_type, "from_dict"):
43
+ return [getattr(record_type, "from_dict").__call__(x) for x in response]
44
+ else:
45
+ result: list[RecordType] = []
46
+ for x in response:
47
+ if isinstance(x, dict):
48
+ result.append(record_type(**x))
49
+ else:
50
+ result.append(record_type.__call__(x))
51
+ return result
52
+ else:
53
+ raise ValueError(f"Unexpected response type: {type(response)}")
54
+
55
+
56
+ def query_one(
57
+ client: Union[BlockingWsSurrealConnection, BlockingHttpSurrealConnection],
58
+ query: str,
59
+ vars: dict[str, Any],
60
+ record_type: type[RecordType],
61
+ ) -> Optional[RecordType]:
62
+ response = _query_aux(client, query, vars)
63
+ if response is None:
64
+ return None
65
+ elif not isinstance(response, list):
66
+ if dataclasses.is_dataclass(record_type) and hasattr(record_type, "from_dict"):
67
+ return getattr(record_type, "from_dict").__call__(response)
68
+ elif isinstance(response, dict):
69
+ return record_type(**response)
70
+ else:
71
+ return record_type.__call__(response)
72
+ elif isinstance(response, list):
73
+ # Handle list responses - SurrealDB might return a list with a single element
74
+ if len(response) == 1 and isinstance(response[0], dict):
75
+ result = response[0]
76
+ if dataclasses.is_dataclass(record_type) and hasattr(record_type, "from_dict"):
77
+ return getattr(record_type, "from_dict").__call__(result)
78
+ elif record_type is dict:
79
+ return cast(RecordType, result)
80
+ else:
81
+ return record_type(**result)
82
+ elif len(response) == 0:
83
+ return None
84
+ else:
85
+ raise ValueError(f"Expected single record, got {len(response)} records: {response}")
86
+ else:
87
+ raise ValueError(f"Unexpected response type: {type(response)}")
agno/eval/accuracy.py CHANGED
@@ -7,13 +7,13 @@ from uuid import uuid4
7
7
  from pydantic import BaseModel, Field
8
8
 
9
9
  from agno.agent import Agent
10
- from agno.db.base import BaseDb
10
+ from agno.db.base import AsyncBaseDb, BaseDb
11
11
  from agno.db.schemas.evals import EvalType
12
12
  from agno.eval.utils import async_log_eval, log_eval_run, store_result_in_file
13
13
  from agno.exceptions import EvalError
14
14
  from agno.models.base import Model
15
15
  from agno.team.team import Team
16
- from agno.utils.log import logger, set_log_level_to_debug, set_log_level_to_info
16
+ from agno.utils.log import log_error, logger, set_log_level_to_debug, set_log_level_to_info
17
17
 
18
18
  if TYPE_CHECKING:
19
19
  from rich.console import Console
@@ -176,7 +176,7 @@ class AccuracyEval:
176
176
  # Enable debug logs
177
177
  debug_mode: bool = getenv("AGNO_DEBUG", "false").lower() == "true"
178
178
  # The database to store Evaluation results
179
- db: Optional[BaseDb] = None
179
+ db: Optional[Union[BaseDb, AsyncBaseDb]] = None
180
180
 
181
181
  # Telemetry settings
182
182
  # telemetry=True logs minimal telemetry for analytics
@@ -327,6 +327,9 @@ Remember: You must only compare the agent_output to the expected_output. The exp
327
327
  print_summary: bool = True,
328
328
  print_results: bool = True,
329
329
  ) -> Optional[AccuracyResult]:
330
+ if isinstance(self.db, AsyncBaseDb):
331
+ raise ValueError("run() is not supported with an async DB. Please use arun() instead.")
332
+
330
333
  if self.agent is None and self.team is None:
331
334
  logger.error("You need to provide one of 'agent' or 'team' to run the evaluation.")
332
335
  return None
@@ -661,47 +664,51 @@ Remember: You must only compare the agent_output to the expected_output. The exp
661
664
  )
662
665
  # Log results to the Agno DB if requested
663
666
  if self.db:
664
- if self.agent is not None:
665
- agent_id = self.agent.id
666
- team_id = None
667
- model_id = self.agent.model.id if self.agent.model is not None else None
668
- model_provider = self.agent.model.provider if self.agent.model is not None else None
669
- evaluated_component_name = self.agent.name
670
- elif self.team is not None:
671
- agent_id = None
672
- team_id = self.team.id
673
- model_id = self.team.model.id if self.team.model is not None else None
674
- model_provider = self.team.model.provider if self.team.model is not None else None
675
- evaluated_component_name = self.team.name
676
- else:
677
- agent_id = None
678
- team_id = None
679
- model_id = None
680
- model_provider = None
681
- evaluated_component_name = None
667
+ if isinstance(self.db, AsyncBaseDb):
668
+ log_error("You are using an async DB in a non-async method. The evaluation won't be stored in the DB.")
682
669
 
683
- log_eval_input = {
684
- "additional_guidelines": self.additional_guidelines,
685
- "additional_context": self.additional_context,
686
- "num_iterations": self.num_iterations,
687
- "expected_output": self.expected_output,
688
- "input": self.input,
689
- }
690
-
691
- log_eval_run(
692
- db=self.db,
693
- run_id=self.eval_id, # type: ignore
694
- run_data=asdict(self.result),
695
- eval_type=EvalType.ACCURACY,
696
- name=self.name if self.name is not None else None,
697
- agent_id=agent_id,
698
- team_id=team_id,
699
- model_id=model_id,
700
- model_provider=model_provider,
701
- evaluated_component_name=evaluated_component_name,
702
- workflow_id=None,
703
- eval_input=log_eval_input,
704
- )
670
+ else:
671
+ if self.agent is not None:
672
+ agent_id = self.agent.id
673
+ team_id = None
674
+ model_id = self.agent.model.id if self.agent.model is not None else None
675
+ model_provider = self.agent.model.provider if self.agent.model is not None else None
676
+ evaluated_component_name = self.agent.name
677
+ elif self.team is not None:
678
+ agent_id = None
679
+ team_id = self.team.id
680
+ model_id = self.team.model.id if self.team.model is not None else None
681
+ model_provider = self.team.model.provider if self.team.model is not None else None
682
+ evaluated_component_name = self.team.name
683
+ else:
684
+ agent_id = None
685
+ team_id = None
686
+ model_id = None
687
+ model_provider = None
688
+ evaluated_component_name = None
689
+
690
+ log_eval_input = {
691
+ "additional_guidelines": self.additional_guidelines,
692
+ "additional_context": self.additional_context,
693
+ "num_iterations": self.num_iterations,
694
+ "expected_output": self.expected_output,
695
+ "input": self.input,
696
+ }
697
+
698
+ log_eval_run(
699
+ db=self.db,
700
+ run_id=self.eval_id, # type: ignore
701
+ run_data=asdict(self.result),
702
+ eval_type=EvalType.ACCURACY,
703
+ name=self.name if self.name is not None else None,
704
+ agent_id=agent_id,
705
+ team_id=team_id,
706
+ model_id=model_id,
707
+ model_provider=model_provider,
708
+ evaluated_component_name=evaluated_component_name,
709
+ workflow_id=None,
710
+ eval_input=log_eval_input,
711
+ )
705
712
 
706
713
  if self.telemetry:
707
714
  from agno.api.evals import EvalRunCreate, create_eval_run_telemetry
agno/eval/performance.py CHANGED
@@ -3,10 +3,10 @@ import gc
3
3
  import tracemalloc
4
4
  from dataclasses import dataclass, field
5
5
  from os import getenv
6
- from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional
6
+ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
7
7
  from uuid import uuid4
8
8
 
9
- from agno.db.base import BaseDb
9
+ from agno.db.base import AsyncBaseDb, BaseDb
10
10
  from agno.db.schemas.evals import EvalType
11
11
  from agno.eval.utils import async_log_eval, log_eval_run, store_result_in_file
12
12
  from agno.utils.log import log_debug, set_log_level_to_debug, set_log_level_to_info
@@ -222,7 +222,7 @@ class PerformanceEval:
222
222
  # Enable debug logs
223
223
  debug_mode: bool = getenv("AGNO_DEBUG", "false").lower() == "true"
224
224
  # The database to store Evaluation results
225
- db: Optional[BaseDb] = None
225
+ db: Optional[Union[BaseDb, AsyncBaseDb]] = None
226
226
 
227
227
  # Telemetry settings
228
228
  # telemetry=True logs minimal telemetry for analytics
@@ -491,6 +491,9 @@ class PerformanceEval:
491
491
  6. Print results as requested
492
492
  7. Log results to the Agno platform if requested
493
493
  """
494
+ if isinstance(self.db, AsyncBaseDb):
495
+ raise ValueError("run() is not supported with an async DB. Please use arun() instead.")
496
+
494
497
  from rich.console import Console
495
498
  from rich.live import Live
496
499
  from rich.status import Status
agno/eval/reliability.py CHANGED
@@ -1,9 +1,9 @@
1
1
  from dataclasses import asdict, dataclass, field
2
2
  from os import getenv
3
- from typing import TYPE_CHECKING, Any, Dict, List, Optional
3
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
4
4
  from uuid import uuid4
5
5
 
6
- from agno.db.base import BaseDb
6
+ from agno.db.base import AsyncBaseDb, BaseDb
7
7
 
8
8
  if TYPE_CHECKING:
9
9
  from rich.console import Console
@@ -63,7 +63,7 @@ class ReliabilityEval:
63
63
  # Enable debug logs
64
64
  debug_mode: bool = getenv("AGNO_DEBUG", "false").lower() == "true"
65
65
  # The database to store Evaluation results
66
- db: Optional[BaseDb] = None
66
+ db: Optional[Union[BaseDb, AsyncBaseDb]] = None
67
67
 
68
68
  # Telemetry settings
69
69
  # telemetry=True logs minimal telemetry for analytics
@@ -71,6 +71,9 @@ class ReliabilityEval:
71
71
  telemetry: bool = True
72
72
 
73
73
  def run(self, *, print_results: bool = False) -> Optional[ReliabilityResult]:
74
+ if isinstance(self.db, AsyncBaseDb):
75
+ raise ValueError("run() is not supported with an async DB. Please use arun() instead.")
76
+
74
77
  if self.agent_response is None and self.team_response is None:
75
78
  raise ValueError("You need to provide 'agent_response' or 'team_response' to run the evaluation.")
76
79
 
agno/eval/utils.py CHANGED
@@ -2,7 +2,7 @@ from dataclasses import asdict
2
2
  from pathlib import Path
3
3
  from typing import TYPE_CHECKING, Optional, Union
4
4
 
5
- from agno.db.base import BaseDb
5
+ from agno.db.base import AsyncBaseDb, BaseDb
6
6
  from agno.db.schemas.evals import EvalRunRecord, EvalType
7
7
  from agno.utils.log import log_debug, logger
8
8
 
@@ -49,7 +49,7 @@ def log_eval_run(
49
49
 
50
50
 
51
51
  async def async_log_eval(
52
- db: BaseDb,
52
+ db: Union[BaseDb, AsyncBaseDb],
53
53
  run_id: str,
54
54
  run_data: dict,
55
55
  eval_type: EvalType,
@@ -65,21 +65,38 @@ async def async_log_eval(
65
65
  """Call the API to create an evaluation run."""
66
66
 
67
67
  try:
68
- db.create_eval_run(
69
- EvalRunRecord(
70
- run_id=run_id,
71
- eval_type=eval_type,
72
- eval_data=run_data,
73
- eval_input=eval_input,
74
- agent_id=agent_id,
75
- model_id=model_id,
76
- model_provider=model_provider,
77
- name=name,
78
- evaluated_component_name=evaluated_component_name,
79
- team_id=team_id,
80
- workflow_id=workflow_id,
68
+ if isinstance(db, AsyncBaseDb):
69
+ await db.create_eval_run(
70
+ EvalRunRecord(
71
+ run_id=run_id,
72
+ eval_type=eval_type,
73
+ eval_data=run_data,
74
+ eval_input=eval_input,
75
+ agent_id=agent_id,
76
+ model_id=model_id,
77
+ model_provider=model_provider,
78
+ name=name,
79
+ evaluated_component_name=evaluated_component_name,
80
+ team_id=team_id,
81
+ workflow_id=workflow_id,
82
+ )
83
+ )
84
+ else:
85
+ db.create_eval_run(
86
+ EvalRunRecord(
87
+ run_id=run_id,
88
+ eval_type=eval_type,
89
+ eval_data=run_data,
90
+ eval_input=eval_input,
91
+ agent_id=agent_id,
92
+ model_id=model_id,
93
+ model_provider=model_provider,
94
+ name=name,
95
+ evaluated_component_name=evaluated_component_name,
96
+ team_id=team_id,
97
+ workflow_id=workflow_id,
98
+ )
81
99
  )
82
- )
83
100
  except Exception as e:
84
101
  log_debug(f"Could not create agent event: {e}")
85
102
 
agno/exceptions.py CHANGED
@@ -130,7 +130,10 @@ class InputCheckError(Exception):
130
130
  ):
131
131
  super().__init__(message)
132
132
  self.type = "input_check_error"
133
- self.error_id = check_trigger.value
133
+ if isinstance(check_trigger, CheckTrigger):
134
+ self.error_id = check_trigger.value
135
+ else:
136
+ self.error_id = str(check_trigger)
134
137
 
135
138
  self.message = message
136
139
  self.check_trigger = check_trigger
@@ -148,7 +151,10 @@ class OutputCheckError(Exception):
148
151
  ):
149
152
  super().__init__(message)
150
153
  self.type = "output_check_error"
151
- self.error_id = check_trigger.value
154
+ if isinstance(check_trigger, CheckTrigger):
155
+ self.error_id = check_trigger.value
156
+ else:
157
+ self.error_id = str(check_trigger)
152
158
 
153
159
  self.message = message
154
160
  self.check_trigger = check_trigger