agno 2.1.4__py3-none-any.whl → 2.1.5__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 (88) hide show
  1. agno/agent/agent.py +1767 -535
  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 +30 -13
  14. agno/db/mongo/mongo.py +18 -6
  15. agno/db/mysql/mysql.py +35 -13
  16. agno/db/postgres/postgres.py +29 -6
  17. agno/db/redis/redis.py +2 -2
  18. agno/db/singlestore/singlestore.py +2 -2
  19. agno/db/sqlite/sqlite.py +34 -12
  20. agno/db/sqlite/utils.py +8 -3
  21. agno/eval/accuracy.py +50 -43
  22. agno/eval/performance.py +6 -3
  23. agno/eval/reliability.py +6 -3
  24. agno/eval/utils.py +33 -16
  25. agno/exceptions.py +8 -2
  26. agno/knowledge/knowledge.py +260 -46
  27. agno/knowledge/reader/pdf_reader.py +4 -6
  28. agno/knowledge/reader/reader_factory.py +2 -3
  29. agno/memory/manager.py +241 -33
  30. agno/models/anthropic/claude.py +37 -0
  31. agno/os/app.py +8 -7
  32. agno/os/interfaces/a2a/router.py +3 -5
  33. agno/os/interfaces/agui/router.py +4 -1
  34. agno/os/interfaces/agui/utils.py +27 -6
  35. agno/os/interfaces/slack/router.py +2 -4
  36. agno/os/mcp.py +98 -41
  37. agno/os/router.py +23 -0
  38. agno/os/routers/evals/evals.py +52 -20
  39. agno/os/routers/evals/utils.py +14 -14
  40. agno/os/routers/knowledge/knowledge.py +130 -9
  41. agno/os/routers/knowledge/schemas.py +57 -0
  42. agno/os/routers/memory/memory.py +116 -44
  43. agno/os/routers/metrics/metrics.py +16 -6
  44. agno/os/routers/session/session.py +65 -22
  45. agno/os/schema.py +36 -0
  46. agno/os/utils.py +67 -12
  47. agno/reasoning/anthropic.py +80 -0
  48. agno/reasoning/gemini.py +73 -0
  49. agno/reasoning/openai.py +5 -0
  50. agno/reasoning/vertexai.py +76 -0
  51. agno/session/workflow.py +3 -3
  52. agno/team/team.py +918 -175
  53. agno/tools/googlesheets.py +20 -5
  54. agno/tools/mcp_toolbox.py +3 -3
  55. agno/tools/scrapegraph.py +1 -1
  56. agno/utils/models/claude.py +3 -1
  57. agno/utils/streamlit.py +1 -1
  58. agno/vectordb/base.py +22 -1
  59. agno/vectordb/cassandra/cassandra.py +9 -0
  60. agno/vectordb/chroma/chromadb.py +26 -6
  61. agno/vectordb/clickhouse/clickhousedb.py +9 -1
  62. agno/vectordb/couchbase/couchbase.py +11 -0
  63. agno/vectordb/lancedb/lance_db.py +20 -0
  64. agno/vectordb/langchaindb/langchaindb.py +11 -0
  65. agno/vectordb/lightrag/lightrag.py +9 -0
  66. agno/vectordb/llamaindex/llamaindexdb.py +15 -1
  67. agno/vectordb/milvus/milvus.py +23 -0
  68. agno/vectordb/mongodb/mongodb.py +22 -0
  69. agno/vectordb/pgvector/pgvector.py +19 -0
  70. agno/vectordb/pineconedb/pineconedb.py +35 -4
  71. agno/vectordb/qdrant/qdrant.py +24 -0
  72. agno/vectordb/singlestore/singlestore.py +25 -17
  73. agno/vectordb/surrealdb/surrealdb.py +18 -1
  74. agno/vectordb/upstashdb/upstashdb.py +26 -1
  75. agno/vectordb/weaviate/weaviate.py +18 -0
  76. agno/workflow/condition.py +4 -0
  77. agno/workflow/loop.py +4 -0
  78. agno/workflow/parallel.py +4 -0
  79. agno/workflow/router.py +4 -0
  80. agno/workflow/step.py +22 -14
  81. agno/workflow/steps.py +4 -0
  82. agno/workflow/types.py +2 -2
  83. agno/workflow/workflow.py +328 -61
  84. {agno-2.1.4.dist-info → agno-2.1.5.dist-info}/METADATA +100 -41
  85. {agno-2.1.4.dist-info → agno-2.1.5.dist-info}/RECORD +88 -81
  86. {agno-2.1.4.dist-info → agno-2.1.5.dist-info}/WHEEL +0 -0
  87. {agno-2.1.4.dist-info → agno-2.1.5.dist-info}/licenses/LICENSE +0 -0
  88. {agno-2.1.4.dist-info → agno-2.1.5.dist-info}/top_level.txt +0 -0
