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.
- agno/agent/agent.py +1767 -535
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/async_postgres/async_postgres.py +1668 -0
- agno/db/async_postgres/schemas.py +124 -0
- agno/db/async_postgres/utils.py +289 -0
- agno/db/base.py +237 -2
- agno/db/dynamo/dynamo.py +2 -2
- agno/db/firestore/firestore.py +2 -2
- agno/db/firestore/utils.py +4 -2
- agno/db/gcs_json/gcs_json_db.py +2 -2
- agno/db/in_memory/in_memory_db.py +2 -2
- agno/db/json/json_db.py +2 -2
- agno/db/migrations/v1_to_v2.py +30 -13
- agno/db/mongo/mongo.py +18 -6
- agno/db/mysql/mysql.py +35 -13
- agno/db/postgres/postgres.py +29 -6
- agno/db/redis/redis.py +2 -2
- agno/db/singlestore/singlestore.py +2 -2
- agno/db/sqlite/sqlite.py +34 -12
- agno/db/sqlite/utils.py +8 -3
- agno/eval/accuracy.py +50 -43
- agno/eval/performance.py +6 -3
- agno/eval/reliability.py +6 -3
- agno/eval/utils.py +33 -16
- agno/exceptions.py +8 -2
- agno/knowledge/knowledge.py +260 -46
- agno/knowledge/reader/pdf_reader.py +4 -6
- agno/knowledge/reader/reader_factory.py +2 -3
- agno/memory/manager.py +241 -33
- agno/models/anthropic/claude.py +37 -0
- agno/os/app.py +8 -7
- agno/os/interfaces/a2a/router.py +3 -5
- agno/os/interfaces/agui/router.py +4 -1
- agno/os/interfaces/agui/utils.py +27 -6
- agno/os/interfaces/slack/router.py +2 -4
- agno/os/mcp.py +98 -41
- agno/os/router.py +23 -0
- agno/os/routers/evals/evals.py +52 -20
- agno/os/routers/evals/utils.py +14 -14
- agno/os/routers/knowledge/knowledge.py +130 -9
- agno/os/routers/knowledge/schemas.py +57 -0
- agno/os/routers/memory/memory.py +116 -44
- agno/os/routers/metrics/metrics.py +16 -6
- agno/os/routers/session/session.py +65 -22
- agno/os/schema.py +36 -0
- agno/os/utils.py +67 -12
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/openai.py +5 -0
- agno/reasoning/vertexai.py +76 -0
- agno/session/workflow.py +3 -3
- agno/team/team.py +918 -175
- agno/tools/googlesheets.py +20 -5
- agno/tools/mcp_toolbox.py +3 -3
- agno/tools/scrapegraph.py +1 -1
- agno/utils/models/claude.py +3 -1
- agno/utils/streamlit.py +1 -1
- agno/vectordb/base.py +22 -1
- agno/vectordb/cassandra/cassandra.py +9 -0
- agno/vectordb/chroma/chromadb.py +26 -6
- agno/vectordb/clickhouse/clickhousedb.py +9 -1
- agno/vectordb/couchbase/couchbase.py +11 -0
- agno/vectordb/lancedb/lance_db.py +20 -0
- agno/vectordb/langchaindb/langchaindb.py +11 -0
- agno/vectordb/lightrag/lightrag.py +9 -0
- agno/vectordb/llamaindex/llamaindexdb.py +15 -1
- agno/vectordb/milvus/milvus.py +23 -0
- agno/vectordb/mongodb/mongodb.py +22 -0
- agno/vectordb/pgvector/pgvector.py +19 -0
- agno/vectordb/pineconedb/pineconedb.py +35 -4
- agno/vectordb/qdrant/qdrant.py +24 -0
- agno/vectordb/singlestore/singlestore.py +25 -17
- agno/vectordb/surrealdb/surrealdb.py +18 -1
- agno/vectordb/upstashdb/upstashdb.py +26 -1
- agno/vectordb/weaviate/weaviate.py +18 -0
- agno/workflow/condition.py +4 -0
- agno/workflow/loop.py +4 -0
- agno/workflow/parallel.py +4 -0
- agno/workflow/router.py +4 -0
- agno/workflow/step.py +22 -14
- agno/workflow/steps.py +4 -0
- agno/workflow/types.py +2 -2
- agno/workflow/workflow.py +328 -61
- {agno-2.1.4.dist-info → agno-2.1.5.dist-info}/METADATA +100 -41
- {agno-2.1.4.dist-info → agno-2.1.5.dist-info}/RECORD +88 -81
- {agno-2.1.4.dist-info → agno-2.1.5.dist-info}/WHEEL +0 -0
- {agno-2.1.4.dist-info → agno-2.1.5.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.4.dist-info → agno-2.1.5.dist-info}/top_level.txt +0 -0
agno/tools/googlesheets.py
CHANGED
|
@@ -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
|
|
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)}"
|
agno/utils/models/claude.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
agno/vectordb/chroma/chromadb.py
CHANGED
|
@@ -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__(
|
|
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
|
agno/vectordb/milvus/milvus.py
CHANGED
|
@@ -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]
|
agno/vectordb/mongodb/mongodb.py
CHANGED
|
@@ -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]
|