Skip to content

Commit 9d29c79

Browse files
committed
Release v3.9.7
1 parent f058ce0 commit 9d29c79

File tree

24 files changed

+3149
-145
lines changed

24 files changed

+3149
-145
lines changed

docker/Dockerfile.chat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
1616
# Install Python packages (using latest versions)
1717
RUN pip install --no-cache-dir \
1818
praisonai_tools \
19-
"praisonai>=3.9.6" \
19+
"praisonai>=3.9.7" \
2020
"praisonai[chat]" \
2121
"embedchain[github,youtube]"
2222

docker/Dockerfile.dev

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
2020
# Install Python packages (using latest versions)
2121
RUN pip install --no-cache-dir \
2222
praisonai_tools \
23-
"praisonai>=3.9.6" \
23+
"praisonai>=3.9.7" \
2424
"praisonai[ui]" \
2525
"praisonai[chat]" \
2626
"praisonai[realtime]" \

docker/Dockerfile.ui

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
1616
# Install Python packages (using latest versions)
1717
RUN pip install --no-cache-dir \
1818
praisonai_tools \
19-
"praisonai>=3.9.6" \
19+
"praisonai>=3.9.7" \
2020
"praisonai[ui]" \
2121
"praisonai[crewai]"
2222

src/praisonai-agents/praisonaiagents/knowledge/chunking.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ class Chunking:
1212
'semantic': ['chunk_size', 'embedding_model'],
1313
'sdpm': ['chunk_size', 'embedding_model'],
1414
'late': ['chunk_size', 'embedding_model'],
15+
'code': ['chunk_size', 'tokenizer_or_token_counter'],
16+
'neural': ['chunk_size', 'tokenizer_or_token_counter'],
17+
'slumber': ['chunk_size', 'genie'],
1518
}
1619

1720
@cached_property
@@ -24,7 +27,10 @@ def SUPPORTED_CHUNKERS(self) -> Dict[str, Any]:
2427
SemanticChunker,
2528
SDPMChunker,
2629
LateChunker,
27-
RecursiveChunker
30+
RecursiveChunker,
31+
CodeChunker,
32+
NeuralChunker,
33+
SlumberChunker
2834
)
2935
except ImportError:
3036
raise ImportError(
@@ -37,7 +43,10 @@ def SUPPORTED_CHUNKERS(self) -> Dict[str, Any]:
3743
'semantic': SemanticChunker,
3844
'sdpm': SDPMChunker,
3945
'late': LateChunker,
40-
'recursive': RecursiveChunker
46+
'recursive': RecursiveChunker,
47+
'code': CodeChunker,
48+
'neural': NeuralChunker,
49+
'slumber': SlumberChunker
4150
}
4251

4352
def __init__(

src/praisonai-agents/praisonaiagents/memory/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
MemoryProtocol,
4343
AsyncMemoryProtocol,
4444
ResettableMemoryProtocol,
45+
DeletableMemoryProtocol,
46+
AsyncDeletableMemoryProtocol,
4547
EntityMemoryProtocol,
4648
)
4749

@@ -203,5 +205,7 @@ def __getattr__(name):
203205
"MemoryProtocol",
204206
"AsyncMemoryProtocol",
205207
"ResettableMemoryProtocol",
208+
"DeletableMemoryProtocol",
209+
"AsyncDeletableMemoryProtocol",
206210
"EntityMemoryProtocol",
207211
]

src/praisonai-agents/praisonaiagents/memory/file_memory.py

Lines changed: 278 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,220 @@ def clear_all(self):
806806

807807
self._log("Cleared all memory")
808808