@@ -48,13 +48,14 @@ import json
48
48
  from functools import wraps
49
49
  from os import getenv
50
50
  from pathlib import Path
51
- from typing import Any, List, Optional
51
+ from typing import Any, List, Optional, Union
52
52
 
53
53
  from agno.tools import Toolkit
54
54
 
55
55
  try:
56
56
  from google.auth.transport.requests import Request
57
57
  from google.oauth2.credentials import Credentials
58
+ from google.oauth2.service_account import Credentials as ServiceAccountCredentials
58
59
  from google_auth_oauthlib.flow import InstalledAppFlow
59
60
  from googleapiclient.discovery import Resource, build
60
61
  except ImportError:
@@ -91,9 +92,10 @@ class GoogleSheetsTools(Toolkit):
91
92
  scopes: Optional[List[str]] = None,
92
93
  spreadsheet_id: Optional[str] = None,
93
94
  spreadsheet_range: Optional[str] = None,
94
- creds: Optional[Credentials] = None,
95
+ creds: Optional[Union[Credentials, ServiceAccountCredentials]] = None,
95
96
  creds_path: Optional[str] = None,
96
97
  token_path: Optional[str] = None,
98
+ service_account_path: Optional[str] = None,
97
99
  oauth_port: int = 0,
98
100
  enable_read_sheet: bool = True,
99
101
  enable_create_sheet: bool = False,
@@ -108,9 +110,10 @@ class GoogleSheetsTools(Toolkit):
108
110
  scopes (Optional[List[str]]): Custom OAuth scopes. If None, uses write scope by default.
109
111
  spreadsheet_id (Optional[str]): ID of the target spreadsheet.
110
112
  spreadsheet_range (Optional[str]): Range within the spreadsheet.
111
- creds (Optional[Credentials]): Pre-existing credentials.
113
+ creds (Optional[Credentials | ServiceAccountCredentials]): Pre-existing credentials.
112
114
  creds_path (Optional[str]): Path to credentials file.
113
115
  token_path (Optional[str]): Path to token file.
116
+ service_account_path (Optional[str]): Path to a service account file.
114
117
  oauth_port (int): Port to use for OAuth authentication. Defaults to 0.
115
118
  enable_read_sheet (bool): Enable reading from a sheet.
116
119
  enable_create_sheet (bool): Enable creating a sheet.
@@ -126,6 +129,7 @@ class GoogleSheetsTools(Toolkit):
126
129
  self.token_path = token_path
127
130
  self.oauth_port = oauth_port
128
131
  self.service: Optional[Resource] = None
132
+ self.service_account_path = service_account_path
129
133
 
130
134
  # Determine required scopes based on operations if no custom scopes provided
131
135
  if scopes is None:
@@ -171,6 +175,17 @@ class GoogleSheetsTools(Toolkit):
171
175
  if self.creds and self.creds.valid:
172
176
  return
173
177
 
178
+ service_account_path = self.service_account_path or getenv("GOOGLE_SERVICE_ACCOUNT_FILE")
179
+
180
+ if service_account_path:
181
+ self.creds = ServiceAccountCredentials.from_service_account_file(
182
+ service_account_path,
183
+ scopes=self.scopes,
184
+ )
185
+ if self.creds and self.creds.expired:
186
+ self.creds.refresh(Request())
187
+ return
188
+
174
189
  token_file = Path(self.token_path or "token.json")
175
190
  creds_file = Path(self.credentials_path or "credentials.json")
176
191
 
@@ -178,7 +193,7 @@ class GoogleSheetsTools(Toolkit):
178
193
  self.creds = Credentials.from_authorized_user_file(str(token_file), self.scopes)
179
194
 
180
195
  if not self.creds or not self.creds.valid:
