agno 2.1.2__py3-none-any.whl → 2.3.13__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 +5540 -2273
- agno/api/api.py +2 -0
- agno/api/os.py +1 -1
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +689 -6
- agno/db/dynamo/dynamo.py +933 -37
- agno/db/dynamo/schemas.py +174 -10
- agno/db/dynamo/utils.py +63 -4
- agno/db/firestore/firestore.py +831 -9
- agno/db/firestore/schemas.py +51 -0
- agno/db/firestore/utils.py +102 -4
- agno/db/gcs_json/gcs_json_db.py +660 -12
- agno/db/gcs_json/utils.py +60 -26
- agno/db/in_memory/in_memory_db.py +287 -14
- agno/db/in_memory/utils.py +60 -2
- agno/db/json/json_db.py +590 -14
- agno/db/json/utils.py +60 -26
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +43 -13
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +15 -1
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +879 -11
- agno/db/mongo/schemas.py +42 -0
- agno/db/mongo/utils.py +80 -8
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +946 -68
- agno/db/mysql/schemas.py +72 -10
- agno/db/mysql/utils.py +198 -7
- agno/db/postgres/__init__.py +2 -1
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +942 -57
- agno/db/postgres/schemas.py +81 -18
- agno/db/postgres/utils.py +164 -2
- agno/db/redis/redis.py +671 -7
- agno/db/redis/schemas.py +50 -0
- agno/db/redis/utils.py +65 -7
- agno/db/schemas/__init__.py +2 -1
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +17 -2
- agno/db/singlestore/schemas.py +63 -0
- agno/db/singlestore/singlestore.py +949 -83
- agno/db/singlestore/utils.py +60 -2
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +62 -0
- agno/db/sqlite/sqlite.py +965 -46
- agno/db/sqlite/utils.py +169 -8
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +2 -0
- agno/eval/__init__.py +10 -0
- agno/eval/accuracy.py +75 -55
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +16 -7
- agno/eval/reliability.py +28 -16
- agno/eval/utils.py +35 -17
- agno/exceptions.py +27 -2
- agno/filters.py +354 -0
- agno/guardrails/prompt_injection.py +1 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +1 -1
- agno/knowledge/chunking/agentic.py +13 -10
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/semantic.py +9 -4
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/ollama.py +8 -0
- agno/knowledge/embedder/openai.py +8 -8
- agno/knowledge/embedder/sentence_transformer.py +6 -2
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/knowledge.py +1618 -318
- agno/knowledge/reader/base.py +6 -2
- agno/knowledge/reader/csv_reader.py +8 -10
- agno/knowledge/reader/docx_reader.py +5 -6
- agno/knowledge/reader/field_labeled_csv_reader.py +16 -20
- agno/knowledge/reader/json_reader.py +5 -4
- agno/knowledge/reader/markdown_reader.py +8 -8
- agno/knowledge/reader/pdf_reader.py +17 -19
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +32 -3
- agno/knowledge/reader/s3_reader.py +3 -3
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +22 -10
- agno/knowledge/reader/web_search_reader.py +1 -48
- agno/knowledge/reader/website_reader.py +10 -10
- agno/knowledge/reader/wikipedia_reader.py +33 -1
- agno/knowledge/types.py +1 -0
- agno/knowledge/utils.py +72 -7
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +544 -83
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/aimlapi.py +17 -0
- agno/models/anthropic/claude.py +515 -40
- agno/models/aws/bedrock.py +102 -21
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +41 -19
- agno/models/azure/openai_chat.py +39 -8
- agno/models/base.py +1249 -525
- agno/models/cerebras/cerebras.py +91 -21
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +40 -6
- agno/models/cometapi/cometapi.py +18 -1
- agno/models/dashscope/dashscope.py +2 -3
- agno/models/deepinfra/deepinfra.py +18 -1
- agno/models/deepseek/deepseek.py +69 -3
- agno/models/fireworks/fireworks.py +18 -1
- agno/models/google/gemini.py +877 -80
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +51 -18
- agno/models/huggingface/huggingface.py +17 -6
- agno/models/ibm/watsonx.py +16 -6
- agno/models/internlm/internlm.py +18 -1
- agno/models/langdb/langdb.py +13 -1
- agno/models/litellm/chat.py +44 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +28 -5
- agno/models/meta/llama.py +47 -14
- agno/models/meta/llama_openai.py +22 -17
- agno/models/mistral/mistral.py +8 -4
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/chat.py +24 -8
- agno/models/openai/chat.py +104 -29
- agno/models/openai/responses.py +101 -81
- agno/models/openrouter/openrouter.py +60 -3
- agno/models/perplexity/perplexity.py +17 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +24 -4
- agno/models/response.py +73 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/utils.py +254 -8
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +549 -152
- agno/os/auth.py +190 -3
- agno/os/config.py +23 -0
- agno/os/interfaces/a2a/router.py +8 -11
- agno/os/interfaces/a2a/utils.py +1 -1
- agno/os/interfaces/agui/router.py +18 -3
- agno/os/interfaces/agui/utils.py +152 -39
- agno/os/interfaces/slack/router.py +55 -37
- agno/os/interfaces/slack/slack.py +9 -1
- agno/os/interfaces/whatsapp/router.py +0 -1
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/mcp.py +110 -52
- agno/os/middleware/__init__.py +2 -0
- agno/os/middleware/jwt.py +676 -112
- agno/os/router.py +40 -1478
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/evals.py +96 -39
- agno/os/routers/evals/schemas.py +65 -33
- agno/os/routers/evals/utils.py +80 -10
- agno/os/routers/health.py +10 -4
- agno/os/routers/knowledge/knowledge.py +196 -38
- agno/os/routers/knowledge/schemas.py +82 -22
- agno/os/routers/memory/memory.py +279 -52
- agno/os/routers/memory/schemas.py +46 -17
- agno/os/routers/metrics/metrics.py +20 -8
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +462 -34
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +256 -693
- agno/os/scopes.py +469 -0
- agno/os/utils.py +514 -36
- 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/run/__init__.py +6 -0
- agno/run/agent.py +155 -32
- agno/run/base.py +55 -3
- agno/run/requirement.py +181 -0
- agno/run/team.py +125 -38
- agno/run/workflow.py +72 -18
- agno/session/agent.py +102 -89
- agno/session/summary.py +56 -15
- agno/session/team.py +164 -90
- agno/session/workflow.py +405 -40
- agno/table.py +10 -0
- agno/team/team.py +3974 -1903
- agno/tools/dalle.py +2 -4
- agno/tools/eleven_labs.py +23 -25
- agno/tools/exa.py +21 -16
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +16 -10
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +193 -38
- agno/tools/gmail.py +238 -14
- agno/tools/google_drive.py +271 -0
- agno/tools/googlecalendar.py +36 -8
- agno/tools/googlesheets.py +20 -5
- agno/tools/jira.py +20 -0
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +3 -3
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/notion.py +204 -0
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +76 -36
- agno/tools/redshift.py +406 -0
- agno/tools/scrapegraph.py +1 -1
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +18 -3
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +146 -0
- agno/tools/toolkit.py +25 -0
- agno/tools/workflow.py +8 -1
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +151 -3
- agno/utils/gemini.py +15 -5
- agno/utils/hooks.py +118 -4
- agno/utils/http.py +113 -2
- agno/utils/knowledge.py +12 -5
- agno/utils/log.py +1 -0
- agno/utils/mcp.py +92 -2
- agno/utils/media.py +187 -1
- agno/utils/merge_dict.py +3 -3
- agno/utils/message.py +60 -0
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +49 -14
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/print_response/agent.py +109 -16
- agno/utils/print_response/team.py +223 -30
- agno/utils/print_response/workflow.py +251 -34
- agno/utils/streamlit.py +1 -1
- agno/utils/team.py +98 -9
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +39 -7
- agno/vectordb/cassandra/cassandra.py +21 -5
- agno/vectordb/chroma/chromadb.py +43 -12
- agno/vectordb/clickhouse/clickhousedb.py +21 -5
- agno/vectordb/couchbase/couchbase.py +29 -5
- agno/vectordb/lancedb/lance_db.py +92 -181
- agno/vectordb/langchaindb/langchaindb.py +24 -4
- agno/vectordb/lightrag/lightrag.py +17 -3
- agno/vectordb/llamaindex/llamaindexdb.py +25 -5
- agno/vectordb/milvus/milvus.py +50 -37
- agno/vectordb/mongodb/__init__.py +7 -1
- agno/vectordb/mongodb/mongodb.py +36 -30
- agno/vectordb/pgvector/pgvector.py +201 -77
- agno/vectordb/pineconedb/pineconedb.py +41 -23
- agno/vectordb/qdrant/qdrant.py +67 -54
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/singlestore.py +50 -29
- agno/vectordb/surrealdb/surrealdb.py +31 -41
- agno/vectordb/upstashdb/upstashdb.py +34 -6
- agno/vectordb/weaviate/weaviate.py +53 -14
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +120 -18
- agno/workflow/loop.py +77 -10
- agno/workflow/parallel.py +231 -143
- agno/workflow/router.py +118 -17
- agno/workflow/step.py +609 -170
- agno/workflow/steps.py +73 -6
- agno/workflow/types.py +96 -21
- agno/workflow/workflow.py +2039 -262
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/METADATA +201 -66
- agno-2.3.13.dist-info/RECORD +613 -0
- agno/tools/googlesearch.py +0 -98
- agno/tools/mcp.py +0 -679
- agno/tools/memori.py +0 -339
- agno-2.1.2.dist-info/RECORD +0 -543
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +0 -0
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/vectordb/milvus/milvus.py
CHANGED
|
@@ -9,10 +9,11 @@ try:
|
|
|
9
9
|
except ImportError:
|
|
10
10
|
raise ImportError("The `pymilvus` package is not installed. Please install it via `pip install pymilvus`.")
|
|
11
11
|
|
|
12
|
+
from agno.filters import FilterExpr
|
|
12
13
|
from agno.knowledge.document import Document
|
|
13
14
|
from agno.knowledge.embedder import Embedder
|
|
14
15
|
from agno.knowledge.reranker.base import Reranker
|
|
15
|
-
from agno.utils.log import log_debug, log_error, log_info
|
|
16
|
+
from agno.utils.log import log_debug, log_error, log_info, log_warning
|
|
16
17
|
from agno.vectordb.base import VectorDb
|
|
17
18
|
from agno.vectordb.distance import Distance
|
|
18
19
|
from agno.vectordb.search import SearchType
|
|
@@ -28,6 +29,9 @@ class Milvus(VectorDb):
|
|
|
28
29
|
def __init__(
|
|
29
30
|
self,
|
|
30
31
|
collection: str,
|
|
32
|
+
name: Optional[str] = None,
|
|
33
|
+
description: Optional[str] = None,
|
|
34
|
+
id: Optional[str] = None,
|
|
31
35
|
embedder: Optional[Embedder] = None,
|
|
32
36
|
distance: Distance = Distance.cosine,
|
|
33
37
|
uri: str = "http://localhost:19530",
|
|
@@ -42,6 +46,8 @@ class Milvus(VectorDb):
|
|
|
42
46
|
|
|
43
47
|
Args:
|
|
44
48
|
collection (str): Name of the Milvus collection.
|
|
49
|
+
name (Optional[str]): Name of the vector database.
|
|
50
|
+
description (Optional[str]): Description of the vector database.
|
|
45
51
|
embedder (Embedder): Embedder to use for embedding documents.
|
|
46
52
|
distance (Distance): Distance metric to use for vector similarity.
|
|
47
53
|
uri (Optional[str]): URI of the Milvus server.
|
|
@@ -63,6 +69,20 @@ class Milvus(VectorDb):
|
|
|
63
69
|
reranker (Optional[Reranker]): Reranker to use for hybrid search results
|
|
64
70
|
**kwargs: Additional keyword arguments to pass to the MilvusClient.
|
|
65
71
|
"""
|
|
72
|
+
# Validate required parameters
|
|
73
|
+
if not collection:
|
|
74
|
+
raise ValueError("Collection name must be provided.")
|
|
75
|
+
|
|
76
|
+
# Dynamic ID generation based on unique identifiers
|
|
77
|
+
if id is None:
|
|
78
|
+
from agno.utils.string import generate_id
|
|
79
|
+
|
|
80
|
+
seed = f"{uri or 'milvus'}#{collection}"
|
|
81
|
+
id = generate_id(seed)
|
|
82
|
+
|
|
83
|
+
# Initialize base class with name, description, and generated ID
|
|
84
|
+
super().__init__(id=id, name=name, description=description)
|
|
85
|
+
|
|
66
86
|
self.collection: str = collection
|
|
67
87
|
|
|
68
88
|
if embedder is None:
|
|
@@ -209,7 +229,9 @@ class Milvus(VectorDb):
|
|
|
209
229
|
"""
|
|
210
230
|
|
|
211
231
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
212
|
-
|
|
232
|
+
# Include content_hash in ID to ensure uniqueness across different content hashes
|
|
233
|
+
base_id = document.id or md5(cleaned_content.encode()).hexdigest()
|
|
234
|
+
doc_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
|
|
213
235
|
|
|
214
236
|
# Convert dictionary fields to JSON strings
|
|
215
237
|
meta_data_str = json.dumps(document.meta_data) if document.meta_data else "{}"
|
|
@@ -297,36 +319,6 @@ class Milvus(VectorDb):
|
|
|
297
319
|
max_length=65_535,
|
|
298
320
|
)
|
|
299
321
|
|
|
300
|
-
def doc_exists(self, document: Document) -> bool:
|
|
301
|
-
"""
|
|
302
|
-
Validating if the document exists or not
|
|
303
|
-
|
|
304
|
-
Args:
|
|
305
|
-
document (Document): Document to validate
|
|
306
|
-
"""
|
|
307
|
-
if self.client:
|
|
308
|
-
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
309
|
-
doc_id = md5(cleaned_content.encode()).hexdigest()
|
|
310
|
-
collection_points = self.client.get(
|
|
311
|
-
collection_name=self.collection,
|
|
312
|
-
ids=[doc_id],
|
|
313
|
-
)
|
|
314
|
-
return len(collection_points) > 0
|
|
315
|
-
return False
|
|
316
|
-
|
|
317
|
-
async def async_doc_exists(self, document: Document) -> bool:
|
|
318
|
-
"""
|
|
319
|
-
Check if document exists asynchronously.
|
|
320
|
-
AsyncMilvusClient supports get().
|
|
321
|
-
"""
|
|
322
|
-
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
323
|
-
doc_id = md5(cleaned_content.encode()).hexdigest()
|
|
324
|
-
collection_points = await self.async_client.get(
|
|
325
|
-
collection_name=self.collection,
|
|
326
|
-
ids=[doc_id],
|
|
327
|
-
)
|
|
328
|
-
return len(collection_points) > 0
|
|
329
|
-
|
|
330
322
|
def name_exists(self, name: str) -> bool:
|
|
331
323
|
"""
|
|
332
324
|
Validates if a document with the given name exists in the collection.
|
|
@@ -508,7 +500,9 @@ class Milvus(VectorDb):
|
|
|
508
500
|
log_debug(f"Skipping document without embedding: {document.name} ({document.meta_data})")
|
|
509
501
|
return None
|
|
510
502
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
511
|
-
|
|
503
|
+
# Include content_hash in ID to ensure uniqueness across different content hashes
|
|
504
|
+
base_id = document.id or md5(cleaned_content.encode()).hexdigest()
|
|
505
|
+
doc_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
|
|
512
506
|
|
|
513
507
|
meta_data = document.meta_data or {}
|
|
514
508
|
if filters:
|
|
@@ -656,7 +650,9 @@ class Milvus(VectorDb):
|
|
|
656
650
|
"""
|
|
657
651
|
return MILVUS_DISTANCE_MAP.get(self.distance, "COSINE")
|
|
658
652
|
|
|
659
|
-
def search(
|
|
653
|
+
def search(
|
|
654
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
655
|
+
) -> List[Document]:
|
|
660
656
|
"""
|
|
661
657
|
Search for documents matching the query.
|
|
662
658
|
|
|
@@ -668,6 +664,9 @@ class Milvus(VectorDb):
|
|
|
668
664
|
Returns:
|
|
669
665
|
List[Document]: List of matching documents
|
|
670
666
|
"""
|
|
667
|
+
if isinstance(filters, List):
|
|
668
|
+
log_warning("Filters Expressions are not supported in Milvus. No filters will be applied.")
|
|
669
|
+
filters = None
|
|
671
670
|
if self.search_type == SearchType.hybrid:
|
|
672
671
|
return self.hybrid_search(query, limit)
|
|
673
672
|
|
|
@@ -700,12 +699,20 @@ class Milvus(VectorDb):
|
|
|
700
699
|
)
|
|
701
700
|
)
|
|
702
701
|
|
|
702
|
+
# Apply reranker if available
|
|
703
|
+
if self.reranker and search_results:
|
|
704
|
+
search_results = self.reranker.rerank(query=query, documents=search_results)
|
|
705
|
+
search_results = search_results[:limit]
|
|
706
|
+
|
|
703
707
|
log_info(f"Found {len(search_results)} documents")
|
|
704
708
|
return search_results
|
|
705
709
|
|
|
706
710
|
async def async_search(
|
|
707
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
711
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
708
712
|
) -> List[Document]:
|
|
713
|
+
if isinstance(filters, List):
|
|
714
|
+
log_warning("Filters Expressions are not supported in Milvus. No filters will be applied.")
|
|
715
|
+
filters = None
|
|
709
716
|
if self.search_type == SearchType.hybrid:
|
|
710
717
|
return await self.async_hybrid_search(query, limit, filters)
|
|
711
718
|
|
|
@@ -741,7 +748,9 @@ class Milvus(VectorDb):
|
|
|
741
748
|
log_info(f"Found {len(search_results)} documents")
|
|
742
749
|
return search_results
|
|
743
750
|
|
|
744
|
-
def hybrid_search(
|
|
751
|
+
def hybrid_search(
|
|
752
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
753
|
+
) -> List[Document]:
|
|
745
754
|
"""
|
|
746
755
|
Perform a hybrid search combining dense and sparse vector similarity.
|
|
747
756
|
|
|
@@ -833,7 +842,7 @@ class Milvus(VectorDb):
|
|
|
833
842
|
return []
|
|
834
843
|
|
|
835
844
|
async def async_hybrid_search(
|
|
836
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
845
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
837
846
|
) -> List[Document]:
|
|
838
847
|
"""
|
|
839
848
|
Perform an asynchronous hybrid search combining dense and sparse vector similarity.
|
|
@@ -1141,3 +1150,7 @@ class Milvus(VectorDb):
|
|
|
1141
1150
|
except Exception as e:
|
|
1142
1151
|
log_error(f"Error updating metadata for content_id '{content_id}': {e}")
|
|
1143
1152
|
raise
|
|
1153
|
+
|
|
1154
|
+
def get_supported_search_types(self) -> List[str]:
|
|
1155
|
+
"""Get the supported search types for this vector database."""
|
|
1156
|
+
return [SearchType.vector, SearchType.hybrid]
|
agno/vectordb/mongodb/mongodb.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import time
|
|
3
|
-
from typing import Any, Dict, List, Optional
|
|
3
|
+
from typing import Any, Dict, List, Optional, Union
|
|
4
4
|
|
|
5
5
|
from bson import ObjectId
|
|
6
6
|
|
|
7
|
+
from agno.filters import FilterExpr
|
|
7
8
|
from agno.knowledge.document import Document
|
|
8
9
|
from agno.knowledge.embedder import Embedder
|
|
9
10
|
from agno.utils.log import log_debug, log_info, log_warning, logger
|
|
@@ -33,6 +34,9 @@ class MongoDb(VectorDb):
|
|
|
33
34
|
def __init__(
|
|
34
35
|
self,
|
|
35
36
|
collection_name: str,
|
|
37
|
+
name: Optional[str] = None,
|
|
38
|
+
description: Optional[str] = None,
|
|
39
|
+
id: Optional[str] = None,
|
|
36
40
|
db_url: Optional[str] = "mongodb://localhost:27017/",
|
|
37
41
|
database: str = "agno",
|
|
38
42
|
embedder: Optional[Embedder] = None,
|
|
@@ -56,6 +60,8 @@ class MongoDb(VectorDb):
|
|
|
56
60
|
|
|
57
61
|
Args:
|
|
58
62
|
collection_name (str): Name of the MongoDB collection.
|
|
63
|
+
name (Optional[str]): Name of the vector database.
|
|
64
|
+
description (Optional[str]): Description of the vector database.
|
|
59
65
|
db_url (Optional[str]): MongoDB connection string.
|
|
60
66
|
database (str): Database name.
|
|
61
67
|
embedder (Embedder): Embedder instance for generating embeddings.
|
|
@@ -74,11 +80,24 @@ class MongoDb(VectorDb):
|
|
|
74
80
|
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
81
|
**kwargs: Additional arguments for MongoClient.
|
|
76
82
|
"""
|
|
83
|
+
# Validate required parameters
|
|
77
84
|
if not collection_name:
|
|
78
85
|
raise ValueError("Collection name must not be empty.")
|
|
79
86
|
if not database:
|
|
80
87
|
raise ValueError("Database name must not be empty.")
|
|
88
|
+
|
|
89
|
+
# Dynamic ID generation based on unique identifiers
|
|
90
|
+
if id is None:
|
|
91
|
+
from agno.utils.string import generate_id
|
|
92
|
+
|
|
93
|
+
connection_identifier = db_url or "mongodb://localhost:27017/"
|
|
94
|
+
seed = f"{connection_identifier}#{database}#{collection_name}"
|
|
95
|
+
id = generate_id(seed)
|
|
96
|
+
|
|
81
97
|
self.collection_name = collection_name
|
|
98
|
+
# Initialize base class with name, description, and generated ID
|
|
99
|
+
super().__init__(id=id, name=name, description=description)
|
|
100
|
+
|
|
82
101
|
self.database = database
|
|
83
102
|
self.search_index_name = search_index_name
|
|
84
103
|
self.cosmos_compatibility = cosmos_compatibility
|
|
@@ -452,20 +471,6 @@ class MongoDb(VectorDb):
|
|
|
452
471
|
if self.wait_until_index_ready_in_seconds:
|
|
453
472
|
await self._wait_for_index_ready_async()
|
|
454
473
|
|
|
455
|
-
def doc_exists(self, document: Document) -> bool:
|
|
456
|
-
"""Check if a document exists in the MongoDB collection based on its content."""
|
|
457
|
-
try:
|
|
458
|
-
collection = self._get_collection()
|
|
459
|
-
# Use content hash as document ID
|
|
460
|
-
doc_id = md5(document.content.encode("utf-8")).hexdigest()
|
|
461
|
-
result = collection.find_one({"_id": doc_id})
|
|
462
|
-
exists = result is not None
|
|
463
|
-
log_debug(f"Document {'exists' if exists else 'does not exist'}: {doc_id}")
|
|
464
|
-
return exists
|
|
465
|
-
except Exception as e:
|
|
466
|
-
logger.error(f"Error checking document existence: {e}")
|
|
467
|
-
return False
|
|
468
|
-
|
|
469
474
|
def name_exists(self, name: str) -> bool:
|
|
470
475
|
"""Check if a document with a given name exists in the collection."""
|
|
471
476
|
try:
|
|
@@ -567,9 +572,16 @@ class MongoDb(VectorDb):
|
|
|
567
572
|
return True
|
|
568
573
|
|
|
569
574
|
def search(
|
|
570
|
-
self,
|
|
575
|
+
self,
|
|
576
|
+
query: str,
|
|
577
|
+
limit: int = 5,
|
|
578
|
+
filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
|
|
579
|
+
min_score: float = 0.0,
|
|
571
580
|
) -> List[Document]:
|
|
572
581
|
"""Search for documents using vector similarity."""
|
|
582
|
+
if isinstance(filters, List):
|
|
583
|
+
log_warning("Filters Expressions are not supported in MongoDB. No filters will be applied.")
|
|
584
|
+
filters = None
|
|
573
585
|
if self.search_type == SearchType.hybrid:
|
|
574
586
|
return self.hybrid_search(query, limit=limit, filters=filters)
|
|
575
587
|
|
|
@@ -998,19 +1010,6 @@ class MongoDb(VectorDb):
|
|
|
998
1010
|
logger.error(f"Error getting document count: {e}")
|
|
999
1011
|
return 0
|
|
1000
1012
|
|
|
1001
|
-
async def async_doc_exists(self, document: Document) -> bool:
|
|
1002
|
-
"""Check if a document exists asynchronously."""
|
|
1003
|
-
try:
|
|
1004
|
-
collection = await self._get_async_collection()
|
|
1005
|
-
doc_id = md5(document.content.encode("utf-8")).hexdigest()
|
|
1006
|
-
result = await collection.find_one({"_id": doc_id})
|
|
1007
|
-
exists = result is not None
|
|
1008
|
-
log_debug(f"Document {'exists' if exists else 'does not exist'}: {doc_id}")
|
|
1009
|
-
return exists
|
|
1010
|
-
except Exception as e:
|
|
1011
|
-
logger.error(f"Error checking document existence asynchronously: {e}")
|
|
1012
|
-
return False
|
|
1013
|
-
|
|
1014
1013
|
async def async_insert(
|
|
1015
1014
|
self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None
|
|
1016
1015
|
) -> None:
|
|
@@ -1135,9 +1134,12 @@ class MongoDb(VectorDb):
|
|
|
1135
1134
|
logger.error(f"Error upserting document '{document.name}' asynchronously: {e}")
|
|
1136
1135
|
|
|
1137
1136
|
async def async_search(
|
|
1138
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
1137
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
1139
1138
|
) -> List[Document]:
|
|
1140
1139
|
"""Search for documents asynchronously."""
|
|
1140
|
+
if isinstance(filters, List):
|
|
1141
|
+
log_warning("Filters Expressions are not supported in MongoDB. No filters will be applied.")
|
|
1142
|
+
filters = None
|
|
1141
1143
|
query_embedding = self.embedder.get_embedding(query)
|
|
1142
1144
|
if query_embedding is None:
|
|
1143
1145
|
logger.error(f"Failed to generate embedding for query: {query}")
|
|
@@ -1382,3 +1384,7 @@ class MongoDb(VectorDb):
|
|
|
1382
1384
|
except Exception as e:
|
|
1383
1385
|
logger.error(f"Error updating metadata for content_id '{content_id}': {e}")
|
|
1384
1386
|
raise
|
|
1387
|
+
|
|
1388
|
+
def get_supported_search_types(self) -> List[str]:
|
|
1389
|
+
"""Get the supported search types for this vector database."""
|
|
1390
|
+
return [SearchType.vector, SearchType.hybrid]
|