809+
# -------------------------------------------------------------------------
810+
# Selective Deletion
811+
# -------------------------------------------------------------------------
812+
813+
def delete_short_term(self, memory_id: str) -> bool:
814+
"""
815+
Delete a specific short-term memory by ID.
816+
817+
Args:
818+
memory_id: The unique ID of the memory to delete
819+
820+
Returns:
821+
True if memory was found and deleted, False otherwise
822+
"""
823+
for i, item in enumerate(self._short_term):
824+
if item.id == memory_id:
825+
del self._short_term[i]
826+
self._save_short_term()
827+
self._log(f"Deleted short-term memory: {memory_id}")
828+
return True
829+
return False
830+
831+
def delete_long_term(self, memory_id: str) -> bool:
832+
"""
833+
Delete a specific long-term memory by ID.
834+
835+
Args:
836+
memory_id: The unique ID of the memory to delete
837+
838+
Returns:
839+
True if memory was found and deleted, False otherwise
840+
"""
841+
for i, item in enumerate(self._long_term):
842+
if item.id == memory_id:
843+
del self._long_term[i]
844+
self._save_long_term()
845+
self._log(f"Deleted long-term memory: {memory_id}")
846+
return True
847+
return False
848+
849+
def delete_entity(self, name: str) -> bool:
850+
"""
851+
Delete an entity by name.
852+
853+
Args:
854+
name: The name of the entity to delete
855+
856+
Returns:
857+
True if entity was found and deleted, False otherwise
858+
"""
859+
# Find entity by name using existing helper
860+
entity = self._find_entity_by_name(name)
861+
if entity:
862+
# Delete by entity's ID (which is the dict key)
863+
if entity.id in self._entities:
864+
del self._entities[entity.id]
865+
self._save_entities()
866+
self._log(f"Deleted entity: {name}")
867+
return True
868+
869+
# Try direct ID match as fallback (in case name IS the ID)
870+
if name in self._entities:
871+
del self._entities[name]
872+
self._save_entities()
873+
self._log(f"Deleted entity by ID: {name}")
874+
return True
875+
876+
return False
877+
878+
def delete_episodic(self, memory_id: str, date: Optional[str] = None) -> bool:
879+
"""
880+
Delete a specific episodic memory by ID.
881+
882+
Args:
883+
memory_id: The unique ID of the memory to delete
884+
date: Optional date string (YYYY-MM-DD) to narrow search
885+
886+
Returns:
887+
True if memory was found and deleted, False otherwise
888+
"""
889+
from datetime import datetime, timedelta
890+
891+
# Determine which files to search
892+
if date:
893+
dates_to_check = [date]
894+
else:
895+
# Check last 30 days
896+
dates_to_check = [
897+
(datetime.now() - timedelta(days=i)).strftime("%Y-%m-%d")
898+
for i in range(30)
899+
]
900+
901+
for check_date in dates_to_check:
902+
episodic_file = self.episodic_path / f"{check_date}.json"
903+
if episodic_file.exists():
904+
data = self._read_json(episodic_file, [])
905+
for i, item in enumerate(data):
906+
if item.get("id") == memory_id:
907+
del data[i]
908+
self._write_json(episodic_file, data)
909+
self._log(f"Deleted episodic memory: {memory_id}")
910+
return True
911+
912+
return False
913+
914+
def delete_memory(
915+
self,
916+
memory_id: str,
917+
memory_type: Optional[str] = None
918+
) -> bool:
919+
"""
920+
Delete a specific memory by ID.
921+
922+
This is the unified deletion method that searches across all memory types.
923+
Use this when you have a memory ID but don't know its type.
924+
925+
Particularly useful for:
926+
- Cleaning up image-based memories after processing to free context window
927+
- Removing outdated or incorrect information
928+
- Privacy compliance (selective erasure)
929+
930+
Args:
931+
memory_id: The unique ID of the memory to delete
932+
memory_type: Optional type hint to narrow search:
933+
'short_term', 'long_term', 'entity', 'episodic'
934+
If None, searches all types.
935+
936+
Returns:
937+
True if memory was found and deleted, False otherwise
938+
939+
Example:
940+
# Delete a specific memory after processing an image
941+
memory.delete_memory("abc123def456")
942+
943+
# Delete with type hint for faster lookup
944+
memory.delete_memory("abc123", memory_type="short_term")
945+
"""
946+
# If type specified, only search that type
947+
if memory_type == "short_term":
948+
return self.delete_short_term(memory_id)
949+
elif memory_type == "long_term":
950+
return self.delete_long_term(memory_id)
951+
elif memory_type == "entity":
952+
return self.delete_entity(memory_id)
953+
elif memory_type == "episodic":
954+
return self.delete_episodic(memory_id)
955+
956+
# Search all types
957+
if self.delete_short_term(memory_id):
958+
return True
959+
if self.delete_long_term(memory_id):
960+
return True
961+
if self.delete_entity(memory_id):
962+
return True
963+
if self.delete_episodic(memory_id):
964+
return True
965+
966+
return False
967+
968+
def delete_memories(self, memory_ids: List[str]) -> int:
969+
"""
970+
Delete multiple memories by their IDs.
971+
972+
Args:
973+
memory_ids: List of memory IDs to delete
974+
975+
Returns:
976+
Number of memories successfully deleted
977+
"""
978+
count = 0
979+
for memory_id in memory_ids:
980+
if self.delete_memory(memory_id):
981+
count += 1
982+
return count
983+
984+
def delete_memories_matching(
985+
self,
986+
query: str,
987+
memory_types: Optional[List[str]] = None,
988+
limit: int = 10
989+
) -> int:
990+
"""
991+
Delete memories matching a search query.
992+
993+
Useful for bulk cleanup of related memories, e.g., all image-related
994+
context after finishing an image analysis session.
995+
996+
Args:
997+
query: Search query to match memories
998+
memory_types: Optional list of types to search
999+
limit: Maximum number of memories to delete
1000+
1001+
Returns:
1002+
Number of memories deleted
1003+
"""
1004+
# Search for matching memories
1005+
results = self.search(query, memory_types=memory_types, limit=limit)
1006+
1007+
# Extract IDs and delete
1008+
deleted = 0
1009+
for result in results:
1010+
item = result.get("item", {})
1011+
memory_id = item.get("id")
1012+
memory_type = result.get("type")
1013+
1014+
if memory_id:
1015+
if self.delete_memory(memory_id, memory_type=memory_type):
1016+
deleted += 1
1017+
1018+
if deleted:
1019+
self._log(f"Deleted {deleted} memories matching '{query}'")
1020+
1021+
return deleted
1022+
8091023
def get_stats(self) -> Dict[str, Any]:
8101024
"""Get memory statistics."""
8111025
episodic_count = sum(1 for _ in self.episodic_path.glob("*.json"))
@@ -1222,6 +1436,66 @@ def handle_command(self, command: str) -> Dict[str, Any]:
12221436
else:
12231437
return {"error": "Usage: /memory clear [short|all]"}
12241438

