From e8dc6fde538ed16a3387038f091e5a58c0cc2d27 Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Fri, 27 Mar 2026 11:57:24 +0800 Subject: [PATCH] feat: autoclean monitoring events --- .../pkg/api/http/service/monitoring.py | 51 +++++++++++++++++++ src/langbot/pkg/core/app.py | 28 ++++++++++ src/langbot/templates/config.yaml | 8 +++ 3 files changed, 87 insertions(+) diff --git a/src/langbot/pkg/api/http/service/monitoring.py b/src/langbot/pkg/api/http/service/monitoring.py index 33504aec..d2267a14 100644 --- a/src/langbot/pkg/api/http/service/monitoring.py +++ b/src/langbot/pkg/api/http/service/monitoring.py @@ -16,6 +16,57 @@ class MonitoringService: def __init__(self, ap: app.Application) -> None: self.ap = ap + # ========== Cleanup Methods ========== + + async def cleanup_expired_records(self, retention_days: int) -> dict[str, int]: + """Delete monitoring records older than the specified retention period. + + Args: + retention_days: Number of days to retain records. + + Returns: + A dict mapping table name to the number of deleted rows. + """ + cutoff = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) - datetime.timedelta( + days=retention_days + ) + + tables_and_columns: list[tuple[str, type, sqlalchemy.Column]] = [ + ( + 'monitoring_messages', + persistence_monitoring.MonitoringMessage, + persistence_monitoring.MonitoringMessage.timestamp, + ), + ( + 'monitoring_llm_calls', + persistence_monitoring.MonitoringLLMCall, + persistence_monitoring.MonitoringLLMCall.timestamp, + ), + ( + 'monitoring_embedding_calls', + persistence_monitoring.MonitoringEmbeddingCall, + persistence_monitoring.MonitoringEmbeddingCall.timestamp, + ), + ( + 'monitoring_errors', + persistence_monitoring.MonitoringError, + persistence_monitoring.MonitoringError.timestamp, + ), + ( + 'monitoring_sessions', + persistence_monitoring.MonitoringSession, + persistence_monitoring.MonitoringSession.last_activity, + ), + ] + + deleted_counts: dict[str, int] = {} + + for table_name, model_cls, ts_column in tables_and_columns: + result = await self.ap.persistence_mgr.execute_async(sqlalchemy.delete(model_cls).where(ts_column < cutoff)) + deleted_counts[table_name] = result.rowcount + + return deleted_counts + # ========== Recording Methods ========== async def record_message( diff --git a/src/langbot/pkg/core/app.py b/src/langbot/pkg/core/app.py index 12849f2a..e515cfb9 100644 --- a/src/langbot/pkg/core/app.py +++ b/src/langbot/pkg/core/app.py @@ -188,6 +188,34 @@ class Application: scopes=[core_entities.LifecycleControlScope.APPLICATION], ) + # Start monitoring data cleanup task if enabled + monitoring_cfg = self.instance_config.data.get('monitoring', {}) + auto_cleanup_cfg = monitoring_cfg.get('auto_cleanup', {}) + if auto_cleanup_cfg.get('enabled', True): + retention_days = auto_cleanup_cfg.get('retention_days', 30) + check_interval_hours = auto_cleanup_cfg.get('check_interval_hours', 1) + + async def monitoring_cleanup_loop(): + check_interval_seconds = check_interval_hours * 3600 + while True: + try: + deleted = await self.monitoring_service.cleanup_expired_records(retention_days) + total_deleted = sum(deleted.values()) + if total_deleted > 0: + self.logger.info( + f'Monitoring auto-cleanup: deleted {total_deleted} expired records ' + f'(retention={retention_days}d): {deleted}' + ) + except Exception as e: + self.logger.warning(f'Monitoring auto-cleanup error: {e}') + await asyncio.sleep(check_interval_seconds) + + self.task_mgr.create_task( + monitoring_cleanup_loop(), + name='monitoring-cleanup', + scopes=[core_entities.LifecycleControlScope.APPLICATION], + ) + self.task_mgr.create_task( never_ending(), name='never-ending-task', diff --git a/src/langbot/templates/config.yaml b/src/langbot/templates/config.yaml index a75a32fc..c622405d 100644 --- a/src/langbot/templates/config.yaml +++ b/src/langbot/templates/config.yaml @@ -78,6 +78,14 @@ plugin: runtime_ws_url: 'ws://langbot_plugin_runtime:5400/control/ws' enable_marketplace: true display_plugin_debug_url: 'ws://localhost:5401/plugin/debug/ws' +monitoring: + auto_cleanup: + # Enable automatic cleanup of expired monitoring records + enabled: true + # Retention period in days, records older than this will be deleted + retention_days: 30 + # Cleanup check interval in hours + check_interval_hours: 1 space: # Space service URL for OAuth and API url: 'https://space.langbot.app'