agno 2.1.0__py3-none-any.whl → 2.1.2__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 +13 -1
- agno/db/base.py +8 -4
- agno/db/dynamo/dynamo.py +69 -17
- agno/db/firestore/firestore.py +68 -29
- agno/db/gcs_json/gcs_json_db.py +68 -17
- agno/db/in_memory/in_memory_db.py +83 -14
- agno/db/json/json_db.py +79 -15
- agno/db/mongo/mongo.py +92 -74
- agno/db/mysql/mysql.py +17 -3
- agno/db/postgres/postgres.py +21 -3
- agno/db/redis/redis.py +38 -11
- agno/db/singlestore/singlestore.py +14 -3
- agno/db/sqlite/sqlite.py +34 -46
- agno/db/utils.py +50 -22
- agno/knowledge/knowledge.py +6 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +294 -0
- agno/knowledge/reader/pdf_reader.py +28 -52
- agno/knowledge/reader/reader_factory.py +12 -0
- agno/memory/manager.py +12 -4
- agno/models/anthropic/claude.py +4 -1
- agno/models/aws/bedrock.py +52 -112
- agno/models/openai/responses.py +1 -1
- agno/os/app.py +24 -30
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +252 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/agui.py +21 -5
- agno/os/interfaces/agui/router.py +12 -0
- agno/os/interfaces/base.py +4 -2
- agno/os/interfaces/slack/slack.py +13 -8
- agno/os/interfaces/whatsapp/whatsapp.py +12 -5
- agno/os/mcp.py +1 -1
- agno/os/router.py +39 -9
- agno/os/routers/memory/memory.py +5 -3
- agno/os/routers/memory/schemas.py +1 -0
- agno/os/utils.py +36 -10
- agno/run/base.py +2 -13
- agno/team/team.py +13 -1
- agno/tools/mcp.py +46 -1
- agno/utils/merge_dict.py +22 -1
- agno/utils/serialize.py +32 -0
- agno/utils/streamlit.py +1 -1
- agno/workflow/parallel.py +90 -14
- agno/workflow/step.py +30 -27
- agno/workflow/types.py +4 -6
- agno/workflow/workflow.py +5 -3
- {agno-2.1.0.dist-info → agno-2.1.2.dist-info}/METADATA +16 -14
- {agno-2.1.0.dist-info → agno-2.1.2.dist-info}/RECORD +53 -47
- {agno-2.1.0.dist-info → agno-2.1.2.dist-info}/WHEEL +0 -0
- {agno-2.1.0.dist-info → agno-2.1.2.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.0.dist-info → agno-2.1.2.dist-info}/top_level.txt +0 -0
agno/db/mysql/mysql.py
CHANGED
|
@@ -917,9 +917,13 @@ class MySQLDb(BaseDb):
|
|
|
917
917
|
]
|
|
918
918
|
|
|
919
919
|
# -- Memory methods --
|
|
920
|
-
def delete_user_memory(self, memory_id: str):
|
|
920
|
+
def delete_user_memory(self, memory_id: str, user_id: Optional[str] = None):
|
|
921
921
|
"""Delete a user memory from the database.
|
|
922
922
|
|
|
923
|
+
Args:
|
|
924
|
+
memory_id (str): The ID of the memory to delete.
|
|
925
|
+
user_id (Optional[str]): The user ID to filter by. Defaults to None.
|
|
926
|
+
|
|
923
927
|
Returns:
|
|
924
928
|
bool: True if deletion was successful, False otherwise.
|
|
925
929
|
|
|
@@ -933,6 +937,8 @@ class MySQLDb(BaseDb):
|
|
|
933
937
|
|
|
934
938
|
with self.Session() as sess, sess.begin():
|
|
935
939
|
delete_stmt = table.delete().where(table.c.memory_id == memory_id)
|
|
940
|
+
if user_id is not None:
|
|
941
|
+
delete_stmt = delete_stmt.where(table.c.user_id == user_id)
|
|
936
942
|
result = sess.execute(delete_stmt)
|
|
937
943
|
|
|
938
944
|
success = result.rowcount > 0
|
|
@@ -944,11 +950,12 @@ class MySQLDb(BaseDb):
|
|
|
944
950
|
except Exception as e:
|
|
945
951
|
log_error(f"Error deleting user memory: {e}")
|
|
946
952
|
|
|
947
|
-
def delete_user_memories(self, memory_ids: List[str]) -> None:
|
|
953
|
+
def delete_user_memories(self, memory_ids: List[str], user_id: Optional[str] = None) -> None:
|
|
948
954
|
"""Delete user memories from the database.
|
|
949
955
|
|
|
950
956
|
Args:
|
|
951
957
|
memory_ids (List[str]): The IDs of the memories to delete.
|
|
958
|
+
user_id (Optional[str]): The user ID to filter by. Defaults to None.
|
|
952
959
|
|
|
953
960
|
Raises:
|
|
954
961
|
Exception: If an error occurs during deletion.
|
|
@@ -960,6 +967,8 @@ class MySQLDb(BaseDb):
|
|
|
960
967
|
|
|
961
968
|
with self.Session() as sess, sess.begin():
|
|
962
969
|
delete_stmt = table.delete().where(table.c.memory_id.in_(memory_ids))
|
|
970
|
+
if user_id is not None:
|
|
971
|
+
delete_stmt = delete_stmt.where(table.c.user_id == user_id)
|
|
963
972
|
result = sess.execute(delete_stmt)
|
|
964
973
|
if result.rowcount == 0:
|
|
965
974
|
log_debug(f"No user memories found with ids: {memory_ids}")
|
|
@@ -1002,12 +1011,15 @@ class MySQLDb(BaseDb):
|
|
|
1002
1011
|
log_error(f"Exception reading from memory table: {e}")
|
|
1003
1012
|
raise e
|
|
1004
1013
|
|
|
1005
|
-
def get_user_memory(
|
|
1014
|
+
def get_user_memory(
|
|
1015
|
+
self, memory_id: str, deserialize: Optional[bool] = True, user_id: Optional[str] = None
|
|
1016
|
+
) -> Optional[UserMemory]:
|
|
1006
1017
|
"""Get a memory from the database.
|
|
1007
1018
|
|
|
1008
1019
|
Args:
|
|
1009
1020
|
memory_id (str): The ID of the memory to get.
|
|
1010
1021
|
deserialize (Optional[bool]): Whether to serialize the memory. Defaults to True.
|
|
1022
|
+
user_id (Optional[str]): The user ID to filter by. Defaults to None.
|
|
1011
1023
|
|
|
1012
1024
|
Returns:
|
|
1013
1025
|
Union[UserMemory, Dict[str, Any], None]:
|
|
@@ -1024,6 +1036,8 @@ class MySQLDb(BaseDb):
|
|
|
1024
1036
|
|
|
1025
1037
|
with self.Session() as sess, sess.begin():
|
|
1026
1038
|
stmt = select(table).where(table.c.memory_id == memory_id)
|
|
1039
|
+
if user_id is not None:
|
|
1040
|
+
stmt = stmt.where(table.c.user_id == user_id)
|
|
1027
1041
|
|
|
1028
1042
|
result = sess.execute(stmt).fetchone()
|
|
1029
1043
|
if not result:
|
agno/db/postgres/postgres.py
CHANGED
|
@@ -870,9 +870,13 @@ class PostgresDb(BaseDb):
|
|
|
870
870
|
return []
|
|
871
871
|
|
|
872
872
|
# -- Memory methods --
|
|
873
|
-
def delete_user_memory(self, memory_id: str):
|
|
873
|
+
def delete_user_memory(self, memory_id: str, user_id: Optional[str] = None):
|
|
874
874
|
"""Delete a user memory from the database.
|
|
875
875
|
|
|
876
|
+
Args:
|
|
877
|
+
memory_id (str): The ID of the memory to delete.
|
|
878
|
+
user_id (Optional[str]): The ID of the user to filter by. Defaults to None.
|
|
879
|
+
|
|
876
880
|
Returns:
|
|
877
881
|
bool: True if deletion was successful, False otherwise.
|
|
878
882
|
|
|
@@ -886,6 +890,10 @@ class PostgresDb(BaseDb):
|
|
|
886
890
|
|
|
887
891
|
with self.Session() as sess, sess.begin():
|
|
888
892
|
delete_stmt = table.delete().where(table.c.memory_id == memory_id)
|
|
893
|
+
|
|
894
|
+
if user_id is not None:
|
|
895
|
+
delete_stmt = delete_stmt.where(table.c.user_id == user_id)
|
|
896
|
+
|
|
889
897
|
result = sess.execute(delete_stmt)
|
|
890
898
|
|
|
891
899
|
success = result.rowcount > 0
|
|
@@ -898,11 +906,12 @@ class PostgresDb(BaseDb):
|
|
|
898
906
|
log_error(f"Error deleting user memory: {e}")
|
|
899
907
|
raise e
|
|
900
908
|
|
|
901
|
-
def delete_user_memories(self, memory_ids: List[str]) -> None:
|
|
909
|
+
def delete_user_memories(self, memory_ids: List[str], user_id: Optional[str] = None) -> None:
|
|
902
910
|
"""Delete user memories from the database.
|
|
903
911
|
|
|
904
912
|
Args:
|
|
905
913
|
memory_ids (List[str]): The IDs of the memories to delete.
|
|
914
|
+
user_id (Optional[str]): The ID of the user to filter by. Defaults to None.
|
|
906
915
|
|
|
907
916
|
Raises:
|
|
908
917
|
Exception: If an error occurs during deletion.
|
|
@@ -914,6 +923,10 @@ class PostgresDb(BaseDb):
|
|
|
914
923
|
|
|
915
924
|
with self.Session() as sess, sess.begin():
|
|
916
925
|
delete_stmt = table.delete().where(table.c.memory_id.in_(memory_ids))
|
|
926
|
+
|
|
927
|
+
if user_id is not None:
|
|
928
|
+
delete_stmt = delete_stmt.where(table.c.user_id == user_id)
|
|
929
|
+
|
|
917
930
|
result = sess.execute(delete_stmt)
|
|
918
931
|
|
|
919
932
|
if result.rowcount == 0:
|
|
@@ -938,6 +951,7 @@ class PostgresDb(BaseDb):
|
|
|
938
951
|
|
|
939
952
|
with self.Session() as sess, sess.begin():
|
|
940
953
|
stmt = select(func.json_array_elements_text(table.c.topics))
|
|
954
|
+
|
|
941
955
|
result = sess.execute(stmt).fetchall()
|
|
942
956
|
|
|
943
957
|
return list(set([record[0] for record in result]))
|
|
@@ -947,13 +961,14 @@ class PostgresDb(BaseDb):
|
|
|
947
961
|
return []
|
|
948
962
|
|
|
949
963
|
def get_user_memory(
|
|
950
|
-
self, memory_id: str, deserialize: Optional[bool] = True
|
|
964
|
+
self, memory_id: str, deserialize: Optional[bool] = True, user_id: Optional[str] = None
|
|
951
965
|
) -> Optional[Union[UserMemory, Dict[str, Any]]]:
|
|
952
966
|
"""Get a memory from the database.
|
|
953
967
|
|
|
954
968
|
Args:
|
|
955
969
|
memory_id (str): The ID of the memory to get.
|
|
956
970
|
deserialize (Optional[bool]): Whether to serialize the memory. Defaults to True.
|
|
971
|
+
user_id (Optional[str]): The ID of the user to filter by. Defaults to None.
|
|
957
972
|
|
|
958
973
|
Returns:
|
|
959
974
|
Union[UserMemory, Dict[str, Any], None]:
|
|
@@ -971,6 +986,9 @@ class PostgresDb(BaseDb):
|
|
|
971
986
|
with self.Session() as sess, sess.begin():
|
|
972
987
|
stmt = select(table).where(table.c.memory_id == memory_id)
|
|
973
988
|
|
|
989
|
+
if user_id is not None:
|
|
990
|
+
stmt = stmt.where(table.c.user_id == user_id)
|
|
991
|
+
|
|
974
992
|
result = sess.execute(stmt).fetchone()
|
|
975
993
|
if not result:
|
|
976
994
|
return None
|
agno/db/redis/redis.py
CHANGED
|
@@ -627,11 +627,12 @@ class RedisDb(BaseDb):
|
|
|
627
627
|
|
|
628
628
|
# -- Memory methods --
|
|
629
629
|
|
|
630
|
-
def delete_user_memory(self, memory_id: str):
|
|
630
|
+
def delete_user_memory(self, memory_id: str, user_id: Optional[str] = None):
|
|
631
631
|
"""Delete a user memory from Redis.
|
|
632
632
|
|
|
633
633
|
Args:
|
|
634
634
|
memory_id (str): The ID of the memory to delete.
|
|
635
|
+
user_id (Optional[str]): The ID of the user. If provided, verifies the memory belongs to this user before deleting.
|
|
635
636
|
|
|
636
637
|
Returns:
|
|
637
638
|
bool: True if the memory was deleted, False otherwise.
|
|
@@ -640,6 +641,16 @@ class RedisDb(BaseDb):
|
|
|
640
641
|
Exception: If any error occurs while deleting the memory.
|
|
641
642
|
"""
|
|
642
643
|
try:
|
|
644
|
+
# If user_id is provided, verify ownership before deleting
|
|
645
|
+
if user_id is not None:
|
|
646
|
+
memory = self._get_record("memories", memory_id)
|
|
647
|
+
if memory is None:
|
|
648
|
+
log_debug(f"No user memory found with id: {memory_id}")
|
|
649
|
+
return
|
|
650
|
+
if memory.get("user_id") != user_id:
|
|
651
|
+
log_debug(f"Memory {memory_id} does not belong to user {user_id}")
|
|
652
|
+
return
|
|
653
|
+
|
|
643
654
|
if self._delete_record(
|
|
644
655
|
"memories", memory_id, index_fields=["user_id", "agent_id", "team_id", "workflow_id"]
|
|
645
656
|
):
|
|
@@ -651,15 +662,25 @@ class RedisDb(BaseDb):
|
|
|
651
662
|
log_error(f"Error deleting user memory: {e}")
|
|
652
663
|
raise e
|
|
653
664
|
|
|
654
|
-
def delete_user_memories(self, memory_ids: List[str]) -> None:
|
|
665
|
+
def delete_user_memories(self, memory_ids: List[str], user_id: Optional[str] = None) -> None:
|
|
655
666
|
"""Delete user memories from Redis.
|
|
656
667
|
|
|
657
668
|
Args:
|
|
658
669
|
memory_ids (List[str]): The IDs of the memories to delete.
|
|
670
|
+
user_id (Optional[str]): The ID of the user. If provided, only deletes memories belonging to this user.
|
|
659
671
|
"""
|
|
660
672
|
try:
|
|
661
673
|
# TODO: cant we optimize this?
|
|
662
674
|
for memory_id in memory_ids:
|
|
675
|
+
# If user_id is provided, verify ownership before deleting
|
|
676
|
+
if user_id is not None:
|
|
677
|
+
memory = self._get_record("memories", memory_id)
|
|
678
|
+
if memory is None:
|
|
679
|
+
continue
|
|
680
|
+
if memory.get("user_id") != user_id:
|
|
681
|
+
log_debug(f"Memory {memory_id} does not belong to user {user_id}, skipping deletion")
|
|
682
|
+
continue
|
|
683
|
+
|
|
663
684
|
self._delete_record(
|
|
664
685
|
"memories",
|
|
665
686
|
memory_id,
|
|
@@ -692,12 +713,14 @@ class RedisDb(BaseDb):
|
|
|
692
713
|
raise e
|
|
693
714
|
|
|
694
715
|
def get_user_memory(
|
|
695
|
-
self, memory_id: str, deserialize: Optional[bool] = True
|
|
716
|
+
self, memory_id: str, deserialize: Optional[bool] = True, user_id: Optional[str] = None
|
|
696
717
|
) -> Optional[Union[UserMemory, Dict[str, Any]]]:
|
|
697
718
|
"""Get a memory from Redis.
|
|
698
719
|
|
|
699
720
|
Args:
|
|
700
721
|
memory_id (str): The ID of the memory to get.
|
|
722
|
+
deserialize (Optional[bool]): Whether to deserialize the memory. Defaults to True.
|
|
723
|
+
user_id (Optional[str]): The ID of the user. If provided, only returns the memory if it belongs to this user.
|
|
701
724
|
|
|
702
725
|
Returns:
|
|
703
726
|
Optional[UserMemory]: The memory data if found, None otherwise.
|
|
@@ -707,6 +730,10 @@ class RedisDb(BaseDb):
|
|
|
707
730
|
if memory_raw is None:
|
|
708
731
|
return None
|
|
709
732
|
|
|
733
|
+
# Filter by user_id if provided
|
|
734
|
+
if user_id is not None and memory_raw.get("user_id") != user_id:
|
|
735
|
+
return None
|
|
736
|
+
|
|
710
737
|
if not deserialize:
|
|
711
738
|
return memory_raw
|
|
712
739
|
|
|
@@ -812,21 +839,21 @@ class RedisDb(BaseDb):
|
|
|
812
839
|
# Group by user_id
|
|
813
840
|
user_stats = {}
|
|
814
841
|
for memory in all_memories:
|
|
815
|
-
|
|
816
|
-
if
|
|
842
|
+
memory_user_id = memory.get("user_id")
|
|
843
|
+
if memory_user_id is None:
|
|
817
844
|
continue
|
|
818
845
|
|
|
819
|
-
if
|
|
820
|
-
user_stats[
|
|
821
|
-
"user_id":
|
|
846
|
+
if memory_user_id not in user_stats:
|
|
847
|
+
user_stats[memory_user_id] = {
|
|
848
|
+
"user_id": memory_user_id,
|
|
822
849
|
"total_memories": 0,
|
|
823
850
|
"last_memory_updated_at": 0,
|
|
824
851
|
}
|
|
825
852
|
|
|
826
|
-
user_stats[
|
|
853
|
+
user_stats[memory_user_id]["total_memories"] += 1
|
|
827
854
|
updated_at = memory.get("updated_at", 0)
|
|
828
|
-
if updated_at > user_stats[
|
|
829
|
-
user_stats[
|
|
855
|
+
if updated_at > user_stats[memory_user_id]["last_memory_updated_at"]:
|
|
856
|
+
user_stats[memory_user_id]["last_memory_updated_at"] = updated_at
|
|
830
857
|
|
|
831
858
|
stats_list = list(user_stats.values())
|
|
832
859
|
|
|
@@ -990,11 +990,12 @@ class SingleStoreDb(BaseDb):
|
|
|
990
990
|
return []
|
|
991
991
|
|
|
992
992
|
# -- Memory methods --
|
|
993
|
-
def delete_user_memory(self, memory_id: str):
|
|
993
|
+
def delete_user_memory(self, memory_id: str, user_id: Optional[str] = None):
|
|
994
994
|
"""Delete a user memory from the database.
|
|
995
995
|
|
|
996
996
|
Args:
|
|
997
997
|
memory_id (str): The ID of the memory to delete.
|
|
998
|
+
user_id (Optional[str]): The ID of the user to filter by. Defaults to None.
|
|
998
999
|
|
|
999
1000
|
Returns:
|
|
1000
1001
|
bool: True if deletion was successful, False otherwise.
|
|
@@ -1009,6 +1010,8 @@ class SingleStoreDb(BaseDb):
|
|
|
1009
1010
|
|
|
1010
1011
|
with self.Session() as sess, sess.begin():
|
|
1011
1012
|
delete_stmt = table.delete().where(table.c.memory_id == memory_id)
|
|
1013
|
+
if user_id is not None:
|
|
1014
|
+
delete_stmt = delete_stmt.where(table.c.user_id == user_id)
|
|
1012
1015
|
result = sess.execute(delete_stmt)
|
|
1013
1016
|
|
|
1014
1017
|
success = result.rowcount > 0
|
|
@@ -1021,11 +1024,12 @@ class SingleStoreDb(BaseDb):
|
|
|
1021
1024
|
log_error(f"Error deleting memory: {e}")
|
|
1022
1025
|
raise e
|
|
1023
1026
|
|
|
1024
|
-
def delete_user_memories(self, memory_ids: List[str]) -> None:
|
|
1027
|
+
def delete_user_memories(self, memory_ids: List[str], user_id: Optional[str] = None) -> None:
|
|
1025
1028
|
"""Delete user memories from the database.
|
|
1026
1029
|
|
|
1027
1030
|
Args:
|
|
1028
1031
|
memory_ids (List[str]): The IDs of the memories to delete.
|
|
1032
|
+
user_id (Optional[str]): The ID of the user to filter by. Defaults to None.
|
|
1029
1033
|
|
|
1030
1034
|
Raises:
|
|
1031
1035
|
Exception: If an error occurs during deletion.
|
|
@@ -1037,6 +1041,8 @@ class SingleStoreDb(BaseDb):
|
|
|
1037
1041
|
|
|
1038
1042
|
with self.Session() as sess, sess.begin():
|
|
1039
1043
|
delete_stmt = table.delete().where(table.c.memory_id.in_(memory_ids))
|
|
1044
|
+
if user_id is not None:
|
|
1045
|
+
delete_stmt = delete_stmt.where(table.c.user_id == user_id)
|
|
1040
1046
|
result = sess.execute(delete_stmt)
|
|
1041
1047
|
if result.rowcount == 0:
|
|
1042
1048
|
log_debug(f"No memories found with ids: {memory_ids}")
|
|
@@ -1073,12 +1079,15 @@ class SingleStoreDb(BaseDb):
|
|
|
1073
1079
|
log_error(f"Exception reading from memory table: {e}")
|
|
1074
1080
|
raise e
|
|
1075
1081
|
|
|
1076
|
-
def get_user_memory(
|
|
1082
|
+
def get_user_memory(
|
|
1083
|
+
self, memory_id: str, deserialize: Optional[bool] = True, user_id: Optional[str] = None
|
|
1084
|
+
) -> Optional[UserMemory]:
|
|
1077
1085
|
"""Get a memory from the database.
|
|
1078
1086
|
|
|
1079
1087
|
Args:
|
|
1080
1088
|
memory_id (str): The ID of the memory to get.
|
|
1081
1089
|
deserialize (Optional[bool]): Whether to serialize the memory. Defaults to True.
|
|
1090
|
+
user_id (Optional[str]): The ID of the user to filter by. Defaults to None.
|
|
1082
1091
|
|
|
1083
1092
|
Returns:
|
|
1084
1093
|
Union[UserMemory, Dict[str, Any], None]:
|
|
@@ -1095,6 +1104,8 @@ class SingleStoreDb(BaseDb):
|
|
|
1095
1104
|
|
|
1096
1105
|
with self.Session() as sess, sess.begin():
|
|
1097
1106
|
stmt = select(table).where(table.c.memory_id == memory_id)
|
|
1107
|
+
if user_id is not None:
|
|
1108
|
+
stmt = stmt.where(table.c.user_id == user_id)
|
|
1098
1109
|
|
|
1099
1110
|
result = sess.execute(stmt).fetchone()
|
|
1100
1111
|
if not result:
|
agno/db/sqlite/sqlite.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import time
|
|
2
2
|
from datetime import date, datetime, timedelta, timezone
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
|
|
4
|
+
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union, cast
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
7
7
|
from agno.db.base import BaseDb, SessionType
|
|
@@ -24,7 +24,7 @@ from agno.utils.log import log_debug, log_error, log_info, log_warning
|
|
|
24
24
|
from agno.utils.string import generate_id
|
|
25
25
|
|
|
26
26
|
try:
|
|
27
|
-
from sqlalchemy import Column, MetaData, Table, and_, func, select, text
|
|
27
|
+
from sqlalchemy import Column, MetaData, Table, and_, func, select, text
|
|
28
28
|
from sqlalchemy.dialects import sqlite
|
|
29
29
|
from sqlalchemy.engine import Engine, create_engine
|
|
30
30
|
from sqlalchemy.orm import scoped_session, sessionmaker
|
|
@@ -442,11 +442,7 @@ class SqliteDb(BaseDb):
|
|
|
442
442
|
if end_timestamp is not None:
|
|
443
443
|
stmt = stmt.where(table.c.created_at <= end_timestamp)
|
|
444
444
|
if session_name is not None:
|
|
445
|
-
stmt = stmt.where(
|
|
446
|
-
func.coalesce(func.json_extract(table.c.session_data, "$.session_name"), "").like(
|
|
447
|
-
f"%{session_name}%"
|
|
448
|
-
)
|
|
449
|
-
)
|
|
445
|
+
stmt = stmt.where(table.c.session_data.like(f"%{session_name}%"))
|
|
450
446
|
if session_type is not None:
|
|
451
447
|
stmt = stmt.where(table.c.session_type == session_type.value)
|
|
452
448
|
|
|
@@ -468,8 +464,10 @@ class SqliteDb(BaseDb):
|
|
|
468
464
|
return [] if deserialize else ([], 0)
|
|
469
465
|
|
|
470
466
|
sessions_raw = [deserialize_session_json_fields(dict(record._mapping)) for record in records]
|
|
471
|
-
if not
|
|
467
|
+
if not deserialize:
|
|
472
468
|
return sessions_raw, total_count
|
|
469
|
+
if not sessions_raw:
|
|
470
|
+
return []
|
|
473
471
|
|
|
474
472
|
if session_type == SessionType.AGENT:
|
|
475
473
|
return [AgentSession.from_dict(record) for record in sessions_raw] # type: ignore
|
|
@@ -505,43 +503,20 @@ class SqliteDb(BaseDb):
|
|
|
505
503
|
Exception: If an error occurs during renaming.
|
|
506
504
|
"""
|
|
507
505
|
try:
|
|
508
|
-
|
|
509
|
-
|
|
506
|
+
# Get the current session as a deserialized object
|
|
507
|
+
# Get the session record
|
|
508
|
+
session = self.get_session(session_id, session_type, deserialize=True)
|
|
509
|
+
if session is None:
|
|
510
510
|
return None
|
|
511
511
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
.values(session_data=func.json_set(table.c.session_data, "$.session_name", session_name))
|
|
518
|
-
)
|
|
519
|
-
result = sess.execute(stmt)
|
|
520
|
-
|
|
521
|
-
# Check if any rows were affected
|
|
522
|
-
if result.rowcount == 0:
|
|
523
|
-
return None
|
|
524
|
-
|
|
525
|
-
# Fetch the updated row
|
|
526
|
-
select_stmt = select(table).where(table.c.session_id == session_id)
|
|
527
|
-
row = sess.execute(select_stmt).fetchone()
|
|
528
|
-
|
|
529
|
-
if not row:
|
|
530
|
-
return None
|
|
531
|
-
|
|
532
|
-
session_raw = deserialize_session_json_fields(dict(row._mapping))
|
|
533
|
-
if not session_raw or not deserialize:
|
|
534
|
-
return session_raw
|
|
512
|
+
session = cast(Session, session)
|
|
513
|
+
# Update the session name
|
|
514
|
+
if session.session_data is None:
|
|
515
|
+
session.session_data = {}
|
|
516
|
+
session.session_data["session_name"] = session_name
|
|
535
517
|
|
|
536
|
-
#
|
|
537
|
-
|
|
538
|
-
return AgentSession.from_dict(session_raw)
|
|
539
|
-
elif session_type == SessionType.TEAM:
|
|
540
|
-
return TeamSession.from_dict(session_raw)
|
|
541
|
-
elif session_type == SessionType.WORKFLOW:
|
|
542
|
-
return WorkflowSession.from_dict(session_raw)
|
|
543
|
-
else:
|
|
544
|
-
raise ValueError(f"Invalid session type: {session_type}")
|
|
518
|
+
# Upsert the updated session back to the database
|
|
519
|
+
return self.upsert_session(session, deserialize=deserialize)
|
|
545
520
|
|
|
546
521
|
except Exception as e:
|
|
547
522
|
log_error(f"Exception renaming session: {e}")
|
|
@@ -909,9 +884,13 @@ class SqliteDb(BaseDb):
|
|
|
909
884
|
|
|
910
885
|
# -- Memory methods --
|
|
911
886
|
|
|
912
|
-
def delete_user_memory(self, memory_id: str):
|
|
887
|
+
def delete_user_memory(self, memory_id: str, user_id: Optional[str] = None):
|
|
913
888
|
"""Delete a user memory from the database.
|
|
914
889
|
|
|
890
|
+
Args:
|
|
891
|
+
memory_id (str): The ID of the memory to delete.
|
|
892
|
+
user_id (Optional[str]): The user ID to filter by. Defaults to None.
|
|
893
|
+
|
|
915
894
|
Returns:
|
|
916
895
|
bool: True if deletion was successful, False otherwise.
|
|
917
896
|
|
|
@@ -925,6 +904,8 @@ class SqliteDb(BaseDb):
|
|
|
925
904
|
|
|
926
905
|
with self.Session() as sess, sess.begin():
|
|
927
906
|
delete_stmt = table.delete().where(table.c.memory_id == memory_id)
|
|
907
|
+
if user_id is not None:
|
|
908
|
+
delete_stmt = delete_stmt.where(table.c.user_id == user_id)
|
|
928
909
|
result = sess.execute(delete_stmt)
|
|
929
910
|
|
|
930
911
|
success = result.rowcount > 0
|
|
@@ -937,11 +918,12 @@ class SqliteDb(BaseDb):
|
|
|
937
918
|
log_error(f"Error deleting user memory: {e}")
|
|
938
919
|
raise e
|
|
939
920
|
|
|
940
|
-
def delete_user_memories(self, memory_ids: List[str]) -> None:
|
|
921
|
+
def delete_user_memories(self, memory_ids: List[str], user_id: Optional[str] = None) -> None:
|
|
941
922
|
"""Delete user memories from the database.
|
|
942
923
|
|
|
943
924
|
Args:
|
|
944
925
|
memory_ids (List[str]): The IDs of the memories to delete.
|
|
926
|
+
user_id (Optional[str]): The user ID to filter by. Defaults to None.
|
|
945
927
|
|
|
946
928
|
Raises:
|
|
947
929
|
Exception: If an error occurs during deletion.
|
|
@@ -953,6 +935,8 @@ class SqliteDb(BaseDb):
|
|
|
953
935
|
|
|
954
936
|
with self.Session() as sess, sess.begin():
|
|
955
937
|
delete_stmt = table.delete().where(table.c.memory_id.in_(memory_ids))
|
|
938
|
+
if user_id is not None:
|
|
939
|
+
delete_stmt = delete_stmt.where(table.c.user_id == user_id)
|
|
956
940
|
result = sess.execute(delete_stmt)
|
|
957
941
|
if result.rowcount == 0:
|
|
958
942
|
log_debug(f"No user memories found with ids: {memory_ids}")
|
|
@@ -973,7 +957,8 @@ class SqliteDb(BaseDb):
|
|
|
973
957
|
return []
|
|
974
958
|
|
|
975
959
|
with self.Session() as sess, sess.begin():
|
|
976
|
-
|
|
960
|
+
# Select topics from all results
|
|
961
|
+
stmt = select(func.json_array_elements_text(table.c.topics)).select_from(table)
|
|
977
962
|
result = sess.execute(stmt).fetchall()
|
|
978
963
|
|
|
979
964
|
return list(set([record[0] for record in result]))
|
|
@@ -983,13 +968,14 @@ class SqliteDb(BaseDb):
|
|
|
983
968
|
raise e
|
|
984
969
|
|
|
985
970
|
def get_user_memory(
|
|
986
|
-
self, memory_id: str, deserialize: Optional[bool] = True
|
|
971
|
+
self, memory_id: str, deserialize: Optional[bool] = True, user_id: Optional[str] = None
|
|
987
972
|
) -> Optional[Union[UserMemory, Dict[str, Any]]]:
|
|
988
973
|
"""Get a memory from the database.
|
|
989
974
|
|
|
990
975
|
Args:
|
|
991
976
|
memory_id (str): The ID of the memory to get.
|
|
992
977
|
deserialize (Optional[bool]): Whether to serialize the memory. Defaults to True.
|
|
978
|
+
user_id (Optional[str]): The user ID to filter by. Defaults to None.
|
|
993
979
|
|
|
994
980
|
Returns:
|
|
995
981
|
Optional[Union[UserMemory, Dict[str, Any]]]:
|
|
@@ -1006,6 +992,8 @@ class SqliteDb(BaseDb):
|
|
|
1006
992
|
|
|
1007
993
|
with self.Session() as sess, sess.begin():
|
|
1008
994
|
stmt = select(table).where(table.c.memory_id == memory_id)
|
|
995
|
+
if user_id is not None:
|
|
996
|
+
stmt = stmt.where(table.c.user_id == user_id)
|
|
1009
997
|
result = sess.execute(stmt).fetchone()
|
|
1010
998
|
if result is None:
|
|
1011
999
|
return None
|
agno/db/utils.py
CHANGED
|
@@ -4,7 +4,6 @@ import json
|
|
|
4
4
|
from datetime import date, datetime
|
|
5
5
|
from uuid import UUID
|
|
6
6
|
|
|
7
|
-
from agno.db.base import SessionType
|
|
8
7
|
from agno.models.message import Message
|
|
9
8
|
from agno.models.metrics import Metrics
|
|
10
9
|
|
|
@@ -55,34 +54,63 @@ def serialize_session_json_fields(session: dict) -> dict:
|
|
|
55
54
|
|
|
56
55
|
|
|
57
56
|
def deserialize_session_json_fields(session: dict) -> dict:
|
|
58
|
-
"""Deserialize
|
|
57
|
+
"""Deserialize JSON fields in the given Session dictionary.
|
|
59
58
|
|
|
60
59
|
Args:
|
|
61
60
|
session (dict): The dictionary to deserialize.
|
|
62
61
|
|
|
63
62
|
Returns:
|
|
64
|
-
dict: The dictionary with JSON fields deserialized.
|
|
63
|
+
dict: The dictionary with JSON string fields deserialized to objects.
|
|
65
64
|
"""
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if session.get("
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if session.get("
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
from agno.utils.log import log_warning
|
|
66
|
+
|
|
67
|
+
if session.get("agent_data") is not None and isinstance(session["agent_data"], str):
|
|
68
|
+
try:
|
|
69
|
+
session["agent_data"] = json.loads(session["agent_data"])
|
|
70
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
71
|
+
log_warning(f"Warning: Could not parse agent_data as JSON, keeping as string: {e}")
|
|
72
|
+
|
|
73
|
+
if session.get("team_data") is not None and isinstance(session["team_data"], str):
|
|
74
|
+
try:
|
|
75
|
+
session["team_data"] = json.loads(session["team_data"])
|
|
76
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
77
|
+
log_warning(f"Warning: Could not parse team_data as JSON, keeping as string: {e}")
|
|
78
|
+
|
|
79
|
+
if session.get("workflow_data") is not None and isinstance(session["workflow_data"], str):
|
|
80
|
+
try:
|
|
81
|
+
session["workflow_data"] = json.loads(session["workflow_data"])
|
|
82
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
83
|
+
log_warning(f"Warning: Could not parse workflow_data as JSON, keeping as string: {e}")
|
|
84
|
+
|
|
85
|
+
if session.get("metadata") is not None and isinstance(session["metadata"], str):
|
|
86
|
+
try:
|
|
87
|
+
session["metadata"] = json.loads(session["metadata"])
|
|
88
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
89
|
+
log_warning(f"Warning: Could not parse metadata as JSON, keeping as string: {e}")
|
|
90
|
+
|
|
91
|
+
if session.get("chat_history") is not None and isinstance(session["chat_history"], str):
|
|
92
|
+
try:
|
|
93
|
+
session["chat_history"] = json.loads(session["chat_history"])
|
|
94
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
95
|
+
log_warning(f"Warning: Could not parse chat_history as JSON, keeping as string: {e}")
|
|
96
|
+
|
|
97
|
+
if session.get("summary") is not None and isinstance(session["summary"], str):
|
|
98
|
+
try:
|
|
99
|
+
session["summary"] = json.loads(session["summary"])
|
|
100
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
101
|
+
log_warning(f"Warning: Could not parse summary as JSON, keeping as string: {e}")
|
|
102
|
+
|
|
78
103
|
if session.get("session_data") is not None and isinstance(session["session_data"], str):
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
104
|
+
try:
|
|
105
|
+
session["session_data"] = json.loads(session["session_data"])
|
|
106
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
107
|
+
log_warning(f"Warning: Could not parse session_data as JSON, keeping as string: {e}")
|
|
108
|
+
|
|
109
|
+
# Handle runs field with session type checking
|
|
110
|
+
if session.get("runs") is not None and isinstance(session["runs"], str):
|
|
111
|
+
try:
|
|
86
112
|
session["runs"] = json.loads(session["runs"])
|
|
113
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
114
|
+
log_warning(f"Warning: Could not parse runs as JSON, keeping as string: {e}")
|
|
87
115
|
|
|
88
116
|
return session
|
agno/knowledge/knowledge.py
CHANGED
|
@@ -1357,6 +1357,12 @@ class Knowledge:
|
|
|
1357
1357
|
log_error(f"Error searching for documents: {e}")
|
|
1358
1358
|
return []
|
|
1359
1359
|
|
|
1360
|
+
def get_valid_filters(self) -> Set[str]:
|
|
1361
|
+
if self.valid_metadata_filters is None:
|
|
1362
|
+
self.valid_metadata_filters = set()
|
|
1363
|
+
self.valid_metadata_filters.update(self._get_filters_from_db)
|
|
1364
|
+
return self.valid_metadata_filters
|
|
1365
|
+
|
|
1360
1366
|
def validate_filters(self, filters: Optional[Dict[str, Any]]) -> Tuple[Dict[str, Any], List[str]]:
|
|
1361
1367
|
if self.valid_metadata_filters is None:
|
|
1362
1368
|
self.valid_metadata_filters = set()
|