@@ -710,6 +710,41 @@ def _initialize_schema(self):
710710 Column ('created_at' , DateTime , default = func .now ())
711711 )
712712
713+ Table ('activity_log' , metadata ,
714+ Column ('id' , Integer , primary_key = True , autoincrement = True ),
715+ Column ('timestamp' , String (50 ), nullable = False ),
716+ Column ('activity_type' , String (50 ), nullable = False ),
717+ Column ('activity_data' , Text ),
718+ Column ('directory_path' , Text ),
719+ Column ('npc' , String (100 )),
720+ Column ('device_id' , String (255 )),
721+ Column ('session_id' , String (100 )),
722+ )
723+
724+ Table ('autocomplete_suggestions' , metadata ,
725+ Column ('id' , Integer , primary_key = True , autoincrement = True ),
726+ Column ('timestamp' , String (50 ), nullable = False ),
727+ Column ('suggestion_type' , String (50 ), nullable = False ),
728+ Column ('input_context' , Text ),
729+ Column ('suggestion' , Text , nullable = False ),
730+ Column ('accepted' , Integer , default = 0 ),
731+ Column ('npc' , String (100 )),
732+ Column ('model' , String (100 )),
733+ Column ('provider' , String (100 )),
734+ Column ('directory_path' , Text ),
735+ )
736+
737+ Table ('autocomplete_training' , metadata ,
738+ Column ('id' , Integer , primary_key = True , autoincrement = True ),
739+ Column ('created_at' , DateTime , default = func .now ()),
740+ Column ('suggestion_type' , String (50 ), nullable = False ),
741+ Column ('input_text' , Text , nullable = False ),
742+ Column ('output_text' , Text , nullable = False ),
743+ Column ('accepted' , Integer , nullable = False ),
744+ Column ('npc' , String (100 )),
745+ Column ('model' , String (100 )),
746+ )
747+
713748 metadata .create_all (self .engine , checkfirst = True )
714749 init_kg_schema (self .engine )
715750
@@ -1015,22 +1050,32 @@ def get_memories_for_scope(
10151050 directory_path : str ,
10161051 status : Optional [str ] = None
10171052 ) -> List [Dict ]:
1018-
1019- query = """
1020- SELECT id, initial_memory, final_memory,
1021- status, timestamp, created_at
1022- FROM memory_lifecycle
1023- WHERE npc = :npc AND team = :team AND directory_path = :path
1024- """
1025- params = {"npc" : npc , "team" : team , "path" : directory_path }
1026-
1053+
1054+ conditions = []
1055+ params = {}
1056+
1057+ if npc :
1058+ conditions .append ("npc = :npc" )
1059+ params ["npc" ] = npc
1060+ if team :
1061+ conditions .append ("team = :team" )
1062+ params ["team" ] = team
1063+ if directory_path :
1064+ conditions .append ("directory_path = :path" )
1065+ params ["path" ] = directory_path
10271066 if status :
1028- query += " AND status = :status"
1067+ conditions . append ( " status = :status")
10291068 params ["status" ] = status
1030-
1031- query += " ORDER BY created_at DESC"
1032- data = self ._fetch_all (query , params )
1033- return data
1069+
1070+ where = "WHERE " + " AND " .join (conditions ) if conditions else ""
1071+ query = f"""
1072+ SELECT id, initial_memory, final_memory,
1073+ status, timestamp, created_at, npc, team, directory_path
1074+ FROM memory_lifecycle
1075+ { where }
1076+ ORDER BY created_at DESC
1077+ """
1078+ return self ._fetch_all (query , params )
10341079
10351080 def search_memory (self , query : str , npc : str = None , team : str = None ,
10361081 directory_path : str = None , status_filter : str = None , limit : int = 10 ):
@@ -1524,6 +1569,82 @@ def get_all_commands(self, limit: int = 100) -> List[Dict]:
15241569 """
15251570 return self ._fetch_all (stmt , {"limit" : limit })
15261571
1572+ def log_activity (self , activity_type : str , activity_data : str = None ,
1573+ directory_path : str = None , npc : str = None ,
1574+ device_id : str = None , session_id : str = None ):
1575+ import datetime
1576+ ts = datetime .datetime .now ().isoformat ()
1577+ with self .engine .begin () as conn :
1578+ conn .execute (text ("""
1579+ INSERT INTO activity_log (timestamp, activity_type, activity_data, directory_path, npc, device_id, session_id)
1580+ VALUES (:ts, :atype, :adata, :dpath, :npc, :did, :sid)
1581+ """ ), {"ts" : ts , "atype" : activity_type , "adata" : activity_data ,
1582+ "dpath" : directory_path , "npc" : npc , "did" : device_id , "sid" : session_id })
1583+
1584+ def log_autocomplete (self , suggestion_type : str , input_context : str , suggestion : str ,
1585+ accepted : bool , npc : str = None , model : str = None ,
1586+ provider : str = None , directory_path : str = None ):
1587+ import datetime
1588+ ts = datetime .datetime .now ().isoformat ()
1589+ with self .engine .begin () as conn :
1590+ conn .execute (text ("""
1591+ INSERT INTO autocomplete_suggestions (timestamp, suggestion_type, input_context, suggestion, accepted, npc, model, provider, directory_path)
1592+ VALUES (:ts, :stype, :ctx, :sug, :acc, :npc, :model, :prov, :dpath)
1593+ """ ), {"ts" : ts , "stype" : suggestion_type , "ctx" : input_context ,
1594+ "sug" : suggestion , "acc" : 1 if accepted else 0 ,
1595+ "npc" : npc , "model" : model , "prov" : provider , "dpath" : directory_path })
1596+ conn .execute (text ("""
1597+ INSERT INTO autocomplete_training (suggestion_type, input_text, output_text, accepted, npc, model)
1598+ VALUES (:stype, :inp, :out, :acc, :npc, :model)
1599+ """ ), {"stype" : suggestion_type , "inp" : input_context , "out" : suggestion ,
1600+ "acc" : 1 if accepted else 0 , "npc" : npc , "model" : model })
1601+
1602+ def get_activities (self , activity_type : str = None , limit : int = 100 ,
1603+ directory_path : str = None , session_id : str = None ):
1604+ conditions = []
1605+ params = {"lim" : limit }
1606+ if activity_type :
1607+ conditions .append ("activity_type = :atype" )
1608+ params ["atype" ] = activity_type
1609+ if directory_path :
1610+ conditions .append ("directory_path = :dpath" )
1611+ params ["dpath" ] = directory_path
1612+ if session_id :
1613+ conditions .append ("session_id = :sid" )
1614+ params ["sid" ] = session_id
1615+ where = "WHERE " + " AND " .join (conditions ) if conditions else ""
1616+ return self ._fetch_all (f"SELECT * FROM activity_log { where } ORDER BY timestamp DESC LIMIT :lim" , params )
1617+
1618+ def get_autocomplete_stats (self , suggestion_type : str = None , npc : str = None ):
1619+ conditions = []
1620+ params = {}
1621+ if suggestion_type :
1622+ conditions .append ("suggestion_type = :stype" )
1623+ params ["stype" ] = suggestion_type
1624+ if npc :
1625+ conditions .append ("npc = :npc" )
1626+ params ["npc" ] = npc
1627+ where = "WHERE " + " AND " .join (conditions ) if conditions else ""
1628+ return self ._fetch_all (f"""
1629+ SELECT suggestion_type,
1630+ COUNT(*) as total,
1631+ SUM(accepted) as accepted,
1632+ COUNT(*) - SUM(accepted) as rejected
1633+ FROM autocomplete_suggestions { where }
1634+ GROUP BY suggestion_type
1635+ """ , params )
1636+
1637+ def get_training_data (self , suggestion_type : str = None , accepted_only : bool = False , limit : int = 1000 ):
1638+ conditions = []
1639+ params = {"lim" : limit }
1640+ if suggestion_type :
1641+ conditions .append ("suggestion_type = :stype" )
1642+ params ["stype" ] = suggestion_type
1643+ if accepted_only :
1644+ conditions .append ("accepted = 1" )
1645+ where = "WHERE " + " AND " .join (conditions ) if conditions else ""
1646+ return self ._fetch_all (f"SELECT * FROM autocomplete_training { where } ORDER BY created_at DESC LIMIT :lim" , params )
1647+
15271648 def close (self ):
15281649 """Dispose of the SQLAlchemy engine."""
15291650 if self .engine :
0 commit comments