181
- if self.creds and self.creds.expired and self.creds.refresh_token:
196
+ if self.creds and self.creds.expired and self.creds.refresh_token: # type: ignore
182
197
  self.creds.refresh(Request())
183
198
  else:
184
199
  client_config = {
@@ -199,7 +214,7 @@ class GoogleSheetsTools(Toolkit):
199
214
  flow = InstalledAppFlow.from_client_config(client_config, self.scopes)
200
215
  # Opens up a browser window for OAuth authentication
201
216
  self.creds = flow.run_local_server(port=self.oauth_port)
202
- token_file.write_text(self.creds.to_json()) if self.creds else None
217
+ token_file.write_text(self.creds.to_json()) if self.creds else None # type: ignore
203
218
 
204
219
  @authenticate
205
220
  def read_sheet(self, spreadsheet_id: Optional[str] = None, spreadsheet_range: Optional[str] = None) -> str:
agno/tools/mcp_toolbox.py CHANGED
@@ -35,6 +35,7 @@ class MCPToolbox(MCPTools, metaclass=MCPToolsMeta):
35
35
  tool_name: Optional[str] = None,
36
36
  headers: Optional[Dict[str, Any]] = None,
37
37
  transport: Literal["stdio", "sse", "streamable-http"] = "streamable-http",
38
+ append_mcp_to_url: bool = True,
38
39
  **kwargs,
39
40
  ):
40
41
  """Initialize MCPToolbox with filtering capabilities.
@@ -45,11 +46,10 @@ class MCPToolbox(MCPTools, metaclass=MCPToolsMeta):
45
46
  tool_name (Optional[str], optional): Single tool name to load. Defaults to None.
46
47
  headers (Optional[Dict[str, Any]], optional): Headers for toolbox-core client requests. Defaults to None.
47
48
  transport (Literal["stdio", "sse", "streamable-http"], optional): MCP transport protocol. Defaults to "streamable-http".
49
+ append_mcp_to_url (bool, optional): Whether to append "/mcp" to the URL if it doesn't end with it. Defaults to True.
48
50
 
49
51
  """
50
-
51
- # Ensure the URL ends in "/mcp" as expected
52
- if not url.endswith("/mcp"):
52
+ if append_mcp_to_url and not url.endswith("/mcp"):
53
53
  url = url + "/mcp"
54
54
 
55
55
  super().__init__(url=url, transport=transport, **kwargs)