1439+
elif action == "delete":
1440+
if not args:
1441+
return {"error": "Usage: /memory delete <id> or /memory delete --query <search_query>"}
1442+
1443+
# Handle --query flag for bulk deletion
1444+
if args.startswith("--query "):
1445+
query = args[8:] # Remove "--query " prefix
1446+
if not query:
1447+
return {"error": "Usage: /memory delete --query <search_query>"}
1448+
deleted = self.delete_memories_matching(query, limit=10)
1449+
return {
1450+
"action": "delete",
1451+
"type": "query",
1452+
"query": query,
1453+
"deleted_count": deleted
1454+
}
1455+
1456+
# Single ID deletion
1457+
success = self.delete_memory(args)
1458+
return {
1459+
"action": "delete",
1460+
"type": "single",
1461+
"id": args,
1462+
"success": success
1463+
}
1464+
1465+
elif action == "list":
1466+
# List memories with IDs for easy deletion reference
1467+
memory_type = args.lower() if args else "all"
1468+
items = []
1469+
1470+
if memory_type in ("all", "short", "short_term"):
1471+
for item in self.get_short_term(limit=20):
1472+
items.append({
1473+
"type": "short_term",
1474+
"id": item.id,
1475+
"content": item.content[:100] + "..." if len(item.content) > 100 else item.content,
1476+
"importance": item.importance
1477+
})
1478+
1479+
if memory_type in ("all", "long", "long_term"):
1480+
for item in self.get_long_term(limit=20):
1481+
items.append({
1482+
"type": "long_term",
1483+
"id": item.id,
1484+
"content": item.content[:100] + "..." if len(item.content) > 100 else item.content,
1485+
"importance": item.importance
1486+
})
1487+
1488+
if memory_type in ("all", "entity", "entities"):
1489+
for name, entity in self._entities.items():
1490+
items.append({
1491+
"type": "entity",
1492+
"id": name,
1493+
"name": entity.name,
1494+
"entity_type": entity.entity_type
1495+
})
1496+
1497+
return {"action": "list", "filter": memory_type, "items": items}
1498+
12251499
elif action == "search":
12261500
if not args:
12271501
return {"error": "Usage: /memory search <query>"}
@@ -1274,8 +1548,11 @@ def handle_command(self, command: str) -> Dict[str, Any]:
12741548
"action": "help",
12751549
"commands": {
12761550
"/memory show": "Display memory stats and recent items",
1551+
"/memory list [short|long|entity]": "List memories with IDs for deletion",
12771552
"/memory add <content>": "Add to long-term memory",
1278-
"/memory clear [short|all]": "Clear memory",
1553+
"/memory delete <id>": "Delete a specific memory by ID",
1554+
"/memory delete --query <q>": "Delete memories matching query",
1555+
"/memory clear [short|all]": "Clear all memory (bulk)",
12791556
"/memory search <query>": "Search memories",
12801557
"/memory save <name>": "Save session",
12811558
"/memory resume <name>": "Resume session",

0 commit comments

Comments
 (0)