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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
3
|
from hashlib import md5
|
|
4
|
-
from typing import Any, Dict, List, Optional
|
|
4
|
+
from typing import Any, Dict, List, Optional, Union
|
|
5
5
|
|
|
6
6
|
try:
|
|
7
7
|
from sqlalchemy.dialects import mysql
|
|
@@ -14,10 +14,11 @@ try:
|
|
|
14
14
|
except ImportError:
|
|
15
15
|
raise ImportError("`sqlalchemy` not installed")
|
|
16
16
|
|
|
17
|
+
from agno.filters import FilterExpr
|
|
17
18
|
from agno.knowledge.document import Document
|
|
18
19
|
from agno.knowledge.embedder import Embedder
|
|
19
20
|
from agno.knowledge.reranker.base import Reranker
|
|
20
|
-
from agno.utils.log import log_debug, log_error, log_info
|
|
21
|
+
from agno.utils.log import log_debug, log_error, log_info, log_warning
|
|
21
22
|
from agno.vectordb.base import VectorDb
|
|
22
23
|
from agno.vectordb.distance import Distance
|
|
23
24
|
|
|
@@ -32,6 +33,8 @@ class SingleStore(VectorDb):
|
|
|
32
33
|
embedder: Optional[Embedder] = None,
|
|
33
34
|
distance: Distance = Distance.cosine,
|
|
34
35
|
reranker: Optional[Reranker] = None,
|
|
36
|
+
name: Optional[str] = None,
|
|
37
|
+
description: Optional[str] = None,
|
|
35
38
|
# index: Optional[Union[Ivfflat, HNSW]] = HNSW(),
|
|
36
39
|
):
|
|
37
40
|
_engine: Optional[Engine] = db_engine
|
|
@@ -44,9 +47,11 @@ class SingleStore(VectorDb):
|
|
|
44
47
|
self.collection: str = collection
|
|
45
48
|
self.schema: Optional[str] = schema
|
|
46
49
|
self.db_url: Optional[str] = db_url
|
|
50
|
+
# Initialize base class with name and description
|
|
51
|
+
super().__init__(name=name, description=description)
|
|
52
|
+
|
|
47
53
|
self.db_engine: Engine = _engine
|
|
48
54
|
self.metadata: MetaData = MetaData(schema=self.schema)
|
|
49
|
-
|
|
50
55
|
if embedder is None:
|
|
51
56
|
from agno.knowledge.embedder.openai import OpenAIEmbedder
|
|
52
57
|
|
|
@@ -180,8 +185,10 @@ class SingleStore(VectorDb):
|
|
|
180
185
|
for document in documents:
|
|
181
186
|
document.embed(embedder=self.embedder)
|
|
182
187
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
183
|
-
|
|
184
|
-
|
|
188
|
+
# Include content_hash in ID to ensure uniqueness across different content hashes
|
|
189
|
+
base_id = document.id or md5(cleaned_content.encode()).hexdigest()
|
|
190
|
+
record_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
|
|
191
|
+
_id = record_id
|
|
185
192
|
|
|
186
193
|
meta_data_json = json.dumps(document.meta_data)
|
|
187
194
|
usage_json = json.dumps(document.usage)
|
|
@@ -241,8 +248,10 @@ class SingleStore(VectorDb):
|
|
|
241
248
|
for document in documents:
|
|
242
249
|
document.embed(embedder=self.embedder)
|
|
243
250
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
244
|
-
|
|
245
|
-
|
|
251
|
+
# Include content_hash in ID to ensure uniqueness across different content hashes
|
|
252
|
+
base_id = document.id or md5(cleaned_content.encode()).hexdigest()
|
|
253
|
+
record_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
|
|
254
|
+
_id = record_id
|
|
246
255
|
|
|
247
256
|
meta_data_json = json.dumps(document.meta_data)
|
|
248
257
|
usage_json = json.dumps(document.usage)
|
|
@@ -278,7 +287,9 @@ class SingleStore(VectorDb):
|
|
|
278
287
|
sess.commit()
|
|
279
288
|
log_debug(f"Committed {counter} documents")
|
|
280
289
|
|
|
281
|
-
def search(
|
|
290
|
+
def search(
|
|
291
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
292
|
+
) -> List[Document]:
|
|
282
293
|
"""
|
|
283
294
|
Search for documents based on a query and optional filters.
|
|
284
295
|
|
|
@@ -290,6 +301,8 @@ class SingleStore(VectorDb):
|
|
|
290
301
|
Returns:
|
|
291
302
|
List[Document]: List of documents that match the query.
|
|
292
303
|
"""
|
|
304
|
+
if filters is not None:
|
|
305
|
+
log_warning("Filters are not supported in SingleStore. No filters will be applied.")
|
|
293
306
|
query_embedding = self.embedder.get_embedding(query)
|
|
294
307
|
if query_embedding is None:
|
|
295
308
|
log_error(f"Error getting embedding for Query: {query}")
|
|
@@ -428,9 +441,9 @@ class SingleStore(VectorDb):
|
|
|
428
441
|
try:
|
|
429
442
|
with self.Session.begin() as sess:
|
|
430
443
|
stmt = delete(self.table).where(self.table.c.id == id)
|
|
431
|
-
result = sess.execute(stmt)
|
|
432
|
-
log_info(f"Deleted {result.rowcount} records with ID {id} from table '{self.table.name}'.")
|
|
433
|
-
return result.rowcount > 0
|
|
444
|
+
result = sess.execute(stmt) # type: ignore
|
|
445
|
+
log_info(f"Deleted {result.rowcount} records with ID {id} from table '{self.table.name}'.") # type: ignore
|
|
446
|
+
return result.rowcount > 0 # type: ignore
|
|
434
447
|
except Exception as e:
|
|
435
448
|
log_error(f"Error deleting document with ID {id}: {e}")
|
|
436
449
|
return False
|
|
@@ -444,11 +457,11 @@ class SingleStore(VectorDb):
|
|
|
444
457
|
try:
|
|
445
458
|
with self.Session.begin() as sess:
|
|
446
459
|
stmt = delete(self.table).where(self.table.c.content_id == content_id)
|
|
447
|
-
result = sess.execute(stmt)
|
|
460
|
+
result = sess.execute(stmt) # type: ignore
|
|
448
461
|
log_info(
|
|
449
|
-
f"Deleted {result.rowcount} records with content_id {content_id} from table '{self.table.name}'."
|
|
462
|
+
f"Deleted {result.rowcount} records with content_id {content_id} from table '{self.table.name}'." # type: ignore
|
|
450
463
|
)
|
|
451
|
-
return result.rowcount > 0
|
|
464
|
+
return result.rowcount > 0 # type: ignore
|
|
452
465
|
except Exception as e:
|
|
453
466
|
log_error(f"Error deleting document with content_id {content_id}: {e}")
|
|
454
467
|
return False
|
|
@@ -462,9 +475,9 @@ class SingleStore(VectorDb):
|
|
|
462
475
|
try:
|
|
463
476
|
with self.Session.begin() as sess:
|
|
464
477
|
stmt = delete(self.table).where(self.table.c.name == name)
|
|
465
|
-
result = sess.execute(stmt)
|
|
466
|
-
log_info(f"Deleted {result.rowcount} records with name '{name}' from table '{self.table.name}'.")
|
|
467
|
-
return result.rowcount > 0
|
|
478
|
+
result = sess.execute(stmt) # type: ignore
|
|
479
|
+
log_info(f"Deleted {result.rowcount} records with name '{name}' from table '{self.table.name}'.") # type: ignore
|
|
480
|
+
return result.rowcount > 0 # type: ignore
|
|
468
481
|
except Exception as e:
|
|
469
482
|
log_error(f"Error deleting document with name {name}: {e}")
|
|
470
483
|
return False
|
|
@@ -480,9 +493,9 @@ class SingleStore(VectorDb):
|
|
|
480
493
|
# Convert metadata to JSON string for comparison
|
|
481
494
|
metadata_json = json.dumps(metadata, sort_keys=True)
|
|
482
495
|
stmt = delete(self.table).where(self.table.c.meta_data == metadata_json)
|
|
483
|
-
result = sess.execute(stmt)
|
|
484
|
-
log_info(f"Deleted {result.rowcount} records with metadata {metadata} from table '{self.table.name}'.")
|
|
485
|
-
return result.rowcount > 0
|
|
496
|
+
result = sess.execute(stmt) # type: ignore
|
|
497
|
+
log_info(f"Deleted {result.rowcount} records with metadata {metadata} from table '{self.table.name}'.") # type: ignore
|
|
498
|
+
return result.rowcount > 0 # type: ignore
|
|
486
499
|
except Exception as e:
|
|
487
500
|
log_error(f"Error deleting documents with metadata {metadata}: {e}")
|
|
488
501
|
return False
|
|
@@ -539,8 +552,10 @@ class SingleStore(VectorDb):
|
|
|
539
552
|
counter = 0
|
|
540
553
|
for document in documents:
|
|
541
554
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
542
|
-
|
|
543
|
-
|
|
555
|
+
# Include content_hash in ID to ensure uniqueness across different content hashes
|
|
556
|
+
base_id = document.id or md5(cleaned_content.encode()).hexdigest()
|
|
557
|
+
record_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
|
|
558
|
+
_id = record_id
|
|
544
559
|
|
|
545
560
|
meta_data_json = json.dumps(document.meta_data)
|
|
546
561
|
usage_json = json.dumps(document.usage)
|
|
@@ -623,8 +638,10 @@ class SingleStore(VectorDb):
|
|
|
623
638
|
counter = 0
|
|
624
639
|
for document in documents:
|
|
625
640
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
626
|
-
|
|
627
|
-
|
|
641
|
+
# Include content_hash in ID to ensure uniqueness across different content hashes
|
|
642
|
+
base_id = document.id or md5(cleaned_content.encode()).hexdigest()
|
|
643
|
+
record_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
|
|
644
|
+
_id = record_id
|
|
628
645
|
|
|
629
646
|
meta_data_json = json.dumps(document.meta_data)
|
|
630
647
|
usage_json = json.dumps(document.usage)
|
|
@@ -661,7 +678,7 @@ class SingleStore(VectorDb):
|
|
|
661
678
|
log_debug(f"Committed {counter} documents")
|
|
662
679
|
|
|
663
680
|
async def async_search(
|
|
664
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
681
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
665
682
|
) -> List[Document]:
|
|
666
683
|
return self.search(query=query, limit=limit, filters=filters)
|
|
667
684
|
|
|
@@ -689,11 +706,11 @@ class SingleStore(VectorDb):
|
|
|
689
706
|
try:
|
|
690
707
|
with self.Session.begin() as sess:
|
|
691
708
|
stmt = delete(self.table).where(self.table.c.content_hash == content_hash)
|
|
692
|
-
result = sess.execute(stmt)
|
|
709
|
+
result = sess.execute(stmt) # type: ignore
|
|
693
710
|
log_info(
|
|
694
|
-
f"Deleted {result.rowcount} records with content_hash '{content_hash}' from table '{self.table.name}'."
|
|
711
|
+
f"Deleted {result.rowcount} records with content_hash '{content_hash}' from table '{self.table.name}'." # type: ignore
|
|
695
712
|
)
|
|
696
|
-
return result.rowcount > 0
|
|
713
|
+
return result.rowcount > 0 # type: ignore
|
|
697
714
|
except Exception as e:
|
|
698
715
|
log_error(f"Error deleting documents with content_hash {content_hash}: {e}")
|
|
699
716
|
return False
|
|
@@ -712,7 +729,7 @@ class SingleStore(VectorDb):
|
|
|
712
729
|
with self.Session.begin() as sess:
|
|
713
730
|
# Find documents with the given content_id
|
|
714
731
|
stmt = select(self.table).where(self.table.c.content_id == content_id)
|
|
715
|
-
result = sess.execute(stmt)
|
|
732
|
+
result = sess.execute(stmt) # type: ignore
|
|
716
733
|
|
|
717
734
|
updated_count = 0
|
|
718
735
|
for row in result:
|
|
@@ -748,3 +765,7 @@ class SingleStore(VectorDb):
|
|
|
748
765
|
except Exception as e:
|
|
749
766
|
log_error(f"Error updating metadata for content_id '{content_id}': {e}")
|
|
750
767
|
raise
|
|
768
|
+
|
|
769
|
+
def get_supported_search_types(self) -> List[str]:
|
|
770
|
+
"""Get the supported search types for this vector database."""
|
|
771
|
+
return [] # SingleStore doesn't use SearchType enum
|
|
@@ -11,9 +11,10 @@ except ImportError as e:
|
|
|
11
11
|
msg = "The `surrealdb` package is not installed. Please install it via `pip install surrealdb`."
|
|
12
12
|
raise ImportError(msg) from e
|
|
13
13
|
|
|
14
|
+
from agno.filters import FilterExpr
|
|
14
15
|
from agno.knowledge.document import Document
|
|
15
16
|
from agno.knowledge.embedder import Embedder
|
|
16
|
-
from agno.utils.log import log_debug, log_error, log_info
|
|
17
|
+
from agno.utils.log import log_debug, log_error, log_info, log_warning
|
|
17
18
|
from agno.vectordb.base import VectorDb
|
|
18
19
|
from agno.vectordb.distance import Distance
|
|
19
20
|
|
|
@@ -30,12 +31,6 @@ class SurrealDb(VectorDb):
|
|
|
30
31
|
DEFINE INDEX IF NOT EXISTS vector_idx ON {collection} FIELDS embedding HNSW DIMENSION {dimensions} DIST {distance};
|
|
31
32
|
"""
|
|
32
33
|
|
|
33
|
-
DOC_EXISTS_QUERY: Final[str] = """
|
|
34
|
-
SELECT * FROM {collection}
|
|
35
|
-
WHERE content = $content
|
|
36
|
-
LIMIT 1
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
34
|
NAME_EXISTS_QUERY: Final[str] = """
|
|
40
35
|
SELECT * FROM {collection}
|
|
41
36
|
WHERE meta_data.name = $name
|
|
@@ -107,11 +102,13 @@ class SurrealDb(VectorDb):
|
|
|
107
102
|
m: int = 12,
|
|
108
103
|
search_ef: int = 40,
|
|
109
104
|
embedder: Optional[Embedder] = None,
|
|
105
|
+
name: Optional[str] = None,
|
|
106
|
+
description: Optional[str] = None,
|
|
107
|
+
id: Optional[str] = None,
|
|
110
108
|
):
|
|
111
109
|
"""Initialize SurrealDB connection.
|
|
112
110
|
|
|
113
111
|
Args:
|
|
114
|
-
url: SurrealDB server URL (e.g. ws://localhost:8000/rpc)
|
|
115
112
|
client: A blocking connection, either HTTP or WS
|
|
116
113
|
async_client: An async connection, either HTTP or WS (default: None)
|
|
117
114
|
collection: Collection name to store documents (default: documents)
|
|
@@ -122,6 +119,17 @@ class SurrealDb(VectorDb):
|
|
|
122
119
|
embedder: Embedder instance for creating embeddings (default: OpenAIEmbedder)
|
|
123
120
|
|
|
124
121
|
"""
|
|
122
|
+
# Dynamic ID generation based on unique identifiers
|
|
123
|
+
if id is None:
|
|
124
|
+
from agno.utils.string import generate_id
|
|
125
|
+
|
|
126
|
+
client_info = str(client) if client else str(async_client) if async_client else "default"
|
|
127
|
+
seed = f"{client_info}#{collection}"
|
|
128
|
+
id = generate_id(seed)
|
|
129
|
+
|
|
130
|
+
# Initialize base class with name, description, and generated ID
|
|
131
|
+
super().__init__(id=id, name=name, description=description)
|
|
132
|
+
|
|
125
133
|
# Embedder for embedding the document contents
|
|
126
134
|
if embedder is None:
|
|
127
135
|
from agno.knowledge.embedder.openai import OpenAIEmbedder
|
|
@@ -131,7 +139,6 @@ class SurrealDb(VectorDb):
|
|
|
131
139
|
self.embedder: Embedder = embedder
|
|
132
140
|
self.dimensions = self.embedder.dimensions
|
|
133
141
|
self.collection = collection
|
|
134
|
-
|
|
135
142
|
# Convert Distance enum to SurrealDB distance type
|
|
136
143
|
self.distance = {Distance.cosine: "COSINE", Distance.l2: "EUCLIDEAN", Distance.max_inner_product: "DOT"}[
|
|
137
144
|
distance
|
|
@@ -208,23 +215,6 @@ class SurrealDb(VectorDb):
|
|
|
208
215
|
)
|
|
209
216
|
self.client.query(query)
|
|
210
217
|
|
|
211
|
-
def doc_exists(self, document: Document) -> bool:
|
|
212
|
-
"""Check if a document exists by its content.
|
|
213
|
-
|
|
214
|
-
Args:
|
|
215
|
-
document: The document to check.
|
|
216
|
-
|
|
217
|
-
Returns:
|
|
218
|
-
True if the document exists, False otherwise.
|
|
219
|
-
|
|
220
|
-
"""
|
|
221
|
-
log_debug(f"Checking if document exists: {document.content}")
|
|
222
|
-
result = self.client.query(
|
|
223
|
-
self.DOC_EXISTS_QUERY.format(collection=self.collection),
|
|
224
|
-
{"content": document.content},
|
|
225
|
-
)
|
|
226
|
-
return bool(self._extract_result(result))
|
|
227
|
-
|
|
228
218
|
def name_exists(self, name: str) -> bool:
|
|
229
219
|
"""Check if a document exists by its name.
|
|
230
220
|
|
|
@@ -306,7 +296,9 @@ class SurrealDb(VectorDb):
|
|
|
306
296
|
thing = f"{self.collection}:{doc.id}" if doc.id else self.collection
|
|
307
297
|
self.client.query(self.UPSERT_QUERY.format(thing=thing), data)
|
|
308
298
|
|
|
309
|
-
def search(
|
|
299
|
+
def search(
|
|
300
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
301
|
+
) -> List[Document]:
|
|
310
302
|
"""Search for similar documents.
|
|
311
303
|
|
|
312
304
|
Args:
|
|
@@ -318,6 +310,9 @@ class SurrealDb(VectorDb):
|
|
|
318
310
|
A list of documents that are similar to the query.
|
|
319
311
|
|
|
320
312
|
"""
|
|
313
|
+
if isinstance(filters, List):
|
|
314
|
+
log_warning("Filters Expressions are not supported in SurrealDB. No filters will be applied.")
|
|
315
|
+
filters = None
|
|
321
316
|
query_embedding = self.embedder.get_embedding(query)
|
|
322
317
|
if query_embedding is None:
|
|
323
318
|
log_error(f"Error getting embedding for Query: {query}")
|
|
@@ -475,19 +470,6 @@ class SurrealDb(VectorDb):
|
|
|
475
470
|
),
|
|
476
471
|
)
|
|
477
472
|
|
|
478
|
-
async def async_doc_exists(self, document: Document) -> bool:
|
|
479
|
-
"""Check if a document exists by its content asynchronously.
|
|
480
|
-
|
|
481
|
-
Returns:
|
|
482
|
-
True if the document exists, False otherwise.
|
|
483
|
-
|
|
484
|
-
"""
|
|
485
|
-
response = await self.async_client.query(
|
|
486
|
-
self.DOC_EXISTS_QUERY.format(collection=self.collection),
|
|
487
|
-
{"content": document.content},
|
|
488
|
-
)
|
|
489
|
-
return bool(self._extract_result(response))
|
|
490
|
-
|
|
491
473
|
async def async_name_exists(self, name: str) -> bool:
|
|
492
474
|
"""Check if a document exists by its name asynchronously.
|
|
493
475
|
|
|
@@ -548,7 +530,7 @@ class SurrealDb(VectorDb):
|
|
|
548
530
|
self,
|
|
549
531
|
query: str,
|
|
550
532
|
limit: int = 5,
|
|
551
|
-
filters: Optional[Dict[str, Any]] = None,
|
|
533
|
+
filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
|
|
552
534
|
) -> List[Document]:
|
|
553
535
|
"""Search for similar documents asynchronously.
|
|
554
536
|
|
|
@@ -561,6 +543,10 @@ class SurrealDb(VectorDb):
|
|
|
561
543
|
A list of documents that are similar to the query.
|
|
562
544
|
|
|
563
545
|
"""
|
|
546
|
+
if isinstance(filters, List):
|
|
547
|
+
log_warning("Filters Expressions are not supported in SurrealDB. No filters will be applied.")
|
|
548
|
+
filters = None
|
|
549
|
+
|
|
564
550
|
query_embedding = self.embedder.get_embedding(query)
|
|
565
551
|
if query_embedding is None:
|
|
566
552
|
log_error(f"Error getting embedding for Query: {query}")
|
|
@@ -671,3 +657,7 @@ class SurrealDb(VectorDb):
|
|
|
671
657
|
except Exception as e:
|
|
672
658
|
log_error(f"Error updating metadata for content_id '{content_id}': {e}")
|
|
673
659
|
raise
|
|
660
|
+
|
|
661
|
+
def get_supported_search_types(self) -> List[str]:
|
|
662
|
+
"""Get the supported search types for this vector database."""
|
|
663
|
+
return [] # SurrealDb doesn't use SearchType enum
|
|
@@ -1,5 +1,5 @@
|
|
|
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
|
try:
|
|
5
5
|
from upstash_vector import Index, Vector
|
|
@@ -9,10 +9,11 @@ except ImportError:
|
|
|
9
9
|
"The `upstash-vector` package is not installed, please install using `pip install upstash-vector`"
|
|
10
10
|
)
|
|
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_info, logger
|
|
16
|
+
from agno.utils.log import log_info, log_warning, logger
|
|
16
17
|
from agno.vectordb.base import VectorDb
|
|
17
18
|
|
|
18
19
|
DEFAULT_NAMESPACE = ""
|
|
@@ -32,6 +33,8 @@ class UpstashVectorDb(VectorDb):
|
|
|
32
33
|
embedder (Optional[Embedder], optional): The embedder to use. If None, uses Upstash hosted embedding models.
|
|
33
34
|
namespace (Optional[str], optional): The namespace to use. Defaults to DEFAULT_NAMESPACE.
|
|
34
35
|
reranker (Optional[Reranker], optional): The reranker to use. Defaults to None.
|
|
36
|
+
name (Optional[str], optional): The name of the vector database. Defaults to None.
|
|
37
|
+
description (Optional[str], optional): The description of the vector database. Defaults to None.
|
|
35
38
|
**kwargs: Additional keyword arguments.
|
|
36
39
|
"""
|
|
37
40
|
|
|
@@ -45,8 +48,28 @@ class UpstashVectorDb(VectorDb):
|
|
|
45
48
|
embedder: Optional[Embedder] = None,
|
|
46
49
|
namespace: Optional[str] = DEFAULT_NAMESPACE,
|
|
47
50
|
reranker: Optional[Reranker] = None,
|
|
51
|
+
name: Optional[str] = None,
|
|
52
|
+
description: Optional[str] = None,
|
|
53
|
+
id: Optional[str] = None,
|
|
48
54
|
**kwargs: Any,
|
|
49
55
|
) -> None:
|
|
56
|
+
# Validate required parameters
|
|
57
|
+
if not url:
|
|
58
|
+
raise ValueError("URL must be provided.")
|
|
59
|
+
if not token:
|
|
60
|
+
raise ValueError("Token must be provided.")
|
|
61
|
+
|
|
62
|
+
# Dynamic ID generation based on unique identifiers
|
|
63
|
+
if id is None:
|
|
64
|
+
from agno.utils.string import generate_id
|
|
65
|
+
|
|
66
|
+
namespace_identifier = namespace or DEFAULT_NAMESPACE
|
|
67
|
+
seed = f"{url}#{namespace_identifier}"
|
|
68
|
+
id = generate_id(seed)
|
|
69
|
+
|
|
70
|
+
# Initialize base class with name, description, and generated ID
|
|
71
|
+
super().__init__(id=id, name=name, description=description)
|
|
72
|
+
|
|
50
73
|
self._index: Optional[Index] = None
|
|
51
74
|
self.url: str = url
|
|
52
75
|
self.token: str = token
|
|
@@ -56,7 +79,6 @@ class UpstashVectorDb(VectorDb):
|
|
|
56
79
|
self.namespace: str = namespace if namespace is not None else DEFAULT_NAMESPACE
|
|
57
80
|
self.kwargs: Dict[str, Any] = kwargs
|
|
58
81
|
self.use_upstash_embeddings: bool = embedder is None
|
|
59
|
-
|
|
60
82
|
if embedder is None:
|
|
61
83
|
logger.warning(
|
|
62
84
|
"You have not provided an embedder, using Upstash hosted embedding models. "
|
|
@@ -303,7 +325,7 @@ class UpstashVectorDb(VectorDb):
|
|
|
303
325
|
self,
|
|
304
326
|
query: str,
|
|
305
327
|
limit: int = 5,
|
|
306
|
-
filters: Optional[Dict[str, Any]] = None,
|
|
328
|
+
filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
|
|
307
329
|
namespace: Optional[str] = None,
|
|
308
330
|
) -> List[Document]:
|
|
309
331
|
"""Search for documents in the index.
|
|
@@ -316,7 +338,9 @@ class UpstashVectorDb(VectorDb):
|
|
|
316
338
|
List[Document]: List of matching documents.
|
|
317
339
|
"""
|
|
318
340
|
_namespace = self.namespace if namespace is None else namespace
|
|
319
|
-
|
|
341
|
+
if isinstance(filters, List):
|
|
342
|
+
log_warning("Filters Expressions are not supported in UpstashDB. No filters will be applied.")
|
|
343
|
+
filters = None
|
|
320
344
|
filter_str = "" if filters is None else str(filters)
|
|
321
345
|
|
|
322
346
|
if not self.use_upstash_embeddings and self.embedder is not None:
|
|
@@ -602,7 +626,7 @@ class UpstashVectorDb(VectorDb):
|
|
|
602
626
|
self.index.upsert(vectors, namespace=_namespace)
|
|
603
627
|
|
|
604
628
|
async def async_search(
|
|
605
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
629
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
606
630
|
) -> List[Document]:
|
|
607
631
|
raise NotImplementedError(f"Async not supported on {self.__class__.__name__}.")
|
|
608
632
|
|
|
@@ -688,3 +712,7 @@ class UpstashVectorDb(VectorDb):
|
|
|
688
712
|
except Exception as e:
|
|
689
713
|
logger.error(f"Error updating metadata for content_id '{content_id}': {e}")
|
|
690
714
|
raise
|
|
715
|
+
|
|
716
|
+
def get_supported_search_types(self) -> List[str]:
|
|
717
|
+
"""Get the supported search types for this vector database."""
|
|
718
|
+
return [] # UpstashVectorDb doesn't use SearchType enum
|
|
@@ -3,7 +3,7 @@ import json
|
|
|
3
3
|
import uuid
|
|
4
4
|
from hashlib import md5
|
|
5
5
|
from os import getenv
|
|
6
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
6
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
7
7
|
|
|
8
8
|
try:
|
|
9
9
|
from warnings import filterwarnings
|
|
@@ -18,10 +18,11 @@ try:
|
|
|
18
18
|
except ImportError:
|
|
19
19
|
raise ImportError("Weaviate is not installed. Install using 'pip install weaviate-client'.")
|
|
20
20
|
|
|
21
|
+
from agno.filters import FilterExpr
|
|
21
22
|
from agno.knowledge.document import Document
|
|
22
23
|
from agno.knowledge.embedder import Embedder
|
|
23
24
|
from agno.knowledge.reranker.base import Reranker
|
|
24
|
-
from agno.utils.log import log_debug, log_info, logger
|
|
25
|
+
from agno.utils.log import log_debug, log_info, log_warning, logger
|
|
25
26
|
from agno.vectordb.base import VectorDb
|
|
26
27
|
from agno.vectordb.search import SearchType
|
|
27
28
|
from agno.vectordb.weaviate.index import Distance, VectorIndex
|
|
@@ -41,6 +42,9 @@ class Weaviate(VectorDb):
|
|
|
41
42
|
local: bool = False,
|
|
42
43
|
# Collection params
|
|
43
44
|
collection: str = "default",
|
|
45
|
+
name: Optional[str] = None,
|
|
46
|
+
description: Optional[str] = None,
|
|
47
|
+
id: Optional[str] = None,
|
|
44
48
|
vector_index: VectorIndex = VectorIndex.HNSW,
|
|
45
49
|
distance: Distance = Distance.COSINE,
|
|
46
50
|
# Search/Embedding params
|
|
@@ -49,6 +53,17 @@ class Weaviate(VectorDb):
|
|
|
49
53
|
reranker: Optional[Reranker] = None,
|
|
50
54
|
hybrid_search_alpha: float = 0.5,
|
|
51
55
|
):
|
|
56
|
+
# Dynamic ID generation based on unique identifiers
|
|
57
|
+
if id is None:
|
|
58
|
+
from agno.utils.string import generate_id
|
|
59
|
+
|
|
60
|
+
connection_identifier = wcd_url or "local" if local else "default"
|
|
61
|
+
seed = f"{connection_identifier}#{collection}"
|
|
62
|
+
id = generate_id(seed)
|
|
63
|
+
|
|
64
|
+
# Initialize base class with name, description, and generated ID
|
|
65
|
+
super().__init__(id=id, name=name, description=description)
|
|
66
|
+
|
|
52
67
|
# Connection setup
|
|
53
68
|
self.wcd_url = wcd_url or getenv("WCD_URL")
|
|
54
69
|
self.wcd_api_key = wcd_api_key or getenv("WCD_API_KEY")
|
|
@@ -232,7 +247,9 @@ class Weaviate(VectorDb):
|
|
|
232
247
|
continue
|
|
233
248
|
|
|
234
249
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
235
|
-
|
|
250
|
+
# Include content_hash in ID to ensure uniqueness across different content hashes
|
|
251
|
+
base_id = document.id or md5(cleaned_content.encode()).hexdigest()
|
|
252
|
+
record_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
|
|
236
253
|
doc_uuid = uuid.UUID(hex=record_id[:32])
|
|
237
254
|
|
|
238
255
|
# Merge filters with metadata
|
|
@@ -323,7 +340,9 @@ class Weaviate(VectorDb):
|
|
|
323
340
|
|
|
324
341
|
# Clean content and generate UUID
|
|
325
342
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
326
|
-
|
|
343
|
+
# Include content_hash in ID to ensure uniqueness across different content hashes
|
|
344
|
+
base_id = document.id or md5(cleaned_content.encode()).hexdigest()
|
|
345
|
+
record_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
|
|
327
346
|
doc_uuid = uuid.UUID(hex=record_id[:32])
|
|
328
347
|
|
|
329
348
|
# Serialize meta_data to JSON string
|
|
@@ -378,7 +397,9 @@ class Weaviate(VectorDb):
|
|
|
378
397
|
await self.async_insert(content_hash=content_hash, documents=documents, filters=filters)
|
|
379
398
|
return
|
|
380
399
|
|
|
381
|
-
def search(
|
|
400
|
+
def search(
|
|
401
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
402
|
+
) -> List[Document]:
|
|
382
403
|
"""
|
|
383
404
|
Perform a search based on the configured search type.
|
|
384
405
|
|
|
@@ -390,6 +411,9 @@ class Weaviate(VectorDb):
|
|
|
390
411
|
Returns:
|
|
391
412
|
List[Document]: List of matching documents.
|
|
392
413
|
"""
|
|
414
|
+
if isinstance(filters, List):
|
|
415
|
+
log_warning("Filters Expressions are not supported in Weaviate. No filters will be applied.")
|
|
416
|
+
filters = None
|
|
393
417
|
if self.search_type == SearchType.vector:
|
|
394
418
|
return self.vector_search(query, limit, filters)
|
|
395
419
|
elif self.search_type == SearchType.keyword:
|
|
@@ -401,7 +425,7 @@ class Weaviate(VectorDb):
|
|
|
401
425
|
return []
|
|
402
426
|
|
|
403
427
|
async def async_search(
|
|
404
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
428
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
405
429
|
) -> List[Document]:
|
|
406
430
|
"""
|
|
407
431
|
Perform a search based on the configured search type asynchronously.
|
|
@@ -414,6 +438,9 @@ class Weaviate(VectorDb):
|
|
|
414
438
|
Returns:
|
|
415
439
|
List[Document]: List of matching documents.
|
|
416
440
|
"""
|
|
441
|
+
if isinstance(filters, List):
|
|
442
|
+
log_warning("Filters Expressions are not supported in Weaviate. No filters will be applied.")
|
|
443
|
+
filters = None
|
|
417
444
|
if self.search_type == SearchType.vector:
|
|
418
445
|
return await self.async_vector_search(query, limit, filters)
|
|
419
446
|
elif self.search_type == SearchType.keyword:
|
|
@@ -424,7 +451,9 @@ class Weaviate(VectorDb):
|
|
|
424
451
|
logger.error(f"Invalid search type '{self.search_type}'.")
|
|
425
452
|
return []
|
|
426
453
|
|
|
427
|
-
def vector_search(
|
|
454
|
+
def vector_search(
|
|
455
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
456
|
+
) -> List[Document]:
|
|
428
457
|
try:
|
|
429
458
|
query_embedding = self.embedder.get_embedding(query)
|
|
430
459
|
if query_embedding is None:
|
|
@@ -459,7 +488,7 @@ class Weaviate(VectorDb):
|
|
|
459
488
|
self.get_client().close()
|
|
460
489
|
|
|
461
490
|
async def async_vector_search(
|
|
462
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
491
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
463
492
|
) -> List[Document]:
|
|
464
493
|
"""
|
|
465
494
|
Perform a vector search in Weaviate asynchronously.
|
|
@@ -504,7 +533,9 @@ class Weaviate(VectorDb):
|
|
|
504
533
|
logger.error(f"Error searching for documents: {e}")
|
|
505
534
|
return []
|
|
506
535
|
|
|
507
|
-
def keyword_search(
|
|
536
|
+
def keyword_search(
|
|
537
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
538
|
+
) -> List[Document]:
|
|
508
539
|
try:
|
|
509
540
|
collection = self.get_client().collections.get(self.collection)
|
|
510
541
|
filter_expr = self._build_filter_expression(filters)
|
|
@@ -535,7 +566,7 @@ class Weaviate(VectorDb):
|
|
|
535
566
|
self.get_client().close()
|
|
536
567
|
|
|
537
568
|
async def async_keyword_search(
|
|
538
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
569
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
539
570
|
) -> List[Document]:
|
|
540
571
|
"""
|
|
541
572
|
Perform a keyword search in Weaviate asynchronously.
|
|
@@ -576,7 +607,9 @@ class Weaviate(VectorDb):
|
|
|
576
607
|
logger.error(f"Error searching for documents: {e}")
|
|
577
608
|
return []
|
|
578
609
|
|
|
579
|
-
def hybrid_search(
|
|
610
|
+
def hybrid_search(
|
|
611
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
612
|
+
) -> List[Document]:
|
|
580
613
|
try:
|
|
581
614
|
query_embedding = self.embedder.get_embedding(query)
|
|
582
615
|
if query_embedding is None:
|
|
@@ -614,7 +647,7 @@ class Weaviate(VectorDb):
|
|
|
614
647
|
self.get_client().close()
|
|
615
648
|
|
|
616
649
|
async def async_hybrid_search(
|
|
617
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
650
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
618
651
|
) -> List[Document]:
|
|
619
652
|
"""
|
|
620
653
|
Perform a hybrid search combining vector and keyword search in Weaviate asynchronously.
|
|
@@ -835,7 +868,7 @@ class Weaviate(VectorDb):
|
|
|
835
868
|
"""Indicate that upsert functionality is available."""
|
|
836
869
|
return True
|
|
837
870
|
|
|
838
|
-
def _build_filter_expression(self, filters: Optional[Dict[str, Any]]):
|
|
871
|
+
def _build_filter_expression(self, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]]):
|
|
839
872
|
"""
|
|
840
873
|
Build a filter expression for Weaviate queries.
|
|
841
874
|
|
|
@@ -847,7 +880,9 @@ class Weaviate(VectorDb):
|
|
|
847
880
|
"""
|
|
848
881
|
if not filters:
|
|
849
882
|
return None
|
|
850
|
-
|
|
883
|
+
if isinstance(filters, List):
|
|
884
|
+
log_warning("Filters Expressions are not supported in Weaviate. No filters will be applied.")
|
|
885
|
+
return None
|
|
851
886
|
try:
|
|
852
887
|
# Create a filter for each key-value pair
|
|
853
888
|
filter_conditions = []
|
|
@@ -968,3 +1003,7 @@ class Weaviate(VectorDb):
|
|
|
968
1003
|
except Exception as e:
|
|
969
1004
|
logger.error(f"Error deleting documents by content_hash '{content_hash}': {e}")
|
|
970
1005
|
return False
|
|
1006
|
+
|
|
1007
|
+
def get_supported_search_types(self) -> List[str]:
|
|
1008
|
+
"""Get the supported search types for this vector database."""
|
|
1009
|
+
return [SearchType.vector, SearchType.keyword, SearchType.hybrid]
|
agno/workflow/__init__.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from agno.workflow.agent import WorkflowAgent
|
|
1
2
|
from agno.workflow.condition import Condition
|
|
2
3
|
from agno.workflow.loop import Loop
|
|
3
4
|
from agno.workflow.parallel import Parallel
|
|
@@ -9,6 +10,7 @@ from agno.workflow.workflow import Workflow
|
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
11
12
|
"Workflow",
|
|
13
|
+
"WorkflowAgent",
|
|
12
14
|
"Steps",
|
|
13
15
|
"Step",
|
|
14
16
|
"Loop",
|