agno/tools/scrapegraph.py CHANGED
@@ -187,7 +187,7 @@ class ScrapeGraphTools(Toolkit):
187
187
  """
188
188
  try:
189
189
  log_debug(f"ScrapeGraph searchscraper request with prompt: {user_prompt}")
190
- response = self.client.searchscraper(user_prompt=user_prompt, render_heavy_js=self.render_heavy_js)
190
+ response = self.client.searchscraper(user_prompt=user_prompt)
191
191
  return json.dumps(response["result"])
192
192
  except Exception as e:
193
193
  error_msg = f"Searchscraper failed: {str(e)}"
@@ -32,6 +32,7 @@ class MCPServerConfiguration:
32
32
 
33
33
  ROLE_MAP = {
34
34
  "system": "system",
35
+ "developer": "system",
35
36
  "user": "user",
36
37
  "assistant": "assistant",
37
38
  "tool": "user",
@@ -217,7 +218,8 @@ def format_messages(messages: List[Message]) -> Tuple[List[Dict[str, str]], str]
217
218
 
218
219
  for message in messages:
219
220
  content = message.content or ""
220
- if message.role == "system":
221
+ # Both "system" and "developer" roles should be extracted as system messages
222
+ if message.role in ("system", "developer"):
221
223
  if content is not None:
222
224
  system_messages.append(content) # type: ignore
223
225
  continue
agno/utils/streamlit.py CHANGED
@@ -80,7 +80,7 @@ def session_selector_widget(agent: Agent, model_id: str, agent_creation_callback
80
80
  session_options = []
81
81
  session_dict = {}
82
82
 
83
- for session in sessions:
83
+ for session in sessions: # type: ignore
84
84
  if not hasattr(session, "session_id") or not session.session_id:
85
85
  continue
86
86
 
agno/vectordb/base.py CHANGED
@@ -1,11 +1,28 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from typing import Any, Dict, List, Optional
3
3
 
4
+ from agno.knowledge.document import Document
5
+ from agno.utils.string import generate_id
6
+
4
7
 
5
8
  class VectorDb(ABC):
6
9
  """Base class for Vector Databases"""
7
10
 
8
- from agno.knowledge.document import Document
11
+ def __init__(self, *, id: Optional[str] = None, name: Optional[str] = None, description: Optional[str] = None):
12
+ """Initialize base VectorDb.
13
+
14
+ Args:
15
+ id: Optional custom ID. If not provided, an id will be generated.
16
+ name: Optional name for the vector database.
17
+ description: Optional description for the vector database.
18
+ """
19
+ if name is None:
20
+ name = self.__class__.__name__
21
+
22
+ self.name = name
23
+ self.description = description
24
+ # Last resort fallback to generate id from name if ID not specified
25
+ self.id = id if id else generate_id(name)
9
26
 
10
27
  @abstractmethod
11
28
  def create(self) -> None:
@@ -106,3 +123,7 @@ class VectorDb(ABC):
106
123
  @abstractmethod
107
124
  def delete_by_content_id(self, content_id: str) -> bool:
108
125
  raise NotImplementedError
126
+
127
+ @abstractmethod
128
+ def get_supported_search_types(self) -> List[str]:
129
+ raise NotImplementedError
@@ -15,6 +15,8 @@ class Cassandra(VectorDb):
15
15
  keyspace: str,
16
16
  embedder: Optional[Embedder] = None,
17
17
  session=None,
18
+ name: Optional[str] = None,
19
+ description: Optional[str] = None,
18
20
  ) -> None:
19
21
  if not table_name:
20
22
  raise ValueError("Table name must be provided.")
@@ -30,6 +32,9 @@ class Cassandra(VectorDb):
30
32
 
31
33
  embedder = OpenAIEmbedder()
32
34
  log_info("Embedder not provided, using OpenAIEmbedder as default.")
35
+ # Initialize base class with name and description
36
+ super().__init__(name=name, description=description)
37
+
33
38
  self.table_name: str = table_name
34
39
  self.embedder: Embedder = embedder
35
40
  self.session = session
@@ -483,3 +488,7 @@ class Cassandra(VectorDb):
483
488
  except Exception as e:
484
489
  log_error(f"Error updating metadata for content_id {content_id}: {e}")
485
490
  raise
491
+
492
+ def get_supported_search_types(self) -> List[str]:
493
+ """Get the supported search types for this vector database."""
494
+ return [] # Cassandra doesn't use SearchType enum
@@ -25,6 +25,9 @@ class ChromaDb(VectorDb):
25
25
  def __init__(
26
26
  self,
27
27
  collection: str,
28
+ name: Optional[str] = None,
29
+ description: Optional[str] = None,
30
+ id: Optional[str] = None,
28
31
  embedder: Optional[Embedder] = None,
29
32
  distance: Distance = Distance.cosine,
30
33
  path: str = "tmp/chromadb",
@@ -32,9 +35,22 @@ class ChromaDb(VectorDb):
32
35
  reranker: Optional[Reranker] = None,
33
36
  **kwargs,
34
37
  ):
38
+ # Validate required parameters
39
+ if not collection:
40
+ raise ValueError("Collection name must be provided.")
41
+
42
+ # Dynamic ID generation based on unique identifiers
43
+ if id is None:
44
+ from agno.utils.string import generate_id
45
+
46
+ seed = f"{path}#{collection}"
47
+ id = generate_id(seed)
48
+
49
+ # Initialize base class with name, description, and generated ID
50
+ super().__init__(id=id, name=name, description=description)
51
+
35
52
  # Collection attributes
36
53
  self.collection_name: str = collection
37
-
38
54
  # Embedder for embedding the document contents
39
55
  if embedder is None:
40
56
  from agno.knowledge.embedder.openai import OpenAIEmbedder
@@ -497,11 +513,11 @@ class ChromaDb(VectorDb):
497
513
  # Build search results
498
514
  search_results: List[Document] = []
499
515
 
500
- ids_list = result.get("ids", [[]])
501
- metadata_list = result.get("metadatas", [[{}]])
502
- documents_list = result.get("documents", [[]])
503
- embeddings_list = result.get("embeddings")
504
- distances_list = result.get("distances", [[]])
516
+ ids_list = result.get("ids", [[]]) # type: ignore
517
+ metadata_list = result.get("metadatas", [[{}]]) # type: ignore
518
+ documents_list = result.get("documents", [[]]) # type: ignore
519
+ embeddings_list = result.get("embeddings") # type: ignore
520
+ distances_list = result.get("distances", [[]]) # type: ignore
505
521
 
506
522
  if not ids_list or not metadata_list or not documents_list or embeddings_list is None or not distances_list:
507
523
  return search_results
@@ -901,3 +917,7 @@ class ChromaDb(VectorDb):
901
917
  except Exception as e:
902
918
  logger.error(f"Error updating metadata for content_id '{content_id}': {e}")
903
919
  raise
920
+
921
+ def get_supported_search_types(self) -> List[str]:
922
+ """Get the supported search types for this vector database."""
923
+ return [] # ChromaDb doesn't use SearchType enum
@@ -23,6 +23,8 @@ class Clickhouse(VectorDb):
23
23
  self,
24
24
  table_name: str,
25
25
  host: str,
26
+ name: Optional[str] = None,
27
+ description: Optional[str] = None,
26
28
  username: Optional[str] = None,
27
29
  password: str = "",
28
30
  port: int = 0,
@@ -41,9 +43,11 @@ class Clickhouse(VectorDb):
41
43
  self.password = password
42
44
  self.port = port
43
45
  self.dsn = dsn
46
+ # Initialize base class with name and description
47
+ super().__init__(name=name, description=description)
48
+
44
49
  self.compress = compress
45
50
  self.database_name = database_name
46
-
47
51
  if not client:
48
52
  client = clickhouse_connect.get_client(
49
53
  host=self.host,
@@ -817,3 +821,7 @@ class Clickhouse(VectorDb):
817
821
  except Exception as e:
818
822
  logger.error(f"Error updating metadata for content_id '{content_id}': {e}")
819
823
  raise
824
+
825
+ def get_supported_search_types(self) -> List[str]:
826
+ """Get the supported search types for this vector database."""
827
+ return [] # Clickhouse doesn't use SearchType enum
@@ -66,6 +66,8 @@ class CouchbaseSearch(VectorDb):
66
66
  is_global_level_index: bool = False,
67
67
  wait_until_index_ready: float = 0,
68
68
  batch_limit: int = 500,
69
+ name: Optional[str] = None,
70
+ description: Optional[str] = None,
69
71
  **kwargs,
70
72
  ):
71
73
  """
