bug fixes
This commit is contained in:
134
oai.py
134
oai.py
@@ -30,7 +30,7 @@ from packaging import version as pkg_version
|
|||||||
import io # Added for custom handler
|
import io # Added for custom handler
|
||||||
|
|
||||||
# App version. Changes by author with new releases.
|
# App version. Changes by author with new releases.
|
||||||
version = '1.9.5'
|
version = '1.9.6'
|
||||||
|
|
||||||
app = typer.Typer()
|
app = typer.Typer()
|
||||||
|
|
||||||
@@ -369,41 +369,132 @@ class RotatingRichHandler(RotatingFileHandler):
|
|||||||
except Exception:
|
except Exception:
|
||||||
self.handleError(record)
|
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_MAX_SIZE_MB = int(get_config('log_max_size_mb') or "10")
|
||||||
LOG_BACKUP_COUNT = int(get_config('log_backup_count') or "2")
|
LOG_BACKUP_COUNT = int(get_config('log_backup_count') or "2")
|
||||||
LOG_LEVEL_STR = get_config('log_level') or "info"
|
LOG_LEVEL_STR = get_config('log_level') or "info"
|
||||||
LOG_LEVEL = VALID_LOG_LEVELS.get(LOG_LEVEL_STR.lower(), logging.INFO)
|
LOG_LEVEL = VALID_LOG_LEVELS.get(LOG_LEVEL_STR.lower(), logging.INFO)
|
||||||
|
|
||||||
# Create the custom rotating handler
|
# Global reference to the handler for dynamic reloading
|
||||||
|
app_handler = None
|
||||||
|
app_logger = None
|
||||||
|
|
||||||
|
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(
|
app_handler = RotatingRichHandler(
|
||||||
filename=str(log_file),
|
filename=str(log_file),
|
||||||
maxBytes=LOG_MAX_SIZE_MB * 1024 * 1024, # Convert MB to bytes
|
maxBytes=LOG_MAX_SIZE_MB * 1024 * 1024,
|
||||||
backupCount=LOG_BACKUP_COUNT,
|
backupCount=LOG_BACKUP_COUNT,
|
||||||
encoding='utf-8'
|
encoding='utf-8'
|
||||||
)
|
)
|
||||||
|
|
||||||
logging.basicConfig(
|
# Set handler level to NOTSET so it processes all records
|
||||||
level=logging.NOTSET,
|
app_handler.setLevel(logging.NOTSET)
|
||||||
format="%(message)s", # Rich formats it
|
|
||||||
datefmt="[%X]",
|
|
||||||
handlers=[app_handler]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# 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 = logging.getLogger("oai_app")
|
||||||
app_logger.setLevel(LOG_LEVEL)
|
app_logger.setLevel(LOG_LEVEL)
|
||||||
|
# Don't propagate to avoid root logger filtering
|
||||||
|
app_logger.propagate = True
|
||||||
|
|
||||||
|
return app_logger
|
||||||
|
|
||||||
|
# Initial logging setup
|
||||||
|
app_logger = setup_logging()
|
||||||
|
|
||||||
def set_log_level(level_str: str) -> bool:
|
def set_log_level(level_str: str) -> bool:
|
||||||
"""Set the application log level. Returns True if successful."""
|
"""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()
|
level_str_lower = level_str.lower()
|
||||||
if level_str_lower not in VALID_LOG_LEVELS:
|
if level_str_lower not in VALID_LOG_LEVELS:
|
||||||
return False
|
return False
|
||||||
LOG_LEVEL = VALID_LOG_LEVELS[level_str_lower]
|
LOG_LEVEL = VALID_LOG_LEVELS[level_str_lower]
|
||||||
LOG_LEVEL_STR = level_str_lower
|
LOG_LEVEL_STR = level_str_lower
|
||||||
|
|
||||||
|
# Update the logger level immediately
|
||||||
|
if app_logger:
|
||||||
app_logger.setLevel(LOG_LEVEL)
|
app_logger.setLevel(LOG_LEVEL)
|
||||||
|
|
||||||
return True
|
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
|
# END OF LOGGING SETUP
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -699,7 +790,7 @@ def show_command_help(command: str):
|
|||||||
|
|
||||||
app_logger.info(f"Displayed detailed help for command: {command}")
|
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')
|
API_KEY = get_config('api_key')
|
||||||
OPENROUTER_BASE_URL = get_config('base_url') or "https://openrouter.ai/api/v1"
|
OPENROUTER_BASE_URL = get_config('base_url') or "https://openrouter.ai/api/v1"
|
||||||
STREAM_ENABLED = get_config('stream_enabled') or "on"
|
STREAM_ENABLED = get_config('stream_enabled') or "on"
|
||||||
@@ -909,7 +1000,7 @@ def display_paginated_table(table: Table, title: str):
|
|||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def chat():
|
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_max_token = 0
|
||||||
session_system_prompt = ""
|
session_system_prompt = ""
|
||||||
session_history = []
|
session_history = []
|
||||||
@@ -1319,10 +1410,14 @@ def chat():
|
|||||||
console.print("[bold yellow]Usage: /middleout on|off (or /middleout to view status)[/]")
|
console.print("[bold yellow]Usage: /middleout on|off (or /middleout to view status)[/]")
|
||||||
continue
|
continue
|
||||||
elif user_input.lower() == "/reset":
|
elif user_input.lower() == "/reset":
|
||||||
|
try:
|
||||||
confirm = typer.confirm("Reset conversation context? This clears history and prompt.", default=False)
|
confirm = typer.confirm("Reset conversation context? This clears history and prompt.", default=False)
|
||||||
if not confirm:
|
if not confirm:
|
||||||
console.print("[bold yellow]Reset cancelled.[/]")
|
console.print("[bold yellow]Reset cancelled.[/]")
|
||||||
continue
|
continue
|
||||||
|
except (EOFError, KeyboardInterrupt):
|
||||||
|
console.print("\n[bold yellow]Reset cancelled.[/]")
|
||||||
|
continue
|
||||||
session_history = []
|
session_history = []
|
||||||
current_index = -1
|
current_index = -1
|
||||||
session_system_prompt = ""
|
session_system_prompt = ""
|
||||||
@@ -1538,9 +1633,13 @@ def chat():
|
|||||||
new_size_mb = 100
|
new_size_mb = 100
|
||||||
set_config('log_max_size_mb', str(new_size_mb))
|
set_config('log_max_size_mb', str(new_size_mb))
|
||||||
LOG_MAX_SIZE_MB = 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.[/]")
|
# Reload logging configuration immediately
|
||||||
app_logger.info(f"Log size limit updated to {new_size_mb} MB (requires restart)")
|
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:
|
except ValueError:
|
||||||
console.print("[bold red]Invalid size. Provide a number in MB.[/]")
|
console.print("[bold red]Invalid size. Provide a number in MB.[/]")
|
||||||
elif args.startswith("online"):
|
elif args.startswith("online"):
|
||||||
@@ -1778,7 +1877,7 @@ def chat():
|
|||||||
)
|
)
|
||||||
help_table.add_row(
|
help_table.add_row(
|
||||||
"/config log [size_mb]",
|
"/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"
|
"/config log 20"
|
||||||
)
|
)
|
||||||
help_table.add_row(
|
help_table.add_row(
|
||||||
@@ -1930,7 +2029,6 @@ def chat():
|
|||||||
if not selected_model:
|
if not selected_model:
|
||||||
console.print("[bold yellow]Select a model first with '/model'.[/]")
|
console.print("[bold yellow]Select a model first with '/model'.[/]")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Process file attachments with PDF support
|
# Process file attachments with PDF support
|
||||||
content_blocks = []
|
content_blocks = []
|
||||||
text_part = user_input
|
text_part = user_input
|
||||||
|
|||||||
Reference in New Issue
Block a user