feat: Implement comprehensive non-hierarchical logging system
- Created new logging infrastructure with per-component filtering - Added 6 log levels: DEBUG, INFO, API, WARNING, ERROR, CRITICAL - Implemented non-hierarchical level control (any combination can be enabled) - Migrated 917 print() statements across 31 files to structured logging - Created web UI (system.html) for runtime configuration with dark theme - Added global level controls to enable/disable levels across all components - Added timestamp format control (off/time/date/datetime options) - Implemented log rotation (10MB per file, 5 backups) - Added API endpoints for dynamic log configuration - Configured HTTP request logging with filtering via api.requests component - Intercepted APScheduler logs with proper formatting - Fixed persistence paths to use /app/memory for Docker volume compatibility - Fixed checkbox display bug in web UI (enabled_levels now properly shown) - Changed System Settings button to open in same tab instead of new window Components: bot, api, api.requests, autonomous, persona, vision, llm, conversation, mood, dm, scheduled, gpu, media, server, commands, sentiment, core, apscheduler All settings persist across container restarts via JSON config.
This commit is contained in:
@@ -10,6 +10,10 @@ from PIL import Image
|
||||
import re
|
||||
|
||||
import globals
|
||||
from utils.logger import get_logger
|
||||
|
||||
logger = get_logger('vision')
|
||||
|
||||
# No need for switch_model anymore - llama-swap handles this automatically
|
||||
|
||||
|
||||
@@ -47,7 +51,7 @@ async def extract_tenor_gif_url(tenor_url):
|
||||
match = re.search(r'tenor\.com/(\d+)\.gif', tenor_url)
|
||||
|
||||
if not match:
|
||||
print(f"⚠️ Could not extract Tenor GIF ID from: {tenor_url}")
|
||||
logger.warning(f"Could not extract Tenor GIF ID from: {tenor_url}")
|
||||
return None
|
||||
|
||||
gif_id = match.group(1)
|
||||
@@ -60,7 +64,7 @@ async def extract_tenor_gif_url(tenor_url):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.head(media_url) as resp:
|
||||
if resp.status == 200:
|
||||
print(f"✅ Found Tenor GIF: {media_url}")
|
||||
logger.debug(f"Found Tenor GIF: {media_url}")
|
||||
return media_url
|
||||
|
||||
# If that didn't work, try alternative formats
|
||||
@@ -69,14 +73,14 @@ async def extract_tenor_gif_url(tenor_url):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.head(alt_url) as resp:
|
||||
if resp.status == 200:
|
||||
print(f"✅ Found Tenor GIF (alternative): {alt_url}")
|
||||
logger.debug(f"Found Tenor GIF (alternative): {alt_url}")
|
||||
return alt_url
|
||||
|
||||
print(f"⚠️ Could not find working Tenor media URL for ID: {gif_id}")
|
||||
logger.warning(f"Could not find working Tenor media URL for ID: {gif_id}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error extracting Tenor GIF URL: {e}")
|
||||
logger.error(f"Error extracting Tenor GIF URL: {e}")
|
||||
return None
|
||||
|
||||
|
||||
@@ -114,7 +118,7 @@ async def convert_gif_to_mp4(gif_bytes):
|
||||
with open(temp_mp4_path, 'rb') as f:
|
||||
mp4_bytes = f.read()
|
||||
|
||||
print(f"✅ Converted GIF to MP4 ({len(gif_bytes)} bytes → {len(mp4_bytes)} bytes)")
|
||||
logger.info(f"Converted GIF to MP4 ({len(gif_bytes)} bytes → {len(mp4_bytes)} bytes)")
|
||||
return mp4_bytes
|
||||
|
||||
finally:
|
||||
@@ -125,10 +129,10 @@ async def convert_gif_to_mp4(gif_bytes):
|
||||
os.remove(temp_mp4_path)
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"⚠️ ffmpeg error converting GIF to MP4: {e.stderr.decode()}")
|
||||
logger.error(f"ffmpeg error converting GIF to MP4: {e.stderr.decode()}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error converting GIF to MP4: {e}")
|
||||
logger.error(f"Error converting GIF to MP4: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return None
|
||||
@@ -165,7 +169,7 @@ async def extract_video_frames(video_bytes, num_frames=4):
|
||||
if frames:
|
||||
return frames
|
||||
except Exception as e:
|
||||
print(f"Not a GIF, trying video extraction: {e}")
|
||||
logger.debug(f"Not a GIF, trying video extraction: {e}")
|
||||
|
||||
# For video files (MP4, WebM, etc.), use ffmpeg
|
||||
import subprocess
|
||||
@@ -222,7 +226,7 @@ async def extract_video_frames(video_bytes, num_frames=4):
|
||||
os.remove(temp_video_path)
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error extracting frames: {e}")
|
||||
logger.error(f"Error extracting frames: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
@@ -271,10 +275,10 @@ async def analyze_image_with_vision(base64_img):
|
||||
return data.get("choices", [{}])[0].get("message", {}).get("content", "No description.")
|
||||
else:
|
||||
error_text = await response.text()
|
||||
print(f"❌ Vision API error: {response.status} - {error_text}")
|
||||
logger.error(f"Vision API error: {response.status} - {error_text}")
|
||||
return f"Error analyzing image: {response.status}"
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error in analyze_image_with_vision: {e}")
|
||||
logger.error(f"Error in analyze_image_with_vision: {e}")
|
||||
return f"Error analyzing image: {str(e)}"
|
||||
|
||||
|
||||
@@ -333,10 +337,10 @@ async def analyze_video_with_vision(video_frames, media_type="video"):
|
||||
return data.get("choices", [{}])[0].get("message", {}).get("content", "No description.")
|
||||
else:
|
||||
error_text = await response.text()
|
||||
print(f"❌ Vision API error: {response.status} - {error_text}")
|
||||
logger.error(f"Vision API error: {response.status} - {error_text}")
|
||||
return f"Error analyzing video: {response.status}"
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error in analyze_video_with_vision: {e}")
|
||||
logger.error(f"Error in analyze_video_with_vision: {e}")
|
||||
return f"Error analyzing video: {str(e)}"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user