@@ -75,6 +77,8 @@ class CouchbaseSearch(VectorDb):
75
77
  bucket_name (str): Name of the Couchbase bucket.
76
78
  scope_name (str): Name of the scope within the bucket.
77
79
  collection_name (str): Name of the collection within the scope.
80
+ name (Optional[str]): Name of the vector database.
81
+ description (Optional[str]): Description of the vector database.
78
82
  couchbase_connection_string (str): Couchbase connection string.
79
83
  cluster_options (ClusterOptions): Options for configuring the Couchbase cluster connection.
80
84
  search_index (Union[str, SearchIndex], optional): Search index configuration, either as index name or SearchIndex definition.
@@ -96,6 +100,9 @@ class CouchbaseSearch(VectorDb):
96
100
  self.overwrite = overwrite
97
101
  self.is_global_level_index = is_global_level_index
98
102
  self.wait_until_index_ready = wait_until_index_ready
103
+ # Initialize base class with name and description
104
+ super().__init__(name=name, description=description)
105
+
99
106
  self.kwargs = kwargs
100
107
  self.batch_limit = batch_limit
101
108
  if isinstance(search_index, str):
@@ -1420,3 +1427,7 @@ class CouchbaseSearch(VectorDb):
1420
1427
  except Exception as e:
1421
1428
  logger.error(f"Error updating metadata for content_id '{content_id}': {e}")
1422
1429
  raise
1430
+
1431
+ def get_supported_search_types(self) -> List[str]:
1432
+ """Get the supported search types for this vector database."""
1433
+ return [] # CouchbaseSearch doesn't use SearchType enum
@@ -25,6 +25,8 @@ class LanceDb(VectorDb):
25
25
 
26
26
  Args:
27
27
  uri: The URI of the LanceDB database.
28
+ name: Name of the vector database.
29
+ description: Description of the vector database.
28
30
  connection: The LanceDB connection to use.
29
31
  table: The LanceDB table instance to use.
