bug fixes
This commit is contained in:
158
oai.py
158
oai.py
@@ -30,7 +30,7 @@ from packaging import version as pkg_version
|
||||
import io # Added for custom handler
|
||||
|
||||
# App version. Changes by author with new releases.
|
||||
version = '1.9.5'
|
||||
version = '1.9.6'
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
@@ -369,41 +369,132 @@ class RotatingRichHandler(RotatingFileHandler):
|
||||
except Exception:
|
||||
self.handleError(record)
|
||||
|
||||
# Get log configuration from DB
|
||||
# ============================================================================
|
||||
# LOGGING SETUP - MUST BE DONE AFTER CONFIG IS LOADED
|
||||
# ============================================================================
|
||||
|
||||
# Load log configuration from DB FIRST (before creating handler)
|
||||
LOG_MAX_SIZE_MB = int(get_config('log_max_size_mb') or "10")
|
||||
LOG_BACKUP_COUNT = int(get_config('log_backup_count') or "2")
|
||||
LOG_LEVEL_STR = get_config('log_level') or "info"
|
||||
LOG_LEVEL = VALID_LOG_LEVELS.get(LOG_LEVEL_STR.lower(), logging.INFO)
|
||||
|
||||
# Create the custom rotating handler
|
||||
app_handler = RotatingRichHandler(
|
||||
filename=str(log_file),
|
||||
maxBytes=LOG_MAX_SIZE_MB * 1024 * 1024, # Convert MB to bytes
|
||||
backupCount=LOG_BACKUP_COUNT,
|
||||
encoding='utf-8'
|
||||
)
|
||||
# Global reference to the handler for dynamic reloading
|
||||
app_handler = None
|
||||
app_logger = None
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.NOTSET,
|
||||
format="%(message)s", # Rich formats it
|
||||
datefmt="[%X]",
|
||||
handlers=[app_handler]
|
||||
)
|
||||
def setup_logging():
|
||||
"""Setup or reset logging configuration with current settings."""
|
||||
global app_handler, LOG_MAX_SIZE_MB, LOG_BACKUP_COUNT, LOG_LEVEL, app_logger
|
||||
|
||||
# Get the root logger
|
||||
root_logger = logging.getLogger()
|
||||
|
||||
# Remove existing handler if present
|
||||
if app_handler is not None:
|
||||
root_logger.removeHandler(app_handler)
|
||||
try:
|
||||
app_handler.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
# Check if log file needs immediate rotation
|
||||
if os.path.exists(log_file):
|
||||
current_size = os.path.getsize(log_file)
|
||||
max_bytes = LOG_MAX_SIZE_MB * 1024 * 1024
|
||||
|
||||
if current_size >= max_bytes:
|
||||
# Perform immediate rotation
|
||||
import shutil
|
||||
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
backup_file = f"{log_file}.{timestamp}"
|
||||
try:
|
||||
shutil.move(str(log_file), backup_file)
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not rotate log file: {e}")
|
||||
|
||||
# Clean up old backups if exceeding limit
|
||||
log_dir = os.path.dirname(log_file)
|
||||
log_basename = os.path.basename(log_file)
|
||||
backup_pattern = f"{log_basename}.*"
|
||||
|
||||
import glob
|
||||
backups = sorted(glob.glob(os.path.join(log_dir, backup_pattern)))
|
||||
|
||||
# Keep only the most recent backups
|
||||
while len(backups) > LOG_BACKUP_COUNT:
|
||||
oldest = backups.pop(0)
|
||||
try:
|
||||
os.remove(oldest)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Create new handler with current settings
|
||||
app_handler = RotatingRichHandler(
|
||||
filename=str(log_file),
|
||||
maxBytes=LOG_MAX_SIZE_MB * 1024 * 1024,
|
||||
backupCount=LOG_BACKUP_COUNT,
|
||||
encoding='utf-8'
|
||||
)
|
||||
|
||||
# Set handler level to NOTSET so it processes all records
|
||||
app_handler.setLevel(logging.NOTSET)
|
||||
|
||||
# Configure root logger - set to WARNING to suppress third-party library noise
|
||||
root_logger.setLevel(logging.WARNING)
|
||||
root_logger.addHandler(app_handler)
|
||||
|
||||
# Suppress noisy third-party loggers
|
||||
# These libraries create DEBUG logs that pollute our log file
|
||||
logging.getLogger('asyncio').setLevel(logging.WARNING)
|
||||
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
||||
logging.getLogger('requests').setLevel(logging.WARNING)
|
||||
logging.getLogger('httpx').setLevel(logging.WARNING)
|
||||
logging.getLogger('httpcore').setLevel(logging.WARNING)
|
||||
logging.getLogger('openai').setLevel(logging.WARNING)
|
||||
logging.getLogger('openrouter').setLevel(logging.WARNING)
|
||||
|
||||
# Get or create app logger and set its level (this filters what gets logged)
|
||||
app_logger = logging.getLogger("oai_app")
|
||||
app_logger.setLevel(LOG_LEVEL)
|
||||
# Don't propagate to avoid root logger filtering
|
||||
app_logger.propagate = True
|
||||
|
||||
return app_logger
|
||||
|
||||
app_logger = logging.getLogger("oai_app")
|
||||
app_logger.setLevel(LOG_LEVEL)
|
||||
# Initial logging setup
|
||||
app_logger = setup_logging()
|
||||
|
||||
def set_log_level(level_str: str) -> bool:
|
||||
"""Set the application log level. Returns True if successful."""
|
||||
global LOG_LEVEL, LOG_LEVEL_STR
|
||||
global LOG_LEVEL, LOG_LEVEL_STR, app_logger
|
||||
level_str_lower = level_str.lower()
|
||||
if level_str_lower not in VALID_LOG_LEVELS:
|
||||
return False
|
||||
LOG_LEVEL = VALID_LOG_LEVELS[level_str_lower]
|
||||
LOG_LEVEL_STR = level_str_lower
|
||||
app_logger.setLevel(LOG_LEVEL)
|
||||
|
||||
# Update the logger level immediately
|
||||
if app_logger:
|
||||
app_logger.setLevel(LOG_LEVEL)
|
||||
|
||||
return True
|
||||
|
||||
def reload_logging_config():
|
||||
"""Reload logging configuration from database and reinitialize handler."""
|
||||
global LOG_MAX_SIZE_MB, LOG_BACKUP_COUNT, LOG_LEVEL, LOG_LEVEL_STR, app_logger
|
||||
|
||||
# Reload from database
|
||||
LOG_MAX_SIZE_MB = int(get_config('log_max_size_mb') or "10")
|
||||
LOG_BACKUP_COUNT = int(get_config('log_backup_count') or "2")
|
||||
LOG_LEVEL_STR = get_config('log_level') or "info"
|
||||
LOG_LEVEL = VALID_LOG_LEVELS.get(LOG_LEVEL_STR.lower(), logging.INFO)
|
||||
|
||||
# Reinitialize logging
|
||||
app_logger = setup_logging()
|
||||
|
||||
return app_logger
|
||||
|
||||
# ============================================================================
|
||||
# END OF LOGGING SETUP
|
||||
# ============================================================================
|
||||
@@ -699,7 +790,7 @@ def show_command_help(command: str):
|
||||
|
||||
app_logger.info(f"Displayed detailed help for command: {command}")
|
||||
|
||||
# Load configs
|
||||
# Load configs (AFTER logging is set up)
|
||||
API_KEY = get_config('api_key')
|
||||
OPENROUTER_BASE_URL = get_config('base_url') or "https://openrouter.ai/api/v1"
|
||||
STREAM_ENABLED = get_config('stream_enabled') or "on"
|
||||
@@ -909,7 +1000,7 @@ def display_paginated_table(table: Table, title: str):
|
||||
|
||||
@app.command()
|
||||
def chat():
|
||||
global API_KEY, OPENROUTER_BASE_URL, STREAM_ENABLED, MAX_TOKEN, COST_WARNING_THRESHOLD, DEFAULT_ONLINE_MODE, LOG_MAX_SIZE_MB, LOG_BACKUP_COUNT, LOG_LEVEL, LOG_LEVEL_STR
|
||||
global API_KEY, OPENROUTER_BASE_URL, STREAM_ENABLED, MAX_TOKEN, COST_WARNING_THRESHOLD, DEFAULT_ONLINE_MODE, LOG_MAX_SIZE_MB, LOG_BACKUP_COUNT, LOG_LEVEL, LOG_LEVEL_STR, app_logger
|
||||
session_max_token = 0
|
||||
session_system_prompt = ""
|
||||
session_history = []
|
||||
@@ -1319,9 +1410,13 @@ def chat():
|
||||
console.print("[bold yellow]Usage: /middleout on|off (or /middleout to view status)[/]")
|
||||
continue
|
||||
elif user_input.lower() == "/reset":
|
||||
confirm = typer.confirm("Reset conversation context? This clears history and prompt.", default=False)
|
||||
if not confirm:
|
||||
console.print("[bold yellow]Reset cancelled.[/]")
|
||||
try:
|
||||
confirm = typer.confirm("Reset conversation context? This clears history and prompt.", default=False)
|
||||
if not confirm:
|
||||
console.print("[bold yellow]Reset cancelled.[/]")
|
||||
continue
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
console.print("\n[bold yellow]Reset cancelled.[/]")
|
||||
continue
|
||||
session_history = []
|
||||
current_index = -1
|
||||
@@ -1538,9 +1633,13 @@ def chat():
|
||||
new_size_mb = 100
|
||||
set_config('log_max_size_mb', str(new_size_mb))
|
||||
LOG_MAX_SIZE_MB = new_size_mb
|
||||
console.print(f"[bold green]Log size limit set to {new_size_mb} MB.[/]")
|
||||
console.print("[bold yellow]⚠️ Restart the application for this change to take effect.[/]")
|
||||
app_logger.info(f"Log size limit updated to {new_size_mb} MB (requires restart)")
|
||||
|
||||
# Reload logging configuration immediately
|
||||
app_logger = reload_logging_config()
|
||||
|
||||
console.print(f"[bold green]Log size limit set to {new_size_mb} MB and applied immediately.[/]")
|
||||
console.print(f"[dim cyan]Log file rotated if it exceeded the new limit.[/]")
|
||||
app_logger.info(f"Log size limit updated to {new_size_mb} MB and reloaded")
|
||||
except ValueError:
|
||||
console.print("[bold red]Invalid size. Provide a number in MB.[/]")
|
||||
elif args.startswith("online"):
|
||||
@@ -1778,7 +1877,7 @@ def chat():
|
||||
)
|
||||
help_table.add_row(
|
||||
"/config log [size_mb]",
|
||||
"Set log file size limit in MB. Older logs are rotated automatically. Requires restart.",
|
||||
"Set log file size limit in MB. Older logs are rotated automatically. Takes effect immediately.",
|
||||
"/config log 20"
|
||||
)
|
||||
help_table.add_row(
|
||||
@@ -1930,8 +2029,7 @@ def chat():
|
||||
if not selected_model:
|
||||
console.print("[bold yellow]Select a model first with '/model'.[/]")
|
||||
continue
|
||||
|
||||
# Process file attachments with PDF support
|
||||
# Process file attachments with PDF support
|
||||
content_blocks = []
|
||||
text_part = user_input
|
||||
file_attachments = []
|
||||
|
||||
Reference in New Issue
Block a user