agno 2.2.13__py3-none-any.whl → 2.4.3__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/__init__.py +6 -0
- agno/agent/agent.py +5252 -3145
- agno/agent/remote.py +525 -0
- agno/api/api.py +2 -0
- agno/client/__init__.py +3 -0
- agno/client/a2a/__init__.py +10 -0
- agno/client/a2a/client.py +554 -0
- agno/client/a2a/schemas.py +112 -0
- agno/client/a2a/utils.py +369 -0
- agno/client/os.py +2669 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/manager.py +2 -2
- agno/db/base.py +927 -6
- agno/db/dynamo/dynamo.py +788 -2
- agno/db/dynamo/schemas.py +128 -0
- agno/db/dynamo/utils.py +26 -3
- agno/db/firestore/firestore.py +674 -50
- agno/db/firestore/schemas.py +41 -0
- agno/db/firestore/utils.py +25 -10
- agno/db/gcs_json/gcs_json_db.py +506 -3
- agno/db/gcs_json/utils.py +14 -2
- agno/db/in_memory/in_memory_db.py +203 -4
- agno/db/in_memory/utils.py +14 -2
- agno/db/json/json_db.py +498 -2
- agno/db/json/utils.py +14 -2
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/utils.py +19 -0
- agno/db/migrations/v1_to_v2.py +54 -16
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +977 -0
- agno/db/mongo/async_mongo.py +1013 -39
- agno/db/mongo/mongo.py +684 -4
- agno/db/mongo/schemas.py +48 -0
- agno/db/mongo/utils.py +17 -0
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2958 -0
- agno/db/mysql/mysql.py +722 -53
- agno/db/mysql/schemas.py +77 -11
- agno/db/mysql/utils.py +151 -8
- agno/db/postgres/async_postgres.py +1254 -137
- agno/db/postgres/postgres.py +2316 -93
- agno/db/postgres/schemas.py +153 -21
- agno/db/postgres/utils.py +22 -7
- agno/db/redis/redis.py +531 -3
- agno/db/redis/schemas.py +36 -0
- agno/db/redis/utils.py +31 -15
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +20 -9
- agno/db/singlestore/schemas.py +70 -1
- agno/db/singlestore/singlestore.py +737 -74
- agno/db/singlestore/utils.py +13 -3
- agno/db/sqlite/async_sqlite.py +1069 -89
- agno/db/sqlite/schemas.py +133 -1
- agno/db/sqlite/sqlite.py +2203 -165
- agno/db/sqlite/utils.py +21 -11
- agno/db/surrealdb/models.py +25 -0
- agno/db/surrealdb/surrealdb.py +603 -1
- agno/db/utils.py +60 -0
- agno/eval/__init__.py +26 -3
- agno/eval/accuracy.py +25 -12
- agno/eval/agent_as_judge.py +871 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +10 -4
- agno/eval/reliability.py +22 -13
- agno/eval/utils.py +2 -1
- agno/exceptions.py +42 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +13 -2
- agno/knowledge/__init__.py +4 -0
- agno/knowledge/chunking/code.py +90 -0
- agno/knowledge/chunking/document.py +65 -4
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/markdown.py +102 -11
- agno/knowledge/chunking/recursive.py +2 -2
- agno/knowledge/chunking/semantic.py +130 -48
- agno/knowledge/chunking/strategy.py +18 -0
- agno/knowledge/embedder/azure_openai.py +0 -1
- agno/knowledge/embedder/google.py +1 -1
- agno/knowledge/embedder/mistral.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/openai.py +16 -12
- agno/knowledge/filesystem.py +412 -0
- agno/knowledge/knowledge.py +4261 -1199
- agno/knowledge/protocol.py +134 -0
- agno/knowledge/reader/arxiv_reader.py +3 -2
- agno/knowledge/reader/base.py +9 -7
- agno/knowledge/reader/csv_reader.py +91 -42
- agno/knowledge/reader/docx_reader.py +9 -10
- agno/knowledge/reader/excel_reader.py +225 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
- agno/knowledge/reader/firecrawl_reader.py +3 -2
- agno/knowledge/reader/json_reader.py +16 -22
- agno/knowledge/reader/markdown_reader.py +15 -14
- agno/knowledge/reader/pdf_reader.py +33 -28
- agno/knowledge/reader/pptx_reader.py +9 -10
- agno/knowledge/reader/reader_factory.py +135 -1
- agno/knowledge/reader/s3_reader.py +8 -16
- agno/knowledge/reader/tavily_reader.py +3 -3
- agno/knowledge/reader/text_reader.py +15 -14
- agno/knowledge/reader/utils/__init__.py +17 -0
- agno/knowledge/reader/utils/spreadsheet.py +114 -0
- agno/knowledge/reader/web_search_reader.py +8 -65
- agno/knowledge/reader/website_reader.py +16 -13
- agno/knowledge/reader/wikipedia_reader.py +36 -3
- agno/knowledge/reader/youtube_reader.py +3 -2
- agno/knowledge/remote_content/__init__.py +33 -0
- agno/knowledge/remote_content/config.py +266 -0
- agno/knowledge/remote_content/remote_content.py +105 -17
- agno/knowledge/utils.py +76 -22
- agno/learn/__init__.py +71 -0
- agno/learn/config.py +463 -0
- agno/learn/curate.py +185 -0
- agno/learn/machine.py +725 -0
- agno/learn/schemas.py +1114 -0
- agno/learn/stores/__init__.py +38 -0
- agno/learn/stores/decision_log.py +1156 -0
- agno/learn/stores/entity_memory.py +3275 -0
- agno/learn/stores/learned_knowledge.py +1583 -0
- agno/learn/stores/protocol.py +117 -0
- agno/learn/stores/session_context.py +1217 -0
- agno/learn/stores/user_memory.py +1495 -0
- agno/learn/stores/user_profile.py +1220 -0
- agno/learn/utils.py +209 -0
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +223 -8
- 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 +434 -59
- agno/models/aws/bedrock.py +121 -20
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +10 -6
- agno/models/azure/openai_chat.py +33 -10
- agno/models/base.py +1162 -561
- agno/models/cerebras/cerebras.py +120 -24
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +65 -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 +959 -89
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +48 -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 +88 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +24 -5
- agno/models/meta/llama.py +40 -13
- agno/models/meta/llama_openai.py +22 -21
- agno/models/metrics.py +12 -0
- agno/models/mistral/mistral.py +8 -4
- agno/models/n1n/__init__.py +3 -0
- agno/models/n1n/n1n.py +57 -0
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/__init__.py +2 -0
- agno/models/ollama/chat.py +17 -6
- agno/models/ollama/responses.py +100 -0
- agno/models/openai/__init__.py +2 -0
- agno/models/openai/chat.py +117 -26
- agno/models/openai/open_responses.py +46 -0
- agno/models/openai/responses.py +110 -32
- agno/models/openrouter/__init__.py +2 -0
- agno/models/openrouter/openrouter.py +67 -2
- agno/models/openrouter/responses.py +146 -0
- agno/models/perplexity/perplexity.py +19 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +19 -2
- agno/models/response.py +20 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/claude.py +124 -4
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +467 -137
- agno/os/auth.py +253 -5
- agno/os/config.py +22 -0
- agno/os/interfaces/a2a/a2a.py +7 -6
- agno/os/interfaces/a2a/router.py +635 -26
- agno/os/interfaces/a2a/utils.py +32 -33
- agno/os/interfaces/agui/agui.py +5 -3
- agno/os/interfaces/agui/router.py +26 -16
- agno/os/interfaces/agui/utils.py +97 -57
- agno/os/interfaces/base.py +7 -7
- agno/os/interfaces/slack/router.py +16 -7
- agno/os/interfaces/slack/slack.py +7 -7
- agno/os/interfaces/whatsapp/router.py +35 -7
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/interfaces/whatsapp/whatsapp.py +11 -8
- agno/os/managers.py +326 -0
- agno/os/mcp.py +652 -79
- agno/os/middleware/__init__.py +4 -0
- agno/os/middleware/jwt.py +718 -115
- agno/os/middleware/trailing_slash.py +27 -0
- agno/os/router.py +105 -1558
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +655 -0
- agno/os/routers/agents/schema.py +288 -0
- agno/os/routers/components/__init__.py +3 -0
- agno/os/routers/components/components.py +475 -0
- agno/os/routers/database.py +155 -0
- agno/os/routers/evals/evals.py +111 -18
- agno/os/routers/evals/schemas.py +38 -5
- agno/os/routers/evals/utils.py +80 -11
- agno/os/routers/health.py +3 -3
- agno/os/routers/knowledge/knowledge.py +284 -35
- agno/os/routers/knowledge/schemas.py +14 -2
- agno/os/routers/memory/memory.py +274 -11
- agno/os/routers/memory/schemas.py +44 -3
- agno/os/routers/metrics/metrics.py +30 -15
- agno/os/routers/metrics/schemas.py +10 -6
- agno/os/routers/registry/__init__.py +3 -0
- agno/os/routers/registry/registry.py +337 -0
- agno/os/routers/session/session.py +143 -14
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +550 -0
- agno/os/routers/teams/schema.py +280 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +549 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +757 -0
- agno/os/routers/workflows/schema.py +139 -0
- agno/os/schema.py +157 -584
- agno/os/scopes.py +469 -0
- agno/os/settings.py +3 -0
- agno/os/utils.py +574 -185
- agno/reasoning/anthropic.py +85 -1
- agno/reasoning/azure_ai_foundry.py +93 -1
- agno/reasoning/deepseek.py +102 -2
- agno/reasoning/default.py +6 -7
- agno/reasoning/gemini.py +87 -3
- agno/reasoning/groq.py +109 -2
- agno/reasoning/helpers.py +6 -7
- agno/reasoning/manager.py +1238 -0
- agno/reasoning/ollama.py +93 -1
- agno/reasoning/openai.py +115 -1
- agno/reasoning/vertexai.py +85 -1
- agno/registry/__init__.py +3 -0
- agno/registry/registry.py +68 -0
- agno/remote/__init__.py +3 -0
- agno/remote/base.py +581 -0
- agno/run/__init__.py +2 -4
- agno/run/agent.py +134 -19
- agno/run/base.py +49 -1
- agno/run/cancel.py +65 -52
- agno/run/cancellation_management/__init__.py +9 -0
- agno/run/cancellation_management/base.py +78 -0
- agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
- agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +111 -19
- agno/run/workflow.py +2 -1
- agno/session/agent.py +57 -92
- agno/session/summary.py +1 -1
- agno/session/team.py +62 -115
- agno/session/workflow.py +353 -57
- agno/skills/__init__.py +17 -0
- agno/skills/agent_skills.py +377 -0
- agno/skills/errors.py +32 -0
- agno/skills/loaders/__init__.py +4 -0
- agno/skills/loaders/base.py +27 -0
- agno/skills/loaders/local.py +216 -0
- agno/skills/skill.py +65 -0
- agno/skills/utils.py +107 -0
- agno/skills/validator.py +277 -0
- agno/table.py +10 -0
- agno/team/__init__.py +5 -1
- agno/team/remote.py +447 -0
- agno/team/team.py +3769 -2202
- agno/tools/brandfetch.py +27 -18
- agno/tools/browserbase.py +225 -16
- agno/tools/crawl4ai.py +3 -0
- agno/tools/duckduckgo.py +25 -71
- agno/tools/exa.py +0 -21
- agno/tools/file.py +14 -13
- agno/tools/file_generation.py +12 -6
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +94 -113
- agno/tools/google_bigquery.py +11 -2
- agno/tools/google_drive.py +4 -3
- agno/tools/knowledge.py +9 -4
- agno/tools/mcp/mcp.py +301 -18
- agno/tools/mcp/multi_mcp.py +269 -14
- agno/tools/mem0.py +11 -10
- agno/tools/memory.py +47 -46
- agno/tools/mlx_transcribe.py +10 -7
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/parallel.py +0 -7
- agno/tools/postgres.py +76 -36
- agno/tools/python.py +14 -6
- agno/tools/reasoning.py +30 -23
- agno/tools/redshift.py +406 -0
- agno/tools/shopify.py +1519 -0
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +4 -1
- agno/tools/toolkit.py +253 -18
- agno/tools/websearch.py +93 -0
- agno/tools/website.py +1 -1
- agno/tools/wikipedia.py +1 -1
- agno/tools/workflow.py +56 -48
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +161 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +112 -0
- agno/utils/agent.py +251 -10
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +264 -7
- agno/utils/hooks.py +111 -3
- agno/utils/http.py +161 -2
- agno/utils/mcp.py +49 -8
- agno/utils/media.py +22 -1
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +20 -5
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/os.py +0 -0
- agno/utils/print_response/agent.py +99 -16
- agno/utils/print_response/team.py +223 -24
- agno/utils/print_response/workflow.py +0 -2
- agno/utils/prompts.py +8 -6
- agno/utils/remote.py +23 -0
- agno/utils/response.py +1 -13
- agno/utils/string.py +91 -2
- agno/utils/team.py +62 -12
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +15 -2
- agno/vectordb/cassandra/cassandra.py +1 -1
- agno/vectordb/chroma/__init__.py +2 -1
- agno/vectordb/chroma/chromadb.py +468 -23
- agno/vectordb/clickhouse/clickhousedb.py +1 -1
- agno/vectordb/couchbase/couchbase.py +6 -2
- agno/vectordb/lancedb/lance_db.py +7 -38
- agno/vectordb/lightrag/lightrag.py +7 -6
- agno/vectordb/milvus/milvus.py +118 -84
- agno/vectordb/mongodb/__init__.py +2 -1
- agno/vectordb/mongodb/mongodb.py +14 -31
- agno/vectordb/pgvector/pgvector.py +120 -66
- agno/vectordb/pineconedb/pineconedb.py +2 -19
- agno/vectordb/qdrant/__init__.py +2 -1
- agno/vectordb/qdrant/qdrant.py +33 -56
- agno/vectordb/redis/__init__.py +2 -1
- agno/vectordb/redis/redisdb.py +19 -31
- agno/vectordb/singlestore/singlestore.py +17 -9
- agno/vectordb/surrealdb/surrealdb.py +2 -38
- agno/vectordb/weaviate/__init__.py +2 -1
- agno/vectordb/weaviate/weaviate.py +7 -3
- agno/workflow/__init__.py +5 -1
- agno/workflow/agent.py +2 -2
- agno/workflow/condition.py +12 -10
- agno/workflow/loop.py +28 -9
- agno/workflow/parallel.py +21 -13
- agno/workflow/remote.py +362 -0
- agno/workflow/router.py +12 -9
- agno/workflow/step.py +261 -36
- agno/workflow/steps.py +12 -8
- agno/workflow/types.py +40 -77
- agno/workflow/workflow.py +939 -213
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
- agno-2.4.3.dist-info/RECORD +677 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
- agno/tools/googlesearch.py +0 -98
- agno/tools/memori.py +0 -339
- agno-2.2.13.dist-info/RECORD +0 -575
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/top_level.txt +0 -0
agno/vectordb/qdrant/qdrant.py
CHANGED
|
@@ -102,7 +102,7 @@ class Qdrant(VectorDb):
|
|
|
102
102
|
from agno.knowledge.embedder.openai import OpenAIEmbedder
|
|
103
103
|
|
|
104
104
|
embedder = OpenAIEmbedder()
|
|
105
|
-
|
|
105
|
+
log_debug("Embedder not provided, using OpenAIEmbedder as default.")
|
|
106
106
|
|
|
107
107
|
self.embedder: Embedder = embedder
|
|
108
108
|
self.dimensions: Optional[int] = self.embedder.dimensions
|
|
@@ -259,33 +259,6 @@ class Qdrant(VectorDb):
|
|
|
259
259
|
else None,
|
|
260
260
|
)
|
|
261
261
|
|
|
262
|
-
def doc_exists(self, document: Document) -> bool:
|
|
263
|
-
"""
|
|
264
|
-
Validating if the document exists or not
|
|
265
|
-
|
|
266
|
-
Args:
|
|
267
|
-
document (Document): Document to validate
|
|
268
|
-
"""
|
|
269
|
-
if self.client:
|
|
270
|
-
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
271
|
-
doc_id = md5(cleaned_content.encode()).hexdigest()
|
|
272
|
-
collection_points = self.client.retrieve(
|
|
273
|
-
collection_name=self.collection,
|
|
274
|
-
ids=[doc_id],
|
|
275
|
-
)
|
|
276
|
-
return len(collection_points) > 0
|
|
277
|
-
return False
|
|
278
|
-
|
|
279
|
-
async def async_doc_exists(self, document: Document) -> bool:
|
|
280
|
-
"""Check if a document exists asynchronously."""
|
|
281
|
-
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
282
|
-
doc_id = md5(cleaned_content.encode()).hexdigest()
|
|
283
|
-
collection_points = await self.async_client.retrieve(
|
|
284
|
-
collection_name=self.collection,
|
|
285
|
-
ids=[doc_id],
|
|
286
|
-
)
|
|
287
|
-
return len(collection_points) > 0
|
|
288
|
-
|
|
289
262
|
def name_exists(self, name: str) -> bool:
|
|
290
263
|
"""
|
|
291
264
|
Validates if a document with the given name exists in the collection.
|
|
@@ -347,7 +320,9 @@ class Qdrant(VectorDb):
|
|
|
347
320
|
points = []
|
|
348
321
|
for document in documents:
|
|
349
322
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
350
|
-
|
|
323
|
+
# Include content_hash in ID to ensure uniqueness across different content hashes
|
|
324
|
+
base_id = document.id or md5(cleaned_content.encode()).hexdigest()
|
|
325
|
+
doc_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
|
|
351
326
|
|
|
352
327
|
# TODO(v2.0.0): Remove conditional vector naming logic
|
|
353
328
|
if self.use_named_vectors:
|
|
@@ -448,16 +423,18 @@ class Qdrant(VectorDb):
|
|
|
448
423
|
# Fall back to individual embedding
|
|
449
424
|
for doc in documents:
|
|
450
425
|
if self.search_type in [SearchType.vector, SearchType.hybrid]:
|
|
451
|
-
doc.
|
|
426
|
+
await doc.async_embed(embedder=self.embedder)
|
|
452
427
|
else:
|
|
453
428
|
# Use individual embedding
|
|
454
429
|
for doc in documents:
|
|
455
430
|
if self.search_type in [SearchType.vector, SearchType.hybrid]:
|
|
456
|
-
doc.
|
|
431
|
+
await doc.async_embed(embedder=self.embedder)
|
|
457
432
|
|
|
458
433
|
async def process_document(document):
|
|
459
434
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
460
|
-
|
|
435
|
+
# Include content_hash in ID to ensure uniqueness across different content hashes
|
|
436
|
+
base_id = document.id or md5(cleaned_content.encode()).hexdigest()
|
|
437
|
+
doc_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
|
|
461
438
|
|
|
462
439
|
if self.search_type == SearchType.vector:
|
|
463
440
|
# For vector search, maintain backward compatibility with unnamed vectors
|
|
@@ -545,13 +522,13 @@ class Qdrant(VectorDb):
|
|
|
545
522
|
log_warning("Filters Expressions are not supported in Qdrant. No filters will be applied.")
|
|
546
523
|
filters = None
|
|
547
524
|
|
|
548
|
-
|
|
525
|
+
formatted_filters = self._format_filters(filters or {}) # type: ignore
|
|
549
526
|
if self.search_type == SearchType.vector:
|
|
550
|
-
results = self._run_vector_search_sync(query, limit,
|
|
527
|
+
results = self._run_vector_search_sync(query, limit, formatted_filters=formatted_filters) # type: ignore
|
|
551
528
|
elif self.search_type == SearchType.keyword:
|
|
552
|
-
results = self._run_keyword_search_sync(query, limit,
|
|
529
|
+
results = self._run_keyword_search_sync(query, limit, formatted_filters=formatted_filters) # type: ignore
|
|
553
530
|
elif self.search_type == SearchType.hybrid:
|
|
554
|
-
results = self._run_hybrid_search_sync(query, limit,
|
|
531
|
+
results = self._run_hybrid_search_sync(query, limit, formatted_filters=formatted_filters) # type: ignore
|
|
555
532
|
else:
|
|
556
533
|
raise ValueError(f"Unsupported search type: {self.search_type}")
|
|
557
534
|
|
|
@@ -564,13 +541,13 @@ class Qdrant(VectorDb):
|
|
|
564
541
|
log_warning("Filters Expressions are not supported in Qdrant. No filters will be applied.")
|
|
565
542
|
filters = None
|
|
566
543
|
|
|
567
|
-
|
|
544
|
+
formatted_filters = self._format_filters(filters or {}) # type: ignore
|
|
568
545
|
if self.search_type == SearchType.vector:
|
|
569
|
-
results = await self._run_vector_search_async(query, limit,
|
|
546
|
+
results = await self._run_vector_search_async(query, limit, formatted_filters=formatted_filters) # type: ignore
|
|
570
547
|
elif self.search_type == SearchType.keyword:
|
|
571
|
-
results = await self._run_keyword_search_async(query, limit,
|
|
548
|
+
results = await self._run_keyword_search_async(query, limit, formatted_filters=formatted_filters) # type: ignore
|
|
572
549
|
elif self.search_type == SearchType.hybrid:
|
|
573
|
-
results = await self._run_hybrid_search_async(query, limit,
|
|
550
|
+
results = await self._run_hybrid_search_async(query, limit, formatted_filters=formatted_filters) # type: ignore
|
|
574
551
|
else:
|
|
575
552
|
raise ValueError(f"Unsupported search type: {self.search_type}")
|
|
576
553
|
|
|
@@ -580,7 +557,7 @@ class Qdrant(VectorDb):
|
|
|
580
557
|
self,
|
|
581
558
|
query: str,
|
|
582
559
|
limit: int,
|
|
583
|
-
|
|
560
|
+
formatted_filters: Optional[models.Filter],
|
|
584
561
|
) -> List[models.ScoredPoint]:
|
|
585
562
|
dense_embedding = self.embedder.get_embedding(query)
|
|
586
563
|
sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
|
|
@@ -598,7 +575,7 @@ class Qdrant(VectorDb):
|
|
|
598
575
|
with_vectors=True,
|
|
599
576
|
with_payload=True,
|
|
600
577
|
limit=limit,
|
|
601
|
-
query_filter=
|
|
578
|
+
query_filter=formatted_filters,
|
|
602
579
|
)
|
|
603
580
|
return call.points
|
|
604
581
|
|
|
@@ -606,7 +583,7 @@ class Qdrant(VectorDb):
|
|
|
606
583
|
self,
|
|
607
584
|
query: str,
|
|
608
585
|
limit: int,
|
|
609
|
-
|
|
586
|
+
formatted_filters: Optional[models.Filter],
|
|
610
587
|
) -> List[models.ScoredPoint]:
|
|
611
588
|
dense_embedding = self.embedder.get_embedding(query)
|
|
612
589
|
|
|
@@ -618,7 +595,7 @@ class Qdrant(VectorDb):
|
|
|
618
595
|
with_vectors=True,
|
|
619
596
|
with_payload=True,
|
|
620
597
|
limit=limit,
|
|
621
|
-
query_filter=
|
|
598
|
+
query_filter=formatted_filters,
|
|
622
599
|
using=self.dense_vector_name,
|
|
623
600
|
)
|
|
624
601
|
else:
|
|
@@ -629,7 +606,7 @@ class Qdrant(VectorDb):
|
|
|
629
606
|
with_vectors=True,
|
|
630
607
|
with_payload=True,
|
|
631
608
|
limit=limit,
|
|
632
|
-
query_filter=
|
|
609
|
+
query_filter=formatted_filters,
|
|
633
610
|
)
|
|
634
611
|
return call.points
|
|
635
612
|
|
|
@@ -637,7 +614,7 @@ class Qdrant(VectorDb):
|
|
|
637
614
|
self,
|
|
638
615
|
query: str,
|
|
639
616
|
limit: int,
|
|
640
|
-
|
|
617
|
+
formatted_filters: Optional[models.Filter],
|
|
641
618
|
) -> List[models.ScoredPoint]:
|
|
642
619
|
sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
|
|
643
620
|
call = self.client.query_points(
|
|
@@ -647,7 +624,7 @@ class Qdrant(VectorDb):
|
|
|
647
624
|
with_payload=True,
|
|
648
625
|
limit=limit,
|
|
649
626
|
using=self.sparse_vector_name,
|
|
650
|
-
query_filter=
|
|
627
|
+
query_filter=formatted_filters,
|
|
651
628
|
)
|
|
652
629
|
return call.points
|
|
653
630
|
|
|
@@ -655,9 +632,9 @@ class Qdrant(VectorDb):
|
|
|
655
632
|
self,
|
|
656
633
|
query: str,
|
|
657
634
|
limit: int,
|
|
658
|
-
|
|
635
|
+
formatted_filters: Optional[models.Filter],
|
|
659
636
|
) -> List[models.ScoredPoint]:
|
|
660
|
-
dense_embedding = self.embedder.
|
|
637
|
+
dense_embedding = await self.embedder.async_get_embedding(query)
|
|
661
638
|
|
|
662
639
|
# TODO(v2.0.0): Remove this conditional and always use named vectors
|
|
663
640
|
if self.use_named_vectors:
|
|
@@ -667,7 +644,7 @@ class Qdrant(VectorDb):
|
|
|
667
644
|
with_vectors=True,
|
|
668
645
|
with_payload=True,
|
|
669
646
|
limit=limit,
|
|
670
|
-
query_filter=
|
|
647
|
+
query_filter=formatted_filters,
|
|
671
648
|
using=self.dense_vector_name,
|
|
672
649
|
)
|
|
673
650
|
else:
|
|
@@ -678,7 +655,7 @@ class Qdrant(VectorDb):
|
|
|
678
655
|
with_vectors=True,
|
|
679
656
|
with_payload=True,
|
|
680
657
|
limit=limit,
|
|
681
|
-
query_filter=
|
|
658
|
+
query_filter=formatted_filters,
|
|
682
659
|
)
|
|
683
660
|
return call.points
|
|
684
661
|
|
|
@@ -686,7 +663,7 @@ class Qdrant(VectorDb):
|
|
|
686
663
|
self,
|
|
687
664
|
query: str,
|
|
688
665
|
limit: int,
|
|
689
|
-
|
|
666
|
+
formatted_filters: Optional[models.Filter],
|
|
690
667
|
) -> List[models.ScoredPoint]:
|
|
691
668
|
sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
|
|
692
669
|
call = await self.async_client.query_points(
|
|
@@ -696,7 +673,7 @@ class Qdrant(VectorDb):
|
|
|
696
673
|
with_payload=True,
|
|
697
674
|
limit=limit,
|
|
698
675
|
using=self.sparse_vector_name,
|
|
699
|
-
query_filter=
|
|
676
|
+
query_filter=formatted_filters,
|
|
700
677
|
)
|
|
701
678
|
return call.points
|
|
702
679
|
|
|
@@ -704,9 +681,9 @@ class Qdrant(VectorDb):
|
|
|
704
681
|
self,
|
|
705
682
|
query: str,
|
|
706
683
|
limit: int,
|
|
707
|
-
|
|
684
|
+
formatted_filters: Optional[models.Filter],
|
|
708
685
|
) -> List[models.ScoredPoint]:
|
|
709
|
-
dense_embedding = self.embedder.
|
|
686
|
+
dense_embedding = await self.embedder.async_get_embedding(query)
|
|
710
687
|
sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
|
|
711
688
|
call = await self.async_client.query_points(
|
|
712
689
|
collection_name=self.collection,
|
|
@@ -722,7 +699,7 @@ class Qdrant(VectorDb):
|
|
|
722
699
|
with_vectors=True,
|
|
723
700
|
with_payload=True,
|
|
724
701
|
limit=limit,
|
|
725
|
-
query_filter=
|
|
702
|
+
query_filter=formatted_filters,
|
|
726
703
|
)
|
|
727
704
|
return call.points
|
|
728
705
|
|
agno/vectordb/redis/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from agno.vectordb.redis.redisdb import RedisDB
|
|
1
|
+
from agno.vectordb.redis.redisdb import RedisDB, SearchType
|
|
2
2
|
|
|
3
3
|
# Backward compatibility alias
|
|
4
4
|
RedisVectorDb = RedisDB
|
|
@@ -6,4 +6,5 @@ RedisVectorDb = RedisDB
|
|
|
6
6
|
__all__ = [
|
|
7
7
|
"RedisVectorDb",
|
|
8
8
|
"RedisDB",
|
|
9
|
+
"SearchType",
|
|
9
10
|
]
|
agno/vectordb/redis/redisdb.py
CHANGED
|
@@ -15,7 +15,8 @@ except ImportError:
|
|
|
15
15
|
from agno.filters import FilterExpr
|
|
16
16
|
from agno.knowledge.document import Document
|
|
17
17
|
from agno.knowledge.embedder import Embedder
|
|
18
|
-
from agno.
|
|
18
|
+
from agno.knowledge.reranker.base import Reranker
|
|
19
|
+
from agno.utils.log import log_debug, log_error, log_warning
|
|
19
20
|
from agno.utils.string import hash_string_sha256
|
|
20
21
|
from agno.vectordb.base import VectorDb
|
|
21
22
|
from agno.vectordb.distance import Distance
|
|
@@ -39,6 +40,7 @@ class RedisDB(VectorDb):
|
|
|
39
40
|
search_type: SearchType = SearchType.vector,
|
|
40
41
|
distance: Distance = Distance.cosine,
|
|
41
42
|
vector_score_weight: float = 0.7,
|
|
43
|
+
reranker: Optional[Reranker] = None,
|
|
42
44
|
**redis_kwargs,
|
|
43
45
|
):
|
|
44
46
|
"""
|
|
@@ -78,7 +80,7 @@ class RedisDB(VectorDb):
|
|
|
78
80
|
from agno.knowledge.embedder.openai import OpenAIEmbedder
|
|
79
81
|
|
|
80
82
|
embedder = OpenAIEmbedder()
|
|
81
|
-
|
|
83
|
+
log_debug("Embedder not provided, using OpenAIEmbedder as default.")
|
|
82
84
|
|
|
83
85
|
self.embedder: Embedder = embedder
|
|
84
86
|
self.dimensions: Optional[int] = self.embedder.dimensions
|
|
@@ -91,8 +93,8 @@ class RedisDB(VectorDb):
|
|
|
91
93
|
self.distance: Distance = distance
|
|
92
94
|
self.vector_score_weight: float = vector_score_weight
|
|
93
95
|
|
|
94
|
-
#
|
|
95
|
-
|
|
96
|
+
# Reranker instance
|
|
97
|
+
self.reranker: Optional[Reranker] = reranker
|
|
96
98
|
|
|
97
99
|
# Create index schema
|
|
98
100
|
self.schema = self._get_schema()
|
|
@@ -184,32 +186,6 @@ class RedisDB(VectorDb):
|
|
|
184
186
|
log_error(f"Error creating Redis index: {e}")
|
|
185
187
|
raise
|
|
186
188
|
|
|
187
|
-
def doc_exists(self, document: Document) -> bool:
|
|
188
|
-
"""Check if a document exists in the index."""
|
|
189
|
-
try:
|
|
190
|
-
doc_id = document.id or hash_string_sha256(document.content)
|
|
191
|
-
return self.id_exists(doc_id)
|
|
192
|
-
except Exception as e:
|
|
193
|
-
log_error(f"Error checking if document exists: {e}")
|
|
194
|
-
return False
|
|
195
|
-
|
|
196
|
-
async def async_doc_exists(self, document: Document) -> bool:
|
|
197
|
-
"""Async version of doc_exists method."""
|
|
198
|
-
try:
|
|
199
|
-
doc_id = document.id or hash_string_sha256(document.content)
|
|
200
|
-
async_index = await self._get_async_index()
|
|
201
|
-
id_filter = Tag("id") == doc_id
|
|
202
|
-
query = FilterQuery(
|
|
203
|
-
filter_expression=id_filter,
|
|
204
|
-
return_fields=["id"],
|
|
205
|
-
num_results=1,
|
|
206
|
-
)
|
|
207
|
-
results = await async_index.query(query)
|
|
208
|
-
return len(results) > 0
|
|
209
|
-
except Exception as e:
|
|
210
|
-
log_error(f"Error checking if document exists: {e}")
|
|
211
|
-
return False
|
|
212
|
-
|
|
213
189
|
def name_exists(self, name: str) -> bool:
|
|
214
190
|
"""Check if a document with the given name exists."""
|
|
215
191
|
try:
|
|
@@ -453,6 +429,10 @@ class RedisDB(VectorDb):
|
|
|
453
429
|
# Convert results to documents
|
|
454
430
|
documents = [Document.from_dict(r) for r in results]
|
|
455
431
|
|
|
432
|
+
# Apply reranking if reranker is available
|
|
433
|
+
if self.reranker:
|
|
434
|
+
documents = self.reranker.rerank(query=query, documents=documents)
|
|
435
|
+
|
|
456
436
|
return documents
|
|
457
437
|
except Exception as e:
|
|
458
438
|
log_error(f"Error in vector search: {e}")
|
|
@@ -476,6 +456,10 @@ class RedisDB(VectorDb):
|
|
|
476
456
|
# Convert results to documents
|
|
477
457
|
documents = [Document.from_dict(p) for p in parsed]
|
|
478
458
|
|
|
459
|
+
# Apply reranking if reranker is available
|
|
460
|
+
if self.reranker:
|
|
461
|
+
documents = self.reranker.rerank(query=query, documents=documents)
|
|
462
|
+
|
|
479
463
|
return documents
|
|
480
464
|
except Exception as e:
|
|
481
465
|
log_error(f"Error in keyword search: {e}")
|
|
@@ -493,7 +477,7 @@ class RedisDB(VectorDb):
|
|
|
493
477
|
vector_field_name="embedding",
|
|
494
478
|
text=query,
|
|
495
479
|
text_field_name="content",
|
|
496
|
-
|
|
480
|
+
linear_alpha=self.vector_score_weight,
|
|
497
481
|
return_fields=["id", "name", "content"],
|
|
498
482
|
num_results=limit,
|
|
499
483
|
)
|
|
@@ -505,6 +489,10 @@ class RedisDB(VectorDb):
|
|
|
505
489
|
# Convert results to documents
|
|
506
490
|
documents = [Document.from_dict(p) for p in parsed]
|
|
507
491
|
|
|
492
|
+
# Apply reranking if reranker is available
|
|
493
|
+
if self.reranker:
|
|
494
|
+
documents = self.reranker.rerank(query=query, documents=documents)
|
|
495
|
+
|
|
508
496
|
return documents
|
|
509
497
|
except Exception as e:
|
|
510
498
|
log_error(f"Error in hybrid search: {e}")
|
|
@@ -56,7 +56,7 @@ class SingleStore(VectorDb):
|
|
|
56
56
|
from agno.knowledge.embedder.openai import OpenAIEmbedder
|
|
57
57
|
|
|
58
58
|
embedder = OpenAIEmbedder()
|
|
59
|
-
|
|
59
|
+
log_debug("Embedder not provided, using OpenAIEmbedder as default.")
|
|
60
60
|
self.embedder: Embedder = embedder
|
|
61
61
|
self.dimensions: Optional[int] = self.embedder.dimensions
|
|
62
62
|
|
|
@@ -185,8 +185,10 @@ class SingleStore(VectorDb):
|
|
|
185
185
|
for document in documents:
|
|
186
186
|
document.embed(embedder=self.embedder)
|
|
187
187
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
188
|
-
|
|
189
|
-
|
|
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
|
|
190
192
|
|
|
191
193
|
meta_data_json = json.dumps(document.meta_data)
|
|
192
194
|
usage_json = json.dumps(document.usage)
|
|
@@ -246,8 +248,10 @@ class SingleStore(VectorDb):
|
|
|
246
248
|
for document in documents:
|
|
247
249
|
document.embed(embedder=self.embedder)
|
|
248
250
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
249
|
-
|
|
250
|
-
|
|
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
|
|
251
255
|
|
|
252
256
|
meta_data_json = json.dumps(document.meta_data)
|
|
253
257
|
usage_json = json.dumps(document.usage)
|
|
@@ -548,8 +552,10 @@ class SingleStore(VectorDb):
|
|
|
548
552
|
counter = 0
|
|
549
553
|
for document in documents:
|
|
550
554
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
551
|
-
|
|
552
|
-
|
|
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
|
|
553
559
|
|
|
554
560
|
meta_data_json = json.dumps(document.meta_data)
|
|
555
561
|
usage_json = json.dumps(document.usage)
|
|
@@ -632,8 +638,10 @@ class SingleStore(VectorDb):
|
|
|
632
638
|
counter = 0
|
|
633
639
|
for document in documents:
|
|
634
640
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
635
|
-
|
|
636
|
-
|
|
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
|
|
637
645
|
|
|
638
646
|
meta_data_json = json.dumps(document.meta_data)
|
|
639
647
|
usage_json = json.dumps(document.usage)
|
|
@@ -14,7 +14,7 @@ except ImportError as e:
|
|
|
14
14
|
from agno.filters import FilterExpr
|
|
15
15
|
from agno.knowledge.document import Document
|
|
16
16
|
from agno.knowledge.embedder import Embedder
|
|
17
|
-
from agno.utils.log import log_debug, log_error,
|
|
17
|
+
from agno.utils.log import log_debug, log_error, log_warning
|
|
18
18
|
from agno.vectordb.base import VectorDb
|
|
19
19
|
from agno.vectordb.distance import Distance
|
|
20
20
|
|
|
@@ -31,12 +31,6 @@ class SurrealDb(VectorDb):
|
|
|
31
31
|
DEFINE INDEX IF NOT EXISTS vector_idx ON {collection} FIELDS embedding HNSW DIMENSION {dimensions} DIST {distance};
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
|
-
DOC_EXISTS_QUERY: Final[str] = """
|
|
35
|
-
SELECT * FROM {collection}
|
|
36
|
-
WHERE content = $content
|
|
37
|
-
LIMIT 1
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
34
|
NAME_EXISTS_QUERY: Final[str] = """
|
|
41
35
|
SELECT * FROM {collection}
|
|
42
36
|
WHERE meta_data.name = $name
|
|
@@ -141,7 +135,7 @@ class SurrealDb(VectorDb):
|
|
|
141
135
|
from agno.knowledge.embedder.openai import OpenAIEmbedder
|
|
142
136
|
|
|
143
137
|
embedder = OpenAIEmbedder()
|
|
144
|
-
|
|
138
|
+
log_debug("Embedder not provided, using OpenAIEmbedder as default.")
|
|
145
139
|
self.embedder: Embedder = embedder
|
|
146
140
|
self.dimensions = self.embedder.dimensions
|
|
147
141
|
self.collection = collection
|
|
@@ -221,23 +215,6 @@ class SurrealDb(VectorDb):
|
|
|
221
215
|
)
|
|
222
216
|
self.client.query(query)
|
|
223
217
|
|
|
224
|
-
def doc_exists(self, document: Document) -> bool:
|
|
225
|
-
"""Check if a document exists by its content.
|
|
226
|
-
|
|
227
|
-
Args:
|
|
228
|
-
document: The document to check.
|
|
229
|
-
|
|
230
|
-
Returns:
|
|
231
|
-
True if the document exists, False otherwise.
|
|
232
|
-
|
|
233
|
-
"""
|
|
234
|
-
log_debug(f"Checking if document exists: {document.content}")
|
|
235
|
-
result = self.client.query(
|
|
236
|
-
self.DOC_EXISTS_QUERY.format(collection=self.collection),
|
|
237
|
-
{"content": document.content},
|
|
238
|
-
)
|
|
239
|
-
return bool(self._extract_result(result))
|
|
240
|
-
|
|
241
218
|
def name_exists(self, name: str) -> bool:
|
|
242
219
|
"""Check if a document exists by its name.
|
|
243
220
|
|
|
@@ -493,19 +470,6 @@ class SurrealDb(VectorDb):
|
|
|
493
470
|
),
|
|
494
471
|
)
|
|
495
472
|
|
|
496
|
-
async def async_doc_exists(self, document: Document) -> bool:
|
|
497
|
-
"""Check if a document exists by its content asynchronously.
|
|
498
|
-
|
|
499
|
-
Returns:
|
|
500
|
-
True if the document exists, False otherwise.
|
|
501
|
-
|
|
502
|
-
"""
|
|
503
|
-
response = await self.async_client.query(
|
|
504
|
-
self.DOC_EXISTS_QUERY.format(collection=self.collection),
|
|
505
|
-
{"content": document.content},
|
|
506
|
-
)
|
|
507
|
-
return bool(self._extract_result(response))
|
|
508
|
-
|
|
509
473
|
async def async_name_exists(self, name: str) -> bool:
|
|
510
474
|
"""Check if a document exists by its name asynchronously.
|
|
511
475
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from agno.vectordb.weaviate.index import Distance, VectorIndex
|
|
2
|
-
from agno.vectordb.weaviate.weaviate import Weaviate
|
|
2
|
+
from agno.vectordb.weaviate.weaviate import SearchType, Weaviate
|
|
3
3
|
|
|
4
4
|
__all__ = [
|
|
5
5
|
"Distance",
|
|
6
6
|
"VectorIndex",
|
|
7
7
|
"Weaviate",
|
|
8
|
+
"SearchType",
|
|
8
9
|
]
|
|
@@ -81,7 +81,7 @@ class Weaviate(VectorDb):
|
|
|
81
81
|
from agno.knowledge.embedder.openai import OpenAIEmbedder
|
|
82
82
|
|
|
83
83
|
embedder = OpenAIEmbedder()
|
|
84
|
-
|
|
84
|
+
log_debug("Embedder not provided, using OpenAIEmbedder as default.")
|
|
85
85
|
self.embedder: Embedder = embedder
|
|
86
86
|
|
|
87
87
|
# Search setup
|
|
@@ -247,7 +247,9 @@ class Weaviate(VectorDb):
|
|
|
247
247
|
continue
|
|
248
248
|
|
|
249
249
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
250
|
-
|
|
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()
|
|
251
253
|
doc_uuid = uuid.UUID(hex=record_id[:32])
|
|
252
254
|
|
|
253
255
|
# Merge filters with metadata
|
|
@@ -338,7 +340,9 @@ class Weaviate(VectorDb):
|
|
|
338
340
|
|
|
339
341
|
# Clean content and generate UUID
|
|
340
342
|
cleaned_content = document.content.replace("\x00", "\ufffd")
|
|
341
|
-
|
|
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()
|
|
342
346
|
doc_uuid = uuid.UUID(hex=record_id[:32])
|
|
343
347
|
|
|
344
348
|
# Serialize meta_data to JSON string
|
agno/workflow/__init__.py
CHANGED
|
@@ -2,15 +2,17 @@ from agno.workflow.agent import WorkflowAgent
|
|
|
2
2
|
from agno.workflow.condition import Condition
|
|
3
3
|
from agno.workflow.loop import Loop
|
|
4
4
|
from agno.workflow.parallel import Parallel
|
|
5
|
+
from agno.workflow.remote import RemoteWorkflow
|
|
5
6
|
from agno.workflow.router import Router
|
|
6
7
|
from agno.workflow.step import Step
|
|
7
8
|
from agno.workflow.steps import Steps
|
|
8
9
|
from agno.workflow.types import StepInput, StepOutput, WorkflowExecutionInput
|
|
9
|
-
from agno.workflow.workflow import Workflow
|
|
10
|
+
from agno.workflow.workflow import Workflow, get_workflow_by_id, get_workflows
|
|
10
11
|
|
|
11
12
|
__all__ = [
|
|
12
13
|
"Workflow",
|
|
13
14
|
"WorkflowAgent",
|
|
15
|
+
"RemoteWorkflow",
|
|
14
16
|
"Steps",
|
|
15
17
|
"Step",
|
|
16
18
|
"Loop",
|
|
@@ -20,4 +22,6 @@ __all__ = [
|
|
|
20
22
|
"WorkflowExecutionInput",
|
|
21
23
|
"StepInput",
|
|
22
24
|
"StepOutput",
|
|
25
|
+
"get_workflow_by_id",
|
|
26
|
+
"get_workflows",
|
|
23
27
|
]
|
agno/workflow/agent.py
CHANGED
|
@@ -5,9 +5,9 @@ from typing import TYPE_CHECKING, Any, Callable, Optional
|
|
|
5
5
|
from agno.agent import Agent
|
|
6
6
|
from agno.models.base import Model
|
|
7
7
|
from agno.run import RunContext
|
|
8
|
-
from agno.workflow.types import WebSocketHandler
|
|
9
8
|
|
|
10
9
|
if TYPE_CHECKING:
|
|
10
|
+
from agno.os.managers import WebSocketHandler
|
|
11
11
|
from agno.session.workflow import WorkflowSession
|
|
12
12
|
from agno.workflow.types import WorkflowExecutionInput
|
|
13
13
|
|
|
@@ -190,7 +190,7 @@ Guidelines:
|
|
|
190
190
|
execution_input: "WorkflowExecutionInput",
|
|
191
191
|
run_context: RunContext,
|
|
192
192
|
stream: bool = False,
|
|
193
|
-
websocket_handler: Optional[WebSocketHandler] = None,
|
|
193
|
+
websocket_handler: Optional["WebSocketHandler"] = None,
|
|
194
194
|
) -> Callable:
|
|
195
195
|
"""
|
|
196
196
|
Create the async workflow execution tool that this agent can call.
|