30
32
  async_connection: The LanceDB async connection to use.
@@ -44,6 +46,9 @@ class LanceDb(VectorDb):
44
46
  def __init__(
45
47
  self,
46
48
  uri: lancedb.URI = "/tmp/lancedb",
49
+ name: Optional[str] = None,
50
+ description: Optional[str] = None,
51
+ id: Optional[str] = None,
47
52
  connection: Optional[lancedb.LanceDBConnection] = None,
48
53
  table: Optional[lancedb.db.LanceTable] = None,
49
54
  async_connection: Optional[lancedb.AsyncConnection] = None,
@@ -59,6 +64,17 @@ class LanceDb(VectorDb):
59
64
  on_bad_vectors: Optional[str] = None, # One of "error", "drop", "fill", "null".
60
65
  fill_value: Optional[float] = None, # Only used if on_bad_vectors is "fill"
61
66
  ):
67
+ # Dynamic ID generation based on unique identifiers
68
+ if id is None:
69
+ from agno.utils.string import generate_id
70
+
71
+ table_identifier = table_name or "default_table"
72
+ seed = f"{uri}#{table_identifier}"
73
+ id = generate_id(seed)
74
+
75
+ # Initialize base class with name, description, and generated ID
76
+ super().__init__(id=id, name=name, description=description)
77
+
62
78
  # Embedder for embedding the document contents
63
79
  if embedder is None:
64
80
  from agno.knowledge.embedder.openai import OpenAIEmbedder
@@ -1048,3 +1064,7 @@ class LanceDb(VectorDb):
1048
1064
  except Exception as e:
1049
1065
  logger.error(f"Error updating metadata for content_id '{content_id}': {e}")
1050
1066
  raise
1067
+
1068
+ def get_supported_search_types(self) -> List[str]:
1069
+ """Get the supported search types for this vector database."""
1070
+ return [SearchType.vector, SearchType.keyword, SearchType.hybrid]
@@ -11,16 +11,23 @@ class LangChainVectorDb(VectorDb):
11
11
  vectorstore: Optional[Any] = None,
12
12
  search_kwargs: Optional[dict] = None,
13
13
  knowledge_retriever: Optional[Any] = None,
14
+ name: Optional[str] = None,
15
+ description: Optional[str] = None,
14
16
  ):
15
17
  """
16
18
  Initialize LangChainVectorDb.
17
19
 
18
20
  Args:
19
21
  vectorstore: The LangChain vectorstore instance
22
+ name (Optional[str]): Name of the vector database.
23
+ description (Optional[str]): Description of the vector database.
20
24
  search_kwargs: Additional search parameters for the retriever
21
25
  knowledge_retriever: An optional LangChain retriever instance
22
26
  """
23
27
  self.vectorstore = vectorstore
28
+ # Initialize base class with name and description
29
+ super().__init__(name=name, description=description)
30
+
24
31
  self.search_kwargs = search_kwargs
25
32
  self.knowledge_retriever = knowledge_retriever
26
33
 
@@ -141,3 +148,7 @@ class LangChainVectorDb(VectorDb):
141
148
  metadata (Dict[str, Any]): The metadata to update
142
149
  """
143
150
  raise NotImplementedError("update_metadata not supported for LangChain vectorstores")
151
+
152
+ def get_supported_search_types(self) -> List[str]:
153
+ """Get the supported search types for this vector database."""
154
+ return [] # LangChainVectorDb doesn't use SearchType enum
@@ -21,9 +21,14 @@ class LightRag(VectorDb):
21
21
  api_key: Optional[str] = None,
22
22
  auth_header_name: str = "X-API-KEY",
23
23
  auth_header_format: str = "{api_key}",
24
+ name: Optional[str] = None,
25
+ description: Optional[str] = None,
24
26
  ):
25
27
  self.server_url = server_url
26
28
  self.api_key = api_key
29
+ # Initialize base class with name and description
30
+ super().__init__(name=name, description=description)
31
+
27
32
  self.auth_header_name = auth_header_name
28
33
  self.auth_header_format = auth_header_format
29
34
 
@@ -372,3 +377,7 @@ class LightRag(VectorDb):
372
377
  metadata (Dict[str, Any]): The metadata to update
373
378
  """
374
379
  raise NotImplementedError("update_metadata not supported for LightRag - use LightRag's native methods")
