agno 2.2.10__py3-none-any.whl → 2.2.12__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 +75 -48
- agno/db/dynamo/utils.py +1 -1
- agno/db/firestore/utils.py +1 -1
- agno/db/gcs_json/utils.py +1 -1
- agno/db/in_memory/utils.py +1 -1
- agno/db/json/utils.py +1 -1
- agno/db/mongo/utils.py +3 -3
- agno/db/mysql/mysql.py +1 -1
- agno/db/mysql/utils.py +1 -1
- agno/db/postgres/utils.py +1 -1
- agno/db/redis/utils.py +1 -1
- agno/db/singlestore/singlestore.py +1 -1
- agno/db/singlestore/utils.py +1 -1
- agno/db/sqlite/async_sqlite.py +1 -1
- agno/db/sqlite/sqlite.py +1 -1
- agno/db/sqlite/utils.py +1 -1
- agno/filters.py +354 -0
- agno/knowledge/chunking/agentic.py +8 -9
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/sentence_transformer.py +6 -2
- agno/knowledge/knowledge.py +43 -22
- agno/knowledge/reader/base.py +6 -2
- agno/knowledge/utils.py +20 -0
- agno/models/anthropic/claude.py +45 -9
- agno/models/base.py +4 -0
- agno/os/app.py +23 -7
- agno/os/interfaces/slack/router.py +53 -33
- agno/os/interfaces/slack/slack.py +9 -1
- agno/os/router.py +25 -1
- agno/os/routers/health.py +5 -3
- agno/os/routers/knowledge/knowledge.py +43 -17
- agno/os/routers/knowledge/schemas.py +4 -3
- agno/run/agent.py +11 -1
- agno/run/base.py +3 -2
- agno/session/agent.py +10 -5
- agno/team/team.py +57 -18
- agno/tools/file_generation.py +4 -4
- agno/tools/gmail.py +179 -0
- agno/tools/parallel.py +314 -0
- agno/utils/agent.py +22 -17
- agno/utils/gemini.py +15 -5
- agno/utils/knowledge.py +12 -5
- agno/utils/log.py +1 -0
- agno/utils/models/claude.py +2 -1
- agno/utils/print_response/agent.py +5 -4
- agno/utils/print_response/team.py +5 -4
- agno/vectordb/base.py +2 -4
- agno/vectordb/cassandra/cassandra.py +12 -5
- agno/vectordb/chroma/chromadb.py +10 -4
- agno/vectordb/clickhouse/clickhousedb.py +12 -4
- agno/vectordb/couchbase/couchbase.py +12 -3
- agno/vectordb/lancedb/lance_db.py +69 -144
- agno/vectordb/langchaindb/langchaindb.py +13 -4
- agno/vectordb/lightrag/lightrag.py +8 -3
- agno/vectordb/llamaindex/llamaindexdb.py +10 -4
- agno/vectordb/milvus/milvus.py +16 -5
- agno/vectordb/mongodb/mongodb.py +14 -3
- agno/vectordb/pgvector/pgvector.py +73 -15
- agno/vectordb/pineconedb/pineconedb.py +6 -2
- agno/vectordb/qdrant/qdrant.py +25 -13
- agno/vectordb/redis/redisdb.py +37 -30
- agno/vectordb/singlestore/singlestore.py +9 -4
- agno/vectordb/surrealdb/surrealdb.py +13 -3
- agno/vectordb/upstashdb/upstashdb.py +8 -5
- agno/vectordb/weaviate/weaviate.py +29 -12
- agno/workflow/step.py +3 -2
- agno/workflow/types.py +20 -1
- agno/workflow/workflow.py +103 -14
- {agno-2.2.10.dist-info → agno-2.2.12.dist-info}/METADATA +4 -1
- {agno-2.2.10.dist-info → agno-2.2.12.dist-info}/RECORD +73 -71
- {agno-2.2.10.dist-info → agno-2.2.12.dist-info}/WHEEL +0 -0
- {agno-2.2.10.dist-info → agno-2.2.12.dist-info}/licenses/LICENSE +0 -0
- {agno-2.2.10.dist-info → agno-2.2.12.dist-info}/top_level.txt +0 -0
agno/vectordb/redis/redisdb.py
CHANGED
|
@@ -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 redis import Redis
|
|
@@ -12,9 +12,10 @@ try:
|
|
|
12
12
|
except ImportError:
|
|
13
13
|
raise ImportError("`redis` and `redisvl` not installed. Please install using `pip install redis redisvl`")
|
|
14
14
|
|
|
15
|
+
from agno.filters import FilterExpr
|
|
15
16
|
from agno.knowledge.document import Document
|
|
16
17
|
from agno.knowledge.embedder import Embedder
|
|
17
|
-
from agno.utils.log import log_debug, log_info,
|
|
18
|
+
from agno.utils.log import log_debug, log_error, log_info, log_warning
|
|
18
19
|
from agno.utils.string import hash_string_sha256
|
|
19
20
|
from agno.vectordb.base import VectorDb
|
|
20
21
|
from agno.vectordb.distance import Distance
|
|
@@ -167,7 +168,7 @@ class RedisDB(VectorDb):
|
|
|
167
168
|
else:
|
|
168
169
|
log_debug(f"Redis index already exists: {self.index_name}")
|
|
169
170
|
except Exception as e:
|
|
170
|
-
|
|
171
|
+
log_error(f"Error creating Redis index: {e}")
|
|
171
172
|
raise
|
|
172
173
|
|
|
173
174
|
async def async_create(self) -> None:
|
|
@@ -180,7 +181,7 @@ class RedisDB(VectorDb):
|
|
|
180
181
|
if "already exists" in str(e).lower():
|
|
181
182
|
log_debug(f"Redis index already exists: {self.index_name}")
|
|
182
183
|
else:
|
|
183
|
-
|
|
184
|
+
log_error(f"Error creating Redis index: {e}")
|
|
184
185
|
raise
|
|
185
186
|
|
|
186
187
|
def doc_exists(self, document: Document) -> bool:
|
|
@@ -189,7 +190,7 @@ class RedisDB(VectorDb):
|
|
|
189
190
|
doc_id = document.id or hash_string_sha256(document.content)
|
|
190
191
|
return self.id_exists(doc_id)
|
|
191
192
|
except Exception as e:
|
|
192
|
-
|
|
193
|
+
log_error(f"Error checking if document exists: {e}")
|
|
193
194
|
return False
|
|
194
195
|
|
|
195
196
|
async def async_doc_exists(self, document: Document) -> bool:
|
|
@@ -206,7 +207,7 @@ class RedisDB(VectorDb):
|
|
|
206
207
|
results = await async_index.query(query)
|
|
207
208
|
return len(results) > 0
|
|
208
209
|
except Exception as e:
|
|
209
|
-
|
|
210
|
+
log_error(f"Error checking if document exists: {e}")
|
|
210
211
|
return False
|
|
211
212
|
|
|
212
213
|
def name_exists(self, name: str) -> bool:
|
|
@@ -221,7 +222,7 @@ class RedisDB(VectorDb):
|
|
|
221
222
|
results = self.index.query(query)
|
|
222
223
|
return len(results) > 0
|
|
223
224
|
except Exception as e:
|
|
224
|
-
|
|
225
|
+
log_error(f"Error checking if name exists: {e}")
|
|
225
226
|
return False
|
|
226
227
|
|
|
227
228
|
async def async_name_exists(self, name: str) -> bool: # type: ignore[override]
|
|
@@ -237,7 +238,7 @@ class RedisDB(VectorDb):
|
|
|
237
238
|
results = await async_index.query(query)
|
|
238
239
|
return len(results) > 0
|
|
239
240
|
except Exception as e:
|
|
240
|
-
|
|
241
|
+
log_error(f"Error checking if name exists: {e}")
|
|
241
242
|
return False
|
|
242
243
|
|
|
243
244
|
def id_exists(self, id: str) -> bool:
|
|
@@ -252,7 +253,7 @@ class RedisDB(VectorDb):
|
|
|
252
253
|
results = self.index.query(query)
|
|
253
254
|
return len(results) > 0
|
|
254
255
|
except Exception as e:
|
|
255
|
-
|
|
256
|
+
log_error(f"Error checking if ID exists: {e}")
|
|
256
257
|
return False
|
|
257
258
|
|
|
258
259
|
def content_hash_exists(self, content_hash: str) -> bool:
|
|
@@ -267,7 +268,7 @@ class RedisDB(VectorDb):
|
|
|
267
268
|
results = self.index.query(query)
|
|
268
269
|
return len(results) > 0
|
|
269
270
|
except Exception as e:
|
|
270
|
-
|
|
271
|
+
log_error(f"Error checking if content hash exists: {e}")
|
|
271
272
|
return False
|
|
272
273
|
|
|
273
274
|
def _parse_redis_hash(self, doc: Document):
|
|
@@ -314,7 +315,7 @@ class RedisDB(VectorDb):
|
|
|
314
315
|
self.index.load(parsed_documents, id_field="id")
|
|
315
316
|
log_debug(f"Inserted {len(documents)} documents with content_hash: {content_hash}")
|
|
316
317
|
except Exception as e:
|
|
317
|
-
|
|
318
|
+
log_error(f"Error inserting documents: {e}")
|
|
318
319
|
raise
|
|
319
320
|
|
|
320
321
|
async def async_insert(
|
|
@@ -334,7 +335,7 @@ class RedisDB(VectorDb):
|
|
|
334
335
|
await async_index.load(parsed_documents, id_field="id")
|
|
335
336
|
log_debug(f"Inserted {len(documents)} documents with content_hash: {content_hash}")
|
|
336
337
|
except Exception as e:
|
|
337
|
-
|
|
338
|
+
log_error(f"Error inserting documents: {e}")
|
|
338
339
|
raise
|
|
339
340
|
|
|
340
341
|
def upsert_available(self) -> bool:
|
|
@@ -368,7 +369,7 @@ class RedisDB(VectorDb):
|
|
|
368
369
|
# Insert new docs
|
|
369
370
|
self.insert(content_hash, documents, filters)
|
|
370
371
|
except Exception as e:
|
|
371
|
-
|
|
372
|
+
log_error(f"Error upserting documents: {e}")
|
|
372
373
|
raise
|
|
373
374
|
|
|
374
375
|
async def async_upsert(
|
|
@@ -400,11 +401,17 @@ class RedisDB(VectorDb):
|
|
|
400
401
|
# Insert new docs
|
|
401
402
|
await self.async_insert(content_hash, documents, filters)
|
|
402
403
|
except Exception as e:
|
|
403
|
-
|
|
404
|
+
log_error(f"Error upserting documents: {e}")
|
|
404
405
|
raise
|
|
405
406
|
|
|
406
|
-
def search(
|
|
407
|
+
def search(
|
|
408
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
409
|
+
) -> List[Document]:
|
|
407
410
|
"""Search for documents using the specified search type."""
|
|
411
|
+
|
|
412
|
+
if filters and isinstance(filters, List):
|
|
413
|
+
log_warning("Filters Expressions are not supported in Redis. No filters will be applied.")
|
|
414
|
+
filters = None
|
|
408
415
|
try:
|
|
409
416
|
if self.search_type == SearchType.vector:
|
|
410
417
|
return self.vector_search(query, limit)
|
|
@@ -415,11 +422,11 @@ class RedisDB(VectorDb):
|
|
|
415
422
|
else:
|
|
416
423
|
raise ValueError(f"Unsupported search type: {self.search_type}")
|
|
417
424
|
except Exception as e:
|
|
418
|
-
|
|
425
|
+
log_error(f"Error in search: {e}")
|
|
419
426
|
return []
|
|
420
427
|
|
|
421
428
|
async def async_search(
|
|
422
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
429
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
423
430
|
) -> List[Document]:
|
|
424
431
|
"""Async version of search method."""
|
|
425
432
|
return await asyncio.to_thread(self.search, query, limit, filters)
|
|
@@ -448,7 +455,7 @@ class RedisDB(VectorDb):
|
|
|
448
455
|
|
|
449
456
|
return documents
|
|
450
457
|
except Exception as e:
|
|
451
|
-
|
|
458
|
+
log_error(f"Error in vector search: {e}")
|
|
452
459
|
return []
|
|
453
460
|
|
|
454
461
|
def keyword_search(self, query: str, limit: int = 5) -> List[Document]:
|
|
@@ -471,7 +478,7 @@ class RedisDB(VectorDb):
|
|
|
471
478
|
|
|
472
479
|
return documents
|
|
473
480
|
except Exception as e:
|
|
474
|
-
|
|
481
|
+
log_error(f"Error in keyword search: {e}")
|
|
475
482
|
return []
|
|
476
483
|
|
|
477
484
|
def hybrid_search(self, query: str, limit: int = 5) -> List[Document]:
|
|
@@ -500,7 +507,7 @@ class RedisDB(VectorDb):
|
|
|
500
507
|
|
|
501
508
|
return documents
|
|
502
509
|
except Exception as e:
|
|
503
|
-
|
|
510
|
+
log_error(f"Error in hybrid search: {e}")
|
|
504
511
|
return []
|
|
505
512
|
|
|
506
513
|
def drop(self) -> bool: # type: ignore[override]
|
|
@@ -510,7 +517,7 @@ class RedisDB(VectorDb):
|
|
|
510
517
|
log_debug(f"Deleted Redis index: {self.index_name}")
|
|
511
518
|
return True
|
|
512
519
|
except Exception as e:
|
|
513
|
-
|
|
520
|
+
log_error(f"Error dropping Redis index: {e}")
|
|
514
521
|
return False
|
|
515
522
|
|
|
516
523
|
async def async_drop(self) -> None:
|
|
@@ -520,7 +527,7 @@ class RedisDB(VectorDb):
|
|
|
520
527
|
await async_index.delete(drop=True)
|
|
521
528
|
log_debug(f"Deleted Redis index: {self.index_name}")
|
|
522
529
|
except Exception as e:
|
|
523
|
-
|
|
530
|
+
log_error(f"Error dropping Redis index: {e}")
|
|
524
531
|
raise
|
|
525
532
|
|
|
526
533
|
def exists(self) -> bool:
|
|
@@ -528,7 +535,7 @@ class RedisDB(VectorDb):
|
|
|
528
535
|
try:
|
|
529
536
|
return self.index.exists()
|
|
530
537
|
except Exception as e:
|
|
531
|
-
|
|
538
|
+
log_error(f"Error checking if index exists: {e}")
|
|
532
539
|
return False
|
|
533
540
|
|
|
534
541
|
async def async_exists(self) -> bool:
|
|
@@ -537,7 +544,7 @@ class RedisDB(VectorDb):
|
|
|
537
544
|
async_index = await self._get_async_index()
|
|
538
545
|
return await async_index.exists()
|
|
539
546
|
except Exception as e:
|
|
540
|
-
|
|
547
|
+
log_error(f"Error checking if index exists: {e}")
|
|
541
548
|
return False
|
|
542
549
|
|
|
543
550
|
def optimize(self) -> None:
|
|
@@ -551,7 +558,7 @@ class RedisDB(VectorDb):
|
|
|
551
558
|
self.index.clear()
|
|
552
559
|
return True
|
|
553
560
|
except Exception as e:
|
|
554
|
-
|
|
561
|
+
log_error(f"Error deleting Redis index: {e}")
|
|
555
562
|
return False
|
|
556
563
|
|
|
557
564
|
def delete_by_id(self, id: str) -> bool:
|
|
@@ -562,7 +569,7 @@ class RedisDB(VectorDb):
|
|
|
562
569
|
log_debug(f"Deleted document with id '{id}' from Redis index")
|
|
563
570
|
return result > 0
|
|
564
571
|
except Exception as e:
|
|
565
|
-
|
|
572
|
+
log_error(f"Error deleting document by ID: {e}")
|
|
566
573
|
return False
|
|
567
574
|
|
|
568
575
|
def delete_by_name(self, name: str) -> bool:
|
|
@@ -588,7 +595,7 @@ class RedisDB(VectorDb):
|
|
|
588
595
|
log_debug(f"Deleted {deleted_count} documents with name '{name}'")
|
|
589
596
|
return deleted_count > 0
|
|
590
597
|
except Exception as e:
|
|
591
|
-
|
|
598
|
+
log_error(f"Error deleting documents by name: {e}")
|
|
592
599
|
return False
|
|
593
600
|
|
|
594
601
|
def delete_by_metadata(self, metadata: Dict[str, Any]) -> bool:
|
|
@@ -626,7 +633,7 @@ class RedisDB(VectorDb):
|
|
|
626
633
|
log_debug(f"Deleted {deleted_count} documents with metadata {metadata}")
|
|
627
634
|
return deleted_count > 0
|
|
628
635
|
except Exception as e:
|
|
629
|
-
|
|
636
|
+
log_error(f"Error deleting documents by metadata: {e}")
|
|
630
637
|
return False
|
|
631
638
|
|
|
632
639
|
def delete_by_content_id(self, content_id: str) -> bool:
|
|
@@ -652,7 +659,7 @@ class RedisDB(VectorDb):
|
|
|
652
659
|
log_debug(f"Deleted {deleted_count} documents with content_id '{content_id}'")
|
|
653
660
|
return deleted_count > 0
|
|
654
661
|
except Exception as e:
|
|
655
|
-
|
|
662
|
+
log_error(f"Error deleting documents by content_id: {e}")
|
|
656
663
|
return False
|
|
657
664
|
|
|
658
665
|
def update_metadata(self, content_id: str, metadata: Dict[str, Any]) -> None:
|
|
@@ -679,7 +686,7 @@ class RedisDB(VectorDb):
|
|
|
679
686
|
|
|
680
687
|
log_debug(f"Updated metadata for documents with content_id '{content_id}'")
|
|
681
688
|
except Exception as e:
|
|
682
|
-
|
|
689
|
+
log_error(f"Error updating metadata: {e}")
|
|
683
690
|
raise
|
|
684
691
|
|
|
685
692
|
def get_supported_search_types(self) -> List[str]:
|
|
@@ -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
|
|
|
@@ -282,7 +283,9 @@ class SingleStore(VectorDb):
|
|
|
282
283
|
sess.commit()
|
|
283
284
|
log_debug(f"Committed {counter} documents")
|
|
284
285
|
|
|
285
|
-
def search(
|
|
286
|
+
def search(
|
|
287
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
288
|
+
) -> List[Document]:
|
|
286
289
|
"""
|
|
287
290
|
Search for documents based on a query and optional filters.
|
|
288
291
|
|
|
@@ -294,6 +297,8 @@ class SingleStore(VectorDb):
|
|
|
294
297
|
Returns:
|
|
295
298
|
List[Document]: List of documents that match the query.
|
|
296
299
|
"""
|
|
300
|
+
if filters is not None:
|
|
301
|
+
log_warning("Filters are not supported in SingleStore. No filters will be applied.")
|
|
297
302
|
query_embedding = self.embedder.get_embedding(query)
|
|
298
303
|
if query_embedding is None:
|
|
299
304
|
log_error(f"Error getting embedding for Query: {query}")
|
|
@@ -665,7 +670,7 @@ class SingleStore(VectorDb):
|
|
|
665
670
|
log_debug(f"Committed {counter} documents")
|
|
666
671
|
|
|
667
672
|
async def async_search(
|
|
668
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
673
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
669
674
|
) -> List[Document]:
|
|
670
675
|
return self.search(query=query, limit=limit, filters=filters)
|
|
671
676
|
|
|
@@ -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
|
|
|
@@ -318,7 +319,9 @@ class SurrealDb(VectorDb):
|
|
|
318
319
|
thing = f"{self.collection}:{doc.id}" if doc.id else self.collection
|
|
319
320
|
self.client.query(self.UPSERT_QUERY.format(thing=thing), data)
|
|
320
321
|
|
|
321
|
-
def search(
|
|
322
|
+
def search(
|
|
323
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
324
|
+
) -> List[Document]:
|
|
322
325
|
"""Search for similar documents.
|
|
323
326
|
|
|
324
327
|
Args:
|
|
@@ -330,6 +333,9 @@ class SurrealDb(VectorDb):
|
|
|
330
333
|
A list of documents that are similar to the query.
|
|
331
334
|
|
|
332
335
|
"""
|
|
336
|
+
if isinstance(filters, List):
|
|
337
|
+
log_warning("Filters Expressions are not supported in SurrealDB. No filters will be applied.")
|
|
338
|
+
filters = None
|
|
333
339
|
query_embedding = self.embedder.get_embedding(query)
|
|
334
340
|
if query_embedding is None:
|
|
335
341
|
log_error(f"Error getting embedding for Query: {query}")
|
|
@@ -560,7 +566,7 @@ class SurrealDb(VectorDb):
|
|
|
560
566
|
self,
|
|
561
567
|
query: str,
|
|
562
568
|
limit: int = 5,
|
|
563
|
-
filters: Optional[Dict[str, Any]] = None,
|
|
569
|
+
filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
|
|
564
570
|
) -> List[Document]:
|
|
565
571
|
"""Search for similar documents asynchronously.
|
|
566
572
|
|
|
@@ -573,6 +579,10 @@ class SurrealDb(VectorDb):
|
|
|
573
579
|
A list of documents that are similar to the query.
|
|
574
580
|
|
|
575
581
|
"""
|
|
582
|
+
if isinstance(filters, List):
|
|
583
|
+
log_warning("Filters Expressions are not supported in SurrealDB. No filters will be applied.")
|
|
584
|
+
filters = None
|
|
585
|
+
|
|
576
586
|
query_embedding = self.embedder.get_embedding(query)
|
|
577
587
|
if query_embedding is None:
|
|
578
588
|
log_error(f"Error getting embedding for Query: {query}")
|
|
@@ -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 = ""
|
|
@@ -324,7 +325,7 @@ class UpstashVectorDb(VectorDb):
|
|
|
324
325
|
self,
|
|
325
326
|
query: str,
|
|
326
327
|
limit: int = 5,
|
|
327
|
-
filters: Optional[Dict[str, Any]] = None,
|
|
328
|
+
filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
|
|
328
329
|
namespace: Optional[str] = None,
|
|
329
330
|
) -> List[Document]:
|
|
330
331
|
"""Search for documents in the index.
|
|
@@ -337,7 +338,9 @@ class UpstashVectorDb(VectorDb):
|
|
|
337
338
|
List[Document]: List of matching documents.
|
|
338
339
|
"""
|
|
339
340
|
_namespace = self.namespace if namespace is None else namespace
|
|
340
|
-
|
|
341
|
+
if isinstance(filters, List):
|
|
342
|
+
log_warning("Filters Expressions are not supported in UpstashDB. No filters will be applied.")
|
|
343
|
+
filters = None
|
|
341
344
|
filter_str = "" if filters is None else str(filters)
|
|
342
345
|
|
|
343
346
|
if not self.use_upstash_embeddings and self.embedder is not None:
|
|
@@ -623,7 +626,7 @@ class UpstashVectorDb(VectorDb):
|
|
|
623
626
|
self.index.upsert(vectors, namespace=_namespace)
|
|
624
627
|
|
|
625
628
|
async def async_search(
|
|
626
|
-
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
|
|
627
630
|
) -> List[Document]:
|
|
628
631
|
raise NotImplementedError(f"Async not supported on {self.__class__.__name__}.")
|
|
629
632
|
|
|
@@ -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
|
|
@@ -392,7 +393,9 @@ class Weaviate(VectorDb):
|
|
|
392
393
|
await self.async_insert(content_hash=content_hash, documents=documents, filters=filters)
|
|
393
394
|
return
|
|
394
395
|
|
|
395
|
-
def search(
|
|
396
|
+
def search(
|
|
397
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
398
|
+
) -> List[Document]:
|
|
396
399
|
"""
|
|
397
400
|
Perform a search based on the configured search type.
|
|
398
401
|
|
|
@@ -404,6 +407,9 @@ class Weaviate(VectorDb):
|
|
|
404
407
|
Returns:
|
|
405
408
|
List[Document]: List of matching documents.
|
|
406
409
|
"""
|
|
410
|
+
if isinstance(filters, List):
|
|
411
|
+
log_warning("Filters Expressions are not supported in Weaviate. No filters will be applied.")
|
|
412
|
+
filters = None
|
|
407
413
|
if self.search_type == SearchType.vector:
|
|
408
414
|
return self.vector_search(query, limit, filters)
|
|
409
415
|
elif self.search_type == SearchType.keyword:
|
|
@@ -415,7 +421,7 @@ class Weaviate(VectorDb):
|
|
|
415
421
|
return []
|
|
416
422
|
|
|
417
423
|
async def async_search(
|
|
418
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
424
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
419
425
|
) -> List[Document]:
|
|
420
426
|
"""
|
|
421
427
|
Perform a search based on the configured search type asynchronously.
|
|
@@ -428,6 +434,9 @@ class Weaviate(VectorDb):
|
|
|
428
434
|
Returns:
|
|
429
435
|
List[Document]: List of matching documents.
|
|
430
436
|
"""
|
|
437
|
+
if isinstance(filters, List):
|
|
438
|
+
log_warning("Filters Expressions are not supported in Weaviate. No filters will be applied.")
|
|
439
|
+
filters = None
|
|
431
440
|
if self.search_type == SearchType.vector:
|
|
432
441
|
return await self.async_vector_search(query, limit, filters)
|
|
433
442
|
elif self.search_type == SearchType.keyword:
|
|
@@ -438,7 +447,9 @@ class Weaviate(VectorDb):
|
|
|
438
447
|
logger.error(f"Invalid search type '{self.search_type}'.")
|
|
439
448
|
return []
|
|
440
449
|
|
|
441
|
-
def vector_search(
|
|
450
|
+
def vector_search(
|
|
451
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
452
|
+
) -> List[Document]:
|
|
442
453
|
try:
|
|
443
454
|
query_embedding = self.embedder.get_embedding(query)
|
|
444
455
|
if query_embedding is None:
|
|
@@ -473,7 +484,7 @@ class Weaviate(VectorDb):
|
|
|
473
484
|
self.get_client().close()
|
|
474
485
|
|
|
475
486
|
async def async_vector_search(
|
|
476
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
487
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
477
488
|
) -> List[Document]:
|
|
478
489
|
"""
|
|
479
490
|
Perform a vector search in Weaviate asynchronously.
|
|
@@ -518,7 +529,9 @@ class Weaviate(VectorDb):
|
|
|
518
529
|
logger.error(f"Error searching for documents: {e}")
|
|
519
530
|
return []
|
|
520
531
|
|
|
521
|
-
def keyword_search(
|
|
532
|
+
def keyword_search(
|
|
533
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
534
|
+
) -> List[Document]:
|
|
522
535
|
try:
|
|
523
536
|
collection = self.get_client().collections.get(self.collection)
|
|
524
537
|
filter_expr = self._build_filter_expression(filters)
|
|
@@ -549,7 +562,7 @@ class Weaviate(VectorDb):
|
|
|
549
562
|
self.get_client().close()
|
|
550
563
|
|
|
551
564
|
async def async_keyword_search(
|
|
552
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
565
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
553
566
|
) -> List[Document]:
|
|
554
567
|
"""
|
|
555
568
|
Perform a keyword search in Weaviate asynchronously.
|
|
@@ -590,7 +603,9 @@ class Weaviate(VectorDb):
|
|
|
590
603
|
logger.error(f"Error searching for documents: {e}")
|
|
591
604
|
return []
|
|
592
605
|
|
|
593
|
-
def hybrid_search(
|
|
606
|
+
def hybrid_search(
|
|
607
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
608
|
+
) -> List[Document]:
|
|
594
609
|
try:
|
|
595
610
|
query_embedding = self.embedder.get_embedding(query)
|
|
596
611
|
if query_embedding is None:
|
|
@@ -628,7 +643,7 @@ class Weaviate(VectorDb):
|
|
|
628
643
|
self.get_client().close()
|
|
629
644
|
|
|
630
645
|
async def async_hybrid_search(
|
|
631
|
-
self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
|
|
646
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
632
647
|
) -> List[Document]:
|
|
633
648
|
"""
|
|
634
649
|
Perform a hybrid search combining vector and keyword search in Weaviate asynchronously.
|
|
@@ -849,7 +864,7 @@ class Weaviate(VectorDb):
|
|
|
849
864
|
"""Indicate that upsert functionality is available."""
|
|
850
865
|
return True
|
|
851
866
|
|
|
852
|
-
def _build_filter_expression(self, filters: Optional[Dict[str, Any]]):
|
|
867
|
+
def _build_filter_expression(self, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]]):
|
|
853
868
|
"""
|
|
854
869
|
Build a filter expression for Weaviate queries.
|
|
855
870
|
|
|
@@ -861,7 +876,9 @@ class Weaviate(VectorDb):
|
|
|
861
876
|
"""
|
|
862
877
|
if not filters:
|
|
863
878
|
return None
|
|
864
|
-
|
|
879
|
+
if isinstance(filters, List):
|
|
880
|
+
log_warning("Filters Expressions are not supported in Weaviate. No filters will be applied.")
|
|
881
|
+
return None
|
|
865
882
|
try:
|
|
866
883
|
# Create a filter for each key-value pair
|
|
867
884
|
filter_conditions = []
|
agno/workflow/step.py
CHANGED
|
@@ -11,9 +11,10 @@ from agno.agent import Agent
|
|
|
11
11
|
from agno.media import Audio, Image, Video
|
|
12
12
|
from agno.models.metrics import Metrics
|
|
13
13
|
from agno.run import RunContext
|
|
14
|
-
from agno.run.agent import RunCompletedEvent,
|
|
14
|
+
from agno.run.agent import RunCompletedEvent, RunContentEvent, RunOutput
|
|
15
15
|
from agno.run.base import BaseRunOutputEvent
|
|
16
|
-
from agno.run.team import RunCompletedEvent as TeamRunCompletedEvent
|
|
16
|
+
from agno.run.team import RunCompletedEvent as TeamRunCompletedEvent
|
|
17
|
+
from agno.run.team import RunContentEvent as TeamRunContentEvent
|
|
17
18
|
from agno.run.team import TeamRunOutput
|
|
18
19
|
from agno.run.workflow import (
|
|
19
20
|
StepCompletedEvent,
|
agno/workflow/types.py
CHANGED
|
@@ -17,6 +17,7 @@ from agno.utils.media import (
|
|
|
17
17
|
reconstruct_videos,
|
|
18
18
|
)
|
|
19
19
|
from agno.utils.serialize import json_serializer
|
|
20
|
+
from agno.utils.timer import Timer
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
@dataclass
|
|
@@ -405,12 +406,18 @@ class WorkflowMetrics:
|
|
|
405
406
|
"""Complete metrics for a workflow execution"""
|
|
406
407
|
|
|
407
408
|
steps: Dict[str, StepMetrics]
|
|
409
|
+
# Timer utility for tracking execution time
|
|
410
|
+
timer: Optional[Timer] = None
|
|
411
|
+
# Total workflow execution time
|
|
412
|
+
duration: Optional[float] = None
|
|
408
413
|
|
|
409
414
|
def to_dict(self) -> Dict[str, Any]:
|
|
410
415
|
"""Convert to dictionary"""
|
|
411
|
-
|
|
416
|
+
result: Dict[str, Any] = {
|
|
412
417
|
"steps": {name: step.to_dict() for name, step in self.steps.items()},
|
|
418
|
+
"duration": self.duration,
|
|
413
419
|
}
|
|
420
|
+
return result
|
|
414
421
|
|
|
415
422
|
@classmethod
|
|
416
423
|
def from_dict(cls, data: Dict[str, Any]) -> "WorkflowMetrics":
|
|
@@ -419,8 +426,20 @@ class WorkflowMetrics:
|
|
|
419
426
|
|
|
420
427
|
return cls(
|
|
421
428
|
steps=steps,
|
|
429
|
+
duration=data.get("duration"),
|
|
422
430
|
)
|
|
423
431
|
|
|
432
|
+
def start_timer(self):
|
|
433
|
+
if self.timer is None:
|
|
434
|
+
self.timer = Timer()
|
|
435
|
+
self.timer.start()
|
|
436
|
+
|
|
437
|
+
def stop_timer(self, set_duration: bool = True):
|
|
438
|
+
if self.timer is not None:
|
|
439
|
+
self.timer.stop()
|
|
440
|
+
if set_duration:
|
|
441
|
+
self.duration = self.timer.elapsed
|
|
442
|
+
|
|
424
443
|
|
|
425
444
|
@dataclass
|
|
426
445
|
class WebSocketHandler:
|