agno 2.0.0rc2__py3-none-any.whl → 2.3.0__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 +6009 -2874
- agno/api/api.py +2 -0
- agno/api/os.py +1 -1
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +385 -6
- agno/db/dynamo/dynamo.py +388 -81
- agno/db/dynamo/schemas.py +47 -10
- agno/db/dynamo/utils.py +63 -4
- agno/db/firestore/firestore.py +435 -64
- agno/db/firestore/schemas.py +11 -0
- agno/db/firestore/utils.py +102 -4
- agno/db/gcs_json/gcs_json_db.py +384 -42
- agno/db/gcs_json/utils.py +60 -26
- agno/db/in_memory/in_memory_db.py +351 -66
- agno/db/in_memory/utils.py +60 -2
- agno/db/json/json_db.py +339 -48
- agno/db/json/utils.py +60 -26
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +510 -37
- 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 +2036 -0
- agno/db/mongo/mongo.py +653 -76
- agno/db/mongo/schemas.py +13 -0
- agno/db/mongo/utils.py +80 -8
- agno/db/mysql/mysql.py +687 -25
- agno/db/mysql/schemas.py +61 -37
- agno/db/mysql/utils.py +60 -2
- agno/db/postgres/__init__.py +2 -1
- agno/db/postgres/async_postgres.py +2001 -0
- agno/db/postgres/postgres.py +676 -57
- agno/db/postgres/schemas.py +43 -18
- agno/db/postgres/utils.py +164 -2
- agno/db/redis/redis.py +344 -38
- agno/db/redis/schemas.py +18 -0
- agno/db/redis/utils.py +60 -2
- agno/db/schemas/__init__.py +2 -1
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/memory.py +13 -0
- agno/db/singlestore/schemas.py +26 -1
- agno/db/singlestore/singlestore.py +687 -53
- agno/db/singlestore/utils.py +60 -2
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2371 -0
- agno/db/sqlite/schemas.py +24 -0
- agno/db/sqlite/sqlite.py +774 -85
- agno/db/sqlite/utils.py +168 -5
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +309 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1361 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +50 -22
- 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 +68 -1
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/integrations/discord/client.py +1 -0
- agno/knowledge/chunking/agentic.py +13 -10
- agno/knowledge/chunking/fixed.py +1 -1
- agno/knowledge/chunking/semantic.py +40 -8
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/aws_bedrock.py +9 -4
- agno/knowledge/embedder/azure_openai.py +54 -0
- agno/knowledge/embedder/base.py +2 -0
- agno/knowledge/embedder/cohere.py +184 -5
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/knowledge/embedder/google.py +79 -1
- agno/knowledge/embedder/huggingface.py +9 -4
- agno/knowledge/embedder/jina.py +63 -0
- agno/knowledge/embedder/mistral.py +78 -11
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/ollama.py +13 -0
- agno/knowledge/embedder/openai.py +37 -65
- agno/knowledge/embedder/sentence_transformer.py +8 -4
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +69 -16
- agno/knowledge/knowledge.py +595 -187
- agno/knowledge/reader/base.py +9 -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 +290 -0
- agno/knowledge/reader/json_reader.py +6 -5
- agno/knowledge/reader/markdown_reader.py +13 -13
- agno/knowledge/reader/pdf_reader.py +43 -68
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +51 -6
- agno/knowledge/reader/s3_reader.py +3 -15
- agno/knowledge/reader/tavily_reader.py +194 -0
- agno/knowledge/reader/text_reader.py +13 -13
- agno/knowledge/reader/web_search_reader.py +2 -43
- agno/knowledge/reader/website_reader.py +43 -25
- agno/knowledge/reranker/__init__.py +3 -0
- agno/knowledge/types.py +9 -0
- agno/knowledge/utils.py +20 -0
- agno/media.py +339 -266
- agno/memory/manager.py +336 -82
- agno/models/aimlapi/aimlapi.py +2 -2
- agno/models/anthropic/claude.py +183 -37
- agno/models/aws/bedrock.py +52 -112
- agno/models/aws/claude.py +33 -1
- agno/models/azure/ai_foundry.py +33 -15
- agno/models/azure/openai_chat.py +25 -8
- agno/models/base.py +1011 -566
- agno/models/cerebras/cerebras.py +19 -13
- agno/models/cerebras/cerebras_openai.py +8 -5
- agno/models/cohere/chat.py +27 -1
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +57 -0
- agno/models/dashscope/dashscope.py +1 -0
- agno/models/deepinfra/deepinfra.py +2 -2
- agno/models/deepseek/deepseek.py +2 -2
- agno/models/fireworks/fireworks.py +2 -2
- agno/models/google/gemini.py +110 -37
- agno/models/groq/groq.py +28 -11
- agno/models/huggingface/huggingface.py +2 -1
- agno/models/internlm/internlm.py +2 -2
- agno/models/langdb/langdb.py +4 -4
- agno/models/litellm/chat.py +18 -1
- agno/models/litellm/litellm_openai.py +2 -2
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/message.py +143 -4
- agno/models/meta/llama.py +27 -10
- agno/models/meta/llama_openai.py +5 -17
- agno/models/nebius/nebius.py +6 -6
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/nvidia.py +2 -2
- agno/models/ollama/chat.py +60 -6
- agno/models/openai/chat.py +102 -43
- agno/models/openai/responses.py +103 -106
- agno/models/openrouter/openrouter.py +41 -3
- agno/models/perplexity/perplexity.py +4 -5
- agno/models/portkey/portkey.py +3 -3
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +52 -0
- agno/models/response.py +81 -5
- agno/models/sambanova/sambanova.py +2 -2
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +25 -0
- agno/models/together/together.py +2 -2
- agno/models/utils.py +254 -8
- agno/models/vercel/v0.py +2 -2
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +96 -0
- agno/models/vllm/vllm.py +1 -0
- agno/models/xai/xai.py +3 -2
- agno/os/app.py +543 -175
- agno/os/auth.py +24 -14
- agno/os/config.py +1 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +250 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/agui.py +23 -7
- agno/os/interfaces/agui/router.py +27 -3
- agno/os/interfaces/agui/utils.py +242 -142
- agno/os/interfaces/base.py +6 -2
- agno/os/interfaces/slack/router.py +81 -23
- agno/os/interfaces/slack/slack.py +29 -14
- agno/os/interfaces/whatsapp/router.py +11 -4
- agno/os/interfaces/whatsapp/whatsapp.py +14 -7
- agno/os/mcp.py +111 -54
- agno/os/middleware/__init__.py +7 -0
- agno/os/middleware/jwt.py +233 -0
- agno/os/router.py +556 -139
- agno/os/routers/evals/evals.py +71 -34
- agno/os/routers/evals/schemas.py +31 -31
- agno/os/routers/evals/utils.py +6 -5
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/knowledge.py +185 -38
- agno/os/routers/knowledge/schemas.py +82 -22
- agno/os/routers/memory/memory.py +158 -53
- agno/os/routers/memory/schemas.py +20 -16
- agno/os/routers/metrics/metrics.py +20 -8
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +499 -38
- agno/os/schema.py +308 -198
- agno/os/utils.py +401 -41
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +2 -2
- agno/reasoning/deepseek.py +2 -2
- agno/reasoning/default.py +3 -1
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +2 -2
- agno/reasoning/ollama.py +2 -2
- agno/reasoning/openai.py +7 -2
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +266 -112
- agno/run/base.py +53 -24
- agno/run/team.py +252 -111
- agno/run/workflow.py +156 -45
- agno/session/agent.py +105 -89
- agno/session/summary.py +65 -25
- agno/session/team.py +176 -96
- agno/session/workflow.py +406 -40
- agno/team/team.py +3854 -1692
- agno/tools/brightdata.py +3 -3
- agno/tools/cartesia.py +3 -5
- agno/tools/dalle.py +9 -8
- agno/tools/decorator.py +4 -2
- agno/tools/desi_vocal.py +2 -2
- agno/tools/duckduckgo.py +15 -11
- agno/tools/e2b.py +20 -13
- agno/tools/eleven_labs.py +26 -28
- agno/tools/exa.py +21 -16
- agno/tools/fal.py +4 -4
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +350 -0
- agno/tools/firecrawl.py +4 -4
- agno/tools/function.py +257 -37
- agno/tools/giphy.py +2 -2
- agno/tools/gmail.py +238 -14
- agno/tools/google_drive.py +270 -0
- agno/tools/googlecalendar.py +36 -8
- agno/tools/googlesheets.py +20 -5
- agno/tools/jira.py +20 -0
- agno/tools/knowledge.py +3 -3
- agno/tools/lumalab.py +3 -3
- 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 +284 -0
- agno/tools/mem0.py +11 -17
- agno/tools/memori.py +1 -53
- agno/tools/memory.py +419 -0
- agno/tools/models/azure_openai.py +2 -2
- agno/tools/models/gemini.py +3 -3
- agno/tools/models/groq.py +3 -5
- agno/tools/models/nebius.py +7 -7
- agno/tools/models_labs.py +25 -15
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +4 -9
- agno/tools/opencv.py +3 -3
- agno/tools/parallel.py +314 -0
- agno/tools/replicate.py +7 -7
- agno/tools/scrapegraph.py +58 -31
- agno/tools/searxng.py +2 -2
- agno/tools/serper.py +2 -2
- agno/tools/slack.py +18 -3
- agno/tools/spider.py +2 -2
- agno/tools/tavily.py +146 -0
- agno/tools/whatsapp.py +1 -1
- agno/tools/workflow.py +278 -0
- agno/tools/yfinance.py +12 -11
- agno/utils/agent.py +820 -0
- agno/utils/audio.py +27 -0
- agno/utils/common.py +90 -1
- agno/utils/events.py +222 -7
- agno/utils/gemini.py +181 -23
- agno/utils/hooks.py +57 -0
- agno/utils/http.py +111 -0
- agno/utils/knowledge.py +12 -5
- agno/utils/log.py +1 -0
- agno/utils/mcp.py +95 -5
- agno/utils/media.py +188 -10
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +60 -0
- agno/utils/models/claude.py +40 -11
- agno/utils/models/cohere.py +1 -1
- agno/utils/models/watsonx.py +1 -1
- agno/utils/openai.py +1 -1
- agno/utils/print_response/agent.py +105 -21
- agno/utils/print_response/team.py +103 -38
- agno/utils/print_response/workflow.py +251 -34
- agno/utils/reasoning.py +22 -1
- agno/utils/serialize.py +32 -0
- agno/utils/streamlit.py +16 -10
- agno/utils/string.py +41 -0
- agno/utils/team.py +98 -9
- agno/utils/tools.py +1 -1
- agno/vectordb/base.py +23 -4
- agno/vectordb/cassandra/cassandra.py +65 -9
- agno/vectordb/chroma/chromadb.py +182 -38
- agno/vectordb/clickhouse/clickhousedb.py +64 -11
- agno/vectordb/couchbase/couchbase.py +105 -10
- agno/vectordb/lancedb/lance_db.py +183 -135
- agno/vectordb/langchaindb/langchaindb.py +25 -7
- agno/vectordb/lightrag/lightrag.py +17 -3
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +46 -7
- agno/vectordb/milvus/milvus.py +126 -9
- agno/vectordb/mongodb/__init__.py +7 -1
- agno/vectordb/mongodb/mongodb.py +112 -7
- agno/vectordb/pgvector/pgvector.py +142 -21
- agno/vectordb/pineconedb/pineconedb.py +80 -8
- agno/vectordb/qdrant/qdrant.py +125 -39
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +694 -0
- agno/vectordb/singlestore/singlestore.py +111 -25
- agno/vectordb/surrealdb/surrealdb.py +31 -5
- agno/vectordb/upstashdb/upstashdb.py +76 -8
- agno/vectordb/weaviate/weaviate.py +86 -15
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +112 -18
- agno/workflow/loop.py +69 -10
- agno/workflow/parallel.py +266 -118
- agno/workflow/router.py +110 -17
- agno/workflow/step.py +645 -136
- agno/workflow/steps.py +65 -6
- agno/workflow/types.py +71 -33
- agno/workflow/workflow.py +2113 -300
- agno-2.3.0.dist-info/METADATA +618 -0
- agno-2.3.0.dist-info/RECORD +577 -0
- agno-2.3.0.dist-info/licenses/LICENSE +201 -0
- agno/knowledge/reader/url_reader.py +0 -128
- agno/tools/googlesearch.py +0 -98
- agno/tools/mcp.py +0 -610
- agno/utils/models/aws_claude.py +0 -170
- agno-2.0.0rc2.dist-info/METADATA +0 -355
- agno-2.0.0rc2.dist-info/RECORD +0 -515
- agno-2.0.0rc2.dist-info/licenses/LICENSE +0 -375
- {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
- {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
from typing import Any, Dict, List, Optional
|
|
1
|
+
from typing import Any, Dict, List, Optional, Union
|
|
2
2
|
|
|
3
|
+
from agno.filters import FilterExpr
|
|
3
4
|
from agno.knowledge.document import Document
|
|
4
|
-
from agno.utils.log import log_debug, logger
|
|
5
|
+
from agno.utils.log import log_debug, log_warning, logger
|
|
5
6
|
from agno.vectordb.base import VectorDb
|
|
6
7
|
|
|
7
8
|
|
|
@@ -11,16 +12,23 @@ class LangChainVectorDb(VectorDb):
|
|
|
11
12
|
vectorstore: Optional[Any] = None,
|
|
12
13
|
search_kwargs: Optional[dict] = None,
|
|
13
14
|
knowledge_retriever: Optional[Any] = None,
|
|
15
|
+
name: Optional[str] = None,
|
|
16
|
+
description: Optional[str] = None,
|
|
14
17
|
):
|
|
15
18
|
"""
|
|
16
19
|
Initialize LangChainVectorDb.
|
|
17
20
|
|
|
18
21
|
Args:
|
|
19
22
|
vectorstore: The LangChain vectorstore instance
|
|
23
|
+
name (Optional[str]): Name of the vector database.
|
|
24
|
+
description (Optional[str]): Description of the vector database.
|
|
20
25
|
search_kwargs: Additional search parameters for the retriever
|
|
21
26
|
knowledge_retriever: An optional LangChain retriever instance
|
|
22
27
|
"""
|
|
23
28
|
self.vectorstore = vectorstore
|
|
29
|
+
# Initialize base class with name and description
|
|
30
|
+
super().__init__(name=name, description=description)
|
|
31
|
+
|
|
24
32
|
self.search_kwargs = search_kwargs
|
|
25
33
|
self.knowledge_retriever = knowledge_retriever
|
|
26
34
|
|
|
@@ -64,10 +72,16 @@ class LangChainVectorDb(VectorDb):
|
|
|
64
72
|
raise NotImplementedError
|
|
65
73
|
|
|
66
74
|
def search(
|
|
67
|
-
self, query: str,
|
|
75
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
68
76
|
) -> List[Document]:
|
|
69
77
|
"""Returns relevant documents matching the query"""
|
|
70
78
|
|
|
79
|
+
if isinstance(filters, List):
|
|
80
|
+
log_warning(
|
|
81
|
+
"Filter Expressions are not supported in LangChainDB. No filters will be applied. Use filters as a dictionary."
|
|
82
|
+
)
|
|
83
|
+
filters = None
|
|
84
|
+
|
|
71
85
|
try:
|
|
72
86
|
from langchain_core.documents import Document as LangChainDocument
|
|
73
87
|
from langchain_core.retrievers import BaseRetriever
|
|
@@ -79,7 +93,7 @@ class LangChainVectorDb(VectorDb):
|
|
|
79
93
|
if self.vectorstore is not None and self.knowledge_retriever is None:
|
|
80
94
|
log_debug("Creating knowledge retriever")
|
|
81
95
|
if self.search_kwargs is None:
|
|
82
|
-
self.search_kwargs = {"k":
|
|
96
|
+
self.search_kwargs = {"k": limit}
|
|
83
97
|
if filters is not None:
|
|
84
98
|
self.search_kwargs.update(filters)
|
|
85
99
|
self.knowledge_retriever = self.vectorstore.as_retriever(search_kwargs=self.search_kwargs)
|
|
@@ -91,7 +105,7 @@ class LangChainVectorDb(VectorDb):
|
|
|
91
105
|
if not isinstance(self.knowledge_retriever, BaseRetriever):
|
|
92
106
|
raise ValueError(f"Knowledge retriever is not of type BaseRetriever: {self.knowledge_retriever}")
|
|
93
107
|
|
|
94
|
-
log_debug(f"Getting {
|
|
108
|
+
log_debug(f"Getting {limit} relevant documents for query: {query}")
|
|
95
109
|
lc_documents: List[LangChainDocument] = self.knowledge_retriever.invoke(input=query)
|
|
96
110
|
documents = []
|
|
97
111
|
for lc_doc in lc_documents:
|
|
@@ -104,9 +118,9 @@ class LangChainVectorDb(VectorDb):
|
|
|
104
118
|
return documents
|
|
105
119
|
|
|
106
120
|
async def async_search(
|
|
107
|
-
self, query: str,
|
|
121
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
108
122
|
) -> List[Document]:
|
|
109
|
-
return self.search(query,
|
|
123
|
+
return self.search(query, limit, filters)
|
|
110
124
|
|
|
111
125
|
def drop(self) -> None:
|
|
112
126
|
raise NotImplementedError
|
|
@@ -143,3 +157,7 @@ class LangChainVectorDb(VectorDb):
|
|
|
143
157
|
metadata (Dict[str, Any]): The metadata to update
|
|
144
158
|
"""
|
|
145
159
|
raise NotImplementedError("update_metadata not supported for LangChain vectorstores")
|
|
160
|
+
|
|
161
|
+
def get_supported_search_types(self) -> List[str]:
|
|
162
|
+
"""Get the supported search types for this vector database."""
|
|
163
|
+
return [] # LangChainVectorDb doesn't use SearchType enum
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from typing import Any, Dict, List, Optional
|
|
2
|
+
from typing import Any, Dict, List, Optional, Union
|
|
3
3
|
|
|
4
4
|
import httpx
|
|
5
5
|
|
|
6
|
+
from agno.filters import FilterExpr
|
|
6
7
|
from agno.knowledge.document import Document
|
|
7
8
|
from agno.utils.log import log_debug, log_error, log_info, log_warning
|
|
8
9
|
from agno.vectordb.base import VectorDb
|
|
@@ -21,9 +22,14 @@ class LightRag(VectorDb):
|
|
|
21
22
|
api_key: Optional[str] = None,
|
|
22
23
|
auth_header_name: str = "X-API-KEY",
|
|
23
24
|
auth_header_format: str = "{api_key}",
|
|
25
|
+
name: Optional[str] = None,
|
|
26
|
+
description: Optional[str] = None,
|
|
24
27
|
):
|
|
25
28
|
self.server_url = server_url
|
|
26
29
|
self.api_key = api_key
|
|
30
|
+
# Initialize base class with name and description
|
|
31
|
+
super().__init__(name=name, description=description)
|
|
32
|
+
|
|
27
33
|
self.auth_header_name = auth_header_name
|
|
28
34
|
self.auth_header_format = auth_header_format
|
|
29
35
|
|
|
@@ -87,14 +93,18 @@ class LightRag(VectorDb):
|
|
|
87
93
|
"""Async upsert documents into the vector database"""
|
|
88
94
|
pass
|
|
89
95
|
|
|
90
|
-
def search(
|
|
96
|
+
def search(
|
|
97
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
98
|
+
) -> List[Document]:
|
|
91
99
|
result = asyncio.run(self.async_search(query, limit=limit, filters=filters))
|
|
92
100
|
return result if result is not None else []
|
|
93
101
|
|
|
94
102
|
async def async_search(
|
|
95
|
-
self, query: str, limit: Optional[int] = None, filters: Optional[Dict[str, Any]] = None
|
|
103
|
+
self, query: str, limit: Optional[int] = None, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
96
104
|
) -> Optional[List[Document]]:
|
|
97
105
|
mode: str = "hybrid" # Default mode, can be "local", "global", or "hybrid"
|
|
106
|
+
if filters is not None:
|
|
107
|
+
log_warning("Filters are not supported in LightRAG. No filters will be applied.")
|
|
98
108
|
try:
|
|
99
109
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
100
110
|
response = await client.post(
|
|
@@ -372,3 +382,7 @@ class LightRag(VectorDb):
|
|
|
372
382
|
metadata (Dict[str, Any]): The metadata to update
|
|
373
383
|
"""
|
|
374
384
|
raise NotImplementedError("update_metadata not supported for LightRag - use LightRag's native methods")
|
|
385
|
+
|
|
386
|
+
def get_supported_search_types(self) -> List[str]:
|
|
387
|
+
"""Get the supported search types for this vector database."""
|
|
388
|
+
return [] # LightRag doesn't use SearchType enum
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
from typing import Any, Callable, Dict, List, Optional
|
|
1
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
2
2
|
|
|
3
|
+
from agno.filters import FilterExpr
|
|
3
4
|
from agno.knowledge.document import Document
|
|
4
|
-
from agno.utils.log import logger
|
|
5
|
+
from agno.utils.log import log_warning, logger
|
|
5
6
|
from agno.vectordb.base import VectorDb
|
|
6
7
|
|
|
7
8
|
try:
|
|
@@ -17,6 +18,21 @@ class LlamaIndexVectorDb(VectorDb):
|
|
|
17
18
|
knowledge_retriever: BaseRetriever
|
|
18
19
|
loader: Optional[Callable] = None
|
|
19
20
|
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
knowledge_retriever: BaseRetriever,
|
|
24
|
+
loader: Optional[Callable] = None,
|
|
25
|
+
name: Optional[str] = None,
|
|
26
|
+
description: Optional[str] = None,
|
|
27
|
+
**kwargs,
|
|
28
|
+
):
|
|
29
|
+
super().__init__(**kwargs)
|
|
30
|
+
# Initialize base class with name and description
|
|
31
|
+
super().__init__(name=name, description=description)
|
|
32
|
+
|
|
33
|
+
self.knowledge_retriever = knowledge_retriever
|
|
34
|
+
self.loader = loader
|
|
35
|
+
|
|
20
36
|
def create(self) -> None:
|
|
21
37
|
raise NotImplementedError
|
|
22
38
|
|
|
@@ -54,14 +70,14 @@ class LlamaIndexVectorDb(VectorDb):
|
|
|
54
70
|
raise NotImplementedError
|
|
55
71
|
|
|
56
72
|
def search(
|
|
57
|
-
self, query: str,
|
|
73
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
58
74
|
) -> List[Document]:
|
|
59
75
|
"""
|
|
60
76
|
Returns relevant documents matching the query.
|
|
61
77
|
|
|
62
78
|
Args:
|
|
63
79
|
query (str): The query string to search for.
|
|
64
|
-
|
|
80
|
+
limit (int): The maximum number of documents to return. Defaults to 5.
|
|
65
81
|
filters (Optional[Dict[str, Any]]): Filters to apply to the search. Defaults to None.
|
|
66
82
|
|
|
67
83
|
Returns:
|
|
@@ -69,12 +85,15 @@ class LlamaIndexVectorDb(VectorDb):
|
|
|
69
85
|
Raises:
|
|
70
86
|
ValueError: If the knowledge retriever is not of type BaseRetriever.
|
|
71
87
|
"""
|
|
88
|
+
if filters is not None:
|
|
89
|
+
log_warning("Filters are not supported in LlamaIndex. No filters will be applied.")
|
|
90
|
+
|
|
72
91
|
if not isinstance(self.knowledge_retriever, BaseRetriever):
|
|
73
92
|
raise ValueError(f"Knowledge retriever is not of type BaseRetriever: {self.knowledge_retriever}")
|
|
74
93
|
|
|
75
94
|
lc_documents: List[NodeWithScore] = self.knowledge_retriever.retrieve(query)
|
|
76
|
-
if
|
|
77
|
-
lc_documents = lc_documents[:
|
|
95
|
+
if limit is not None:
|
|
96
|
+
lc_documents = lc_documents[:limit]
|
|
78
97
|
documents = []
|
|
79
98
|
for lc_doc in lc_documents:
|
|
80
99
|
documents.append(
|
|
@@ -86,7 +105,7 @@ class LlamaIndexVectorDb(VectorDb):
|
|
|
86
105
|
return documents
|
|
87
106
|
|
|
88
107
|
async def async_search(
|
|
89
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
108
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
90
109
|
) -> List[Document]:
|
|
91
110
|
return self.search(query, limit, filters)
|
|
92
111
|
|
|
@@ -125,3 +144,23 @@ class LlamaIndexVectorDb(VectorDb):
|
|
|
125
144
|
metadata (Dict[str, Any]): The metadata to update
|
|
126
145
|
"""
|
|
127
146
|
raise NotImplementedError("update_metadata not supported for LlamaIndex vectorstores")
|
|
147
|
+
|
|
148
|
+
def delete_by_content_id(self, content_id: str) -> bool:
|
|
149
|
+
"""
|
|
150
|
+
Delete documents by content ID.
|
|
151
|
+
Not implemented for LlamaIndex wrapper.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
content_id (str): The content ID to delete
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
bool: False as this operation is not supported
|
|
158
|
+
"""
|
|
159
|
+
logger.warning(
|
|
160
|
+
"LlamaIndexVectorDb.delete_by_content_id() not supported - please check the vectorstore manually."
|
|
161
|
+
)
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
def get_supported_search_types(self) -> List[str]:
|
|
165
|
+
"""Get the supported search types for this vector database."""
|
|
166
|
+
return [] # LlamaIndexVectorDb doesn't use SearchType enum
|
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:
|
|
@@ -423,6 +443,9 @@ class Milvus(VectorDb):
|
|
|
423
443
|
else:
|
|
424
444
|
for document in documents:
|
|
425
445
|
document.embed(embedder=self.embedder)
|
|
446
|
+
if not document.embedding:
|
|
447
|
+
log_debug(f"Skipping document without embedding: {document.name} ({document.meta_data})")
|
|
448
|
+
continue
|
|
426
449
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
427
450
|
doc_id = md5(cleaned_content.encode()).hexdigest()
|
|
428
451
|
|
|
@@ -454,8 +477,44 @@ class Milvus(VectorDb):
|
|
|
454
477
|
"""Insert documents asynchronously based on search type."""
|
|
455
478
|
log_info(f"Inserting {len(documents)} documents asynchronously")
|
|
456
479
|
|
|
457
|
-
|
|
458
|
-
|
|
480
|
+
if self.embedder.enable_batch and hasattr(self.embedder, "async_get_embeddings_batch_and_usage"):
|
|
481
|
+
# Use batch embedding when enabled and supported
|
|
482
|
+
try:
|
|
483
|
+
# Extract content from all documents
|
|
484
|
+
doc_contents = [doc.content for doc in documents]
|
|
485
|
+
|
|
486
|
+
# Get batch embeddings and usage
|
|
487
|
+
embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
|
|
488
|
+
|
|
489
|
+
# Process documents with pre-computed embeddings
|
|
490
|
+
for j, doc in enumerate(documents):
|
|
491
|
+
try:
|
|
492
|
+
if j < len(embeddings):
|
|
493
|
+
doc.embedding = embeddings[j]
|
|
494
|
+
doc.usage = usages[j] if j < len(usages) else None
|
|
495
|
+
except Exception as e:
|
|
496
|
+
log_error(f"Error assigning batch embedding to document '{doc.name}': {e}")
|
|
497
|
+
|
|
498
|
+
except Exception as e:
|
|
499
|
+
# Check if this is a rate limit error - don't fall back as it would make things worse
|
|
500
|
+
error_str = str(e).lower()
|
|
501
|
+
is_rate_limit = any(
|
|
502
|
+
phrase in error_str
|
|
503
|
+
for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
if is_rate_limit:
|
|
507
|
+
log_error(f"Rate limit detected during batch embedding. {e}")
|
|
508
|
+
raise e
|
|
509
|
+
else:
|
|
510
|
+
log_error(f"Async batch embedding failed, falling back to individual embeddings: {e}")
|
|
511
|
+
# Fall back to individual embedding
|
|
512
|
+
embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in documents]
|
|
513
|
+
await asyncio.gather(*embed_tasks, return_exceptions=True)
|
|
514
|
+
else:
|
|
515
|
+
# Use individual embedding
|
|
516
|
+
embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
|
|
517
|
+
await asyncio.gather(*embed_tasks, return_exceptions=True)
|
|
459
518
|
|
|
460
519
|
if self.search_type == SearchType.hybrid:
|
|
461
520
|
await asyncio.gather(
|
|
@@ -465,6 +524,9 @@ class Milvus(VectorDb):
|
|
|
465
524
|
|
|
466
525
|
async def process_document(document):
|
|
467
526
|
document.embed(embedder=self.embedder)
|
|
527
|
+
if not document.embedding:
|
|
528
|
+
log_debug(f"Skipping document without embedding: {document.name} ({document.meta_data})")
|
|
529
|
+
return None
|
|
468
530
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
469
531
|
doc_id = md5(cleaned_content.encode()).hexdigest()
|
|
470
532
|
|
|
@@ -541,8 +603,44 @@ class Milvus(VectorDb):
|
|
|
541
603
|
) -> None:
|
|
542
604
|
log_debug(f"Upserting {len(documents)} documents asynchronously")
|
|
543
605
|
|
|
544
|
-
|
|
545
|
-
|
|
606
|
+
if self.embedder.enable_batch and hasattr(self.embedder, "async_get_embeddings_batch_and_usage"):
|
|
607
|
+
# Use batch embedding when enabled and supported
|
|
608
|
+
try:
|
|
609
|
+
# Extract content from all documents
|
|
610
|
+
doc_contents = [doc.content for doc in documents]
|
|
611
|
+
|
|
612
|
+
# Get batch embeddings and usage
|
|
613
|
+
embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
|
|
614
|
+
|
|
615
|
+
# Process documents with pre-computed embeddings
|
|
616
|
+
for j, doc in enumerate(documents):
|
|
617
|
+
try:
|
|
618
|
+
if j < len(embeddings):
|
|
619
|
+
doc.embedding = embeddings[j]
|
|
620
|
+
doc.usage = usages[j] if j < len(usages) else None
|
|
621
|
+
except Exception as e:
|
|
622
|
+
log_error(f"Error assigning batch embedding to document '{doc.name}': {e}")
|
|
623
|
+
|
|
624
|
+
except Exception as e:
|
|
625
|
+
# Check if this is a rate limit error - don't fall back as it would make things worse
|
|
626
|
+
error_str = str(e).lower()
|
|
627
|
+
is_rate_limit = any(
|
|
628
|
+
phrase in error_str
|
|
629
|
+
for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
if is_rate_limit:
|
|
633
|
+
log_error(f"Rate limit detected during batch embedding. {e}")
|
|
634
|
+
raise e
|
|
635
|
+
else:
|
|
636
|
+
log_error(f"Async batch embedding failed, falling back to individual embeddings: {e}")
|
|
637
|
+
# Fall back to individual embedding
|
|
638
|
+
embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in documents]
|
|
639
|
+
await asyncio.gather(*embed_tasks, return_exceptions=True)
|
|
640
|
+
else:
|
|
641
|
+
# Use individual embedding
|
|
642
|
+
embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
|
|
643
|
+
await asyncio.gather(*embed_tasks, return_exceptions=True)
|
|
546
644
|
|
|
547
645
|
async def process_document(document):
|
|
548
646
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
@@ -578,7 +676,9 @@ class Milvus(VectorDb):
|
|
|
578
676
|
"""
|
|
579
677
|
return MILVUS_DISTANCE_MAP.get(self.distance, "COSINE")
|
|
580
678
|
|
|
581
|
-
def search(
|
|
679
|
+
def search(
|
|
680
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
681
|
+
) -> List[Document]:
|
|
582
682
|
"""
|
|
583
683
|
Search for documents matching the query.
|
|
584
684
|
|
|
@@ -590,6 +690,9 @@ class Milvus(VectorDb):
|
|
|
590
690
|
Returns:
|
|
591
691
|
List[Document]: List of matching documents
|
|
592
692
|
"""
|
|
693
|
+
if isinstance(filters, List):
|
|
694
|
+
log_warning("Filters Expressions are not supported in Milvus. No filters will be applied.")
|
|
695
|
+
filters = None
|
|
593
696
|
if self.search_type == SearchType.hybrid:
|
|
594
697
|
return self.hybrid_search(query, limit)
|
|
595
698
|
|
|
@@ -622,12 +725,20 @@ class Milvus(VectorDb):
|
|
|
622
725
|
)
|
|
623
726
|
)
|
|
624
727
|
|
|
728
|
+
# Apply reranker if available
|
|
729
|
+
if self.reranker and search_results:
|
|
730
|
+
search_results = self.reranker.rerank(query=query, documents=search_results)
|
|
731
|
+
search_results = search_results[:limit]
|
|
732
|
+
|
|
625
733
|
log_info(f"Found {len(search_results)} documents")
|
|
626
734
|
return search_results
|
|
627
735
|
|
|
628
736
|
async def async_search(
|
|
629
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
737
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
630
738
|
) -> List[Document]:
|
|
739
|
+
if isinstance(filters, List):
|
|
740
|
+
log_warning("Filters Expressions are not supported in Milvus. No filters will be applied.")
|
|
741
|
+
filters = None
|
|
631
742
|
if self.search_type == SearchType.hybrid:
|
|
632
743
|
return await self.async_hybrid_search(query, limit, filters)
|
|
633
744
|
|
|
@@ -663,7 +774,9 @@ class Milvus(VectorDb):
|
|
|
663
774
|
log_info(f"Found {len(search_results)} documents")
|
|
664
775
|
return search_results
|
|
665
776
|
|
|
666
|
-
def hybrid_search(
|
|
777
|
+
def hybrid_search(
|
|
778
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
779
|
+
) -> List[Document]:
|
|
667
780
|
"""
|
|
668
781
|
Perform a hybrid search combining dense and sparse vector similarity.
|
|
669
782
|
|
|
@@ -755,7 +868,7 @@ class Milvus(VectorDb):
|
|
|
755
868
|
return []
|
|
756
869
|
|
|
757
870
|
async def async_hybrid_search(
|
|
758
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
871
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
759
872
|
) -> List[Document]:
|
|
760
873
|
"""
|
|
761
874
|
Perform an asynchronous hybrid search combining dense and sparse vector similarity.
|
|
@@ -1063,3 +1176,7 @@ class Milvus(VectorDb):
|
|
|
1063
1176
|
except Exception as e:
|
|
1064
1177
|
log_error(f"Error updating metadata for content_id '{content_id}': {e}")
|
|
1065
1178
|
raise
|
|
1179
|
+
|
|
1180
|
+
def get_supported_search_types(self) -> List[str]:
|
|
1181
|
+
"""Get the supported search types for this vector database."""
|
|
1182
|
+
return [SearchType.vector, SearchType.hybrid]
|