380
+
381
+ def get_supported_search_types(self) -> List[str]:
382
+ """Get the supported search types for this vector database."""
383
+ return [] # LightRag doesn't use SearchType enum
@@ -17,8 +17,18 @@ class LlamaIndexVectorDb(VectorDb):
17
17
  knowledge_retriever: BaseRetriever
18
18
  loader: Optional[Callable] = None
19
19
 
20
- def __init__(self, knowledge_retriever: BaseRetriever, loader: Optional[Callable] = None, **kwargs):
20
+ def __init__(
21
+ self,
22
+ knowledge_retriever: BaseRetriever,
23
+ loader: Optional[Callable] = None,
24
+ name: Optional[str] = None,
25
+ description: Optional[str] = None,
26
+ **kwargs,
27
+ ):
21
28
  super().__init__(**kwargs)
29
+ # Initialize base class with name and description
30
+ super().__init__(name=name, description=description)
31
+
22
32
  self.knowledge_retriever = knowledge_retriever
23
33
  self.loader = loader
24
34
 
@@ -144,3 +154,7 @@ class LlamaIndexVectorDb(VectorDb):
144
154
  "LlamaIndexVectorDb.delete_by_content_id() not supported - please check the vectorstore manually."
145
155
  )
146
156
  return False
157
+
158
+ def get_supported_search_types(self) -> List[str]:
159
+ """Get the supported search types for this vector database."""
160
+ return [] # LlamaIndexVectorDb doesn't use SearchType enum
@@ -28,6 +28,9 @@ class Milvus(VectorDb):
28
28
  def __init__(
29
29
  self,
30
30
  collection: str,
31
+ name: Optional[str] = None,
32
+ description: Optional[str] = None,
33
+ id: Optional[str] = None,
31
34
  embedder: Optional[Embedder] = None,
32
35
  distance: Distance = Distance.cosine,
33
36
  uri: str = "http://localhost:19530",
@@ -42,6 +45,8 @@ class Milvus(VectorDb):
42
45
 
43
46
  Args:
44
47
  collection (str): Name of the Milvus collection.
48
+ name (Optional[str]): Name of the vector database.
49
+ description (Optional[str]): Description of the vector database.
45
50
  embedder (Embedder): Embedder to use for embedding documents.
46
51
  distance (Distance): Distance metric to use for vector similarity.
47
52
  uri (Optional[str]): URI of the Milvus server.
@@ -63,6 +68,20 @@ class Milvus(VectorDb):
63
68
  reranker (Optional[Reranker]): Reranker to use for hybrid search results
64
69
  **kwargs: Additional keyword arguments to pass to the MilvusClient.
65
70
  """
71
+ # Validate required parameters
72
+ if not collection:
73
+ raise ValueError("Collection name must be provided.")
74
+
75
+ # Dynamic ID generation based on unique identifiers
76
+ if id is None:
77
+ from agno.utils.string import generate_id
78
+
79
+ seed = f"{uri or 'milvus'}#{collection}"
80
+ id = generate_id(seed)
81
+
82
+ # Initialize base class with name, description, and generated ID
83
+ super().__init__(id=id, name=name, description=description)
84
+
66
85
  self.collection: str = collection
67
86
 
68
87
  if embedder is None:
@@ -1141,3 +1160,7 @@ class Milvus(VectorDb):
1141
1160
  except Exception as e:
1142
1161
  log_error(f"Error updating metadata for content_id '{content_id}': {e}")
1143
1162
  raise
1163
+
1164
+ def get_supported_search_types(self) -> List[str]:
1165
+ """Get the supported search types for this vector database."""
1166
+ return [SearchType.vector, SearchType.hybrid]
@@ -33,6 +33,9 @@ class MongoDb(VectorDb):
33
33
  def __init__(
34
34
  self,
35
35
  collection_name: str,
36
+ name: Optional[str] = None,
37
+ description: Optional[str] = None,
38
+ id: Optional[str] = None,
36
39
  db_url: Optional[str] = "mongodb://localhost:27017/",
37
40
  database: str = "agno",
38
41
  embedder: Optional[Embedder] = None,
@@ -56,6 +59,8 @@ class MongoDb(VectorDb):
56
59
 
57
60
  Args:
58
61
  collection_name (str): Name of the MongoDB collection.
62
+ name (Optional[str]): Name of the vector database.
63
+ description (Optional[str]): Description of the vector database.
59
64
  db_url (Optional[str]): MongoDB connection string.
60
65
  database (str): Database name.
61
66
  embedder (Embedder): Embedder instance for generating embeddings.
@@ -74,11 +79,24 @@ class MongoDb(VectorDb):
74
79
  hybrid_rank_constant (int): Default rank constant (k) for Reciprocal Rank Fusion in hybrid search. This constant is added to the rank before taking the reciprocal, helping to smooth scores. A common value is 60.
75
80
  **kwargs: Additional arguments for MongoClient.
76
81
  """
82
+ # Validate required parameters
77
83
  if not collection_name:
78
84
  raise ValueError("Collection name must not be empty.")
79
85
  if not database:
80
86
  raise ValueError("Database name must not be empty.")
87
+
88
+ # Dynamic ID generation based on unique identifiers
89
+ if id is None:
90
+ from agno.utils.string import generate_id
91
+
92
+ connection_identifier = db_url or "mongodb://localhost:27017/"
93
+ seed = f"{connection_identifier}#{database}#{collection_name}"
94
+ id = generate_id(seed)
95
+
81
96
  self.collection_name = collection_name
97
+ # Initialize base class with name, description, and generated ID
98
+ super().__init__(id=id, name=name, description=description)
99
+
82
100
  self.database = database
83
101
  self.search_index_name = search_index_name
84
102
  self.cosmos_compatibility = cosmos_compatibility
@@ -1382,3 +1400,7 @@ class MongoDb(VectorDb):
1382
1400
  except Exception as e:
1383
1401
  logger.error(f"Error updating metadata for content_id '{content_id}': {e}")
1384
1402
  raise
1403
+
1404
+ def get_supported_search_types(self) -> List[str]:
1405
+ """Get the supported search types for this vector database."""
1406
+ return [SearchType.vector, SearchType.hybrid]
@@ -3,6 +3,8 @@ from hashlib import md5
3
3
  from math import sqrt
4
4
  from typing import Any, Dict, List, Optional, Union, cast
5
5
 
6
+ from agno.utils.string import generate_id
7
+
6
8
  try:
7
9
  from sqlalchemy import update
8
10
  from sqlalchemy.dialects import postgresql
@@ -43,6 +45,9 @@ class PgVector(VectorDb):
43
45
  self,
44
46
  table_name: str,
45
47
  schema: str = "ai",
48
+ name: Optional[str] = None,
49
+ description: Optional[str] = None,
50
+ id: Optional[str] = None,
46
51
  db_url: Optional[str] = None,
47
52
  db_engine: Optional[Engine] = None,
48
53
  embedder: Optional[Embedder] = None,
@@ -62,6 +67,8 @@ class PgVector(VectorDb):
62
67
  Args:
63
68
  table_name (str): Name of the table to store vector data.
64
69
  schema (str): Database schema name.
70
+ name (Optional[str]): Name of the vector database.
71
+ description (Optional[str]): Description of the vector database.
65
72
  db_url (Optional[str]): Database connection URL.
66
73
  db_engine (Optional[Engine]): SQLAlchemy database engine.
67
74
  embedder (Optional[Embedder]): Embedder instance for creating embeddings.
@@ -80,6 +87,15 @@ class PgVector(VectorDb):
80
87
  if db_engine is None and db_url is None:
81
88
  raise ValueError("Either 'db_url' or 'db_engine' must be provided.")
82
89
 
90
+ if id is None:
91
+ base_seed = db_url or str(db_engine.url) # type: ignore
92
+ schema_suffix = table_name if table_name is not None else "ai"
93
+ seed = f"{base_seed}#{schema_suffix}"
94
+ id = generate_id(seed)
95
+
96
+ # Initialize base class with name and description
97
+ super().__init__(id=id, name=name, description=description)
98
+
83
99
  if db_engine is None:
84
100
  if db_url is None:
85
101
  raise ValueError("Must provide 'db_url' if 'db_engine' is None.")
@@ -1383,3 +1399,6 @@ class PgVector(VectorDb):
1383
1399
  copied_obj.table = copied_obj.get_table()
1384
1400
 
1385
1401
  return copied_obj
1402
+
1403
+ def get_supported_search_types(self) -> List[str]:
1404
+ return [SearchType.vector, SearchType.keyword, SearchType.hybrid]