Commit 4ee2fc61 authored by Your Name's avatar Your Name

Add CLI argument support and change default port to 17765

- Changed default port from 8000 to 17765 in config and code
- Added command-line argument parsing with argparse
- Added --config flag to specify custom config directory
- Added --host, --port flags to override config file settings
- Added --https, --ssl-cert, --ssl-key flags for HTTPS configuration
- Added --no-auth flag to disable authentication
- CLI arguments take precedence over config file
- Updated config.py to support custom config directories via AISBF_CONFIG_DIR env var
- Config and handlers are now initialized after CLI parsing to support --config flag
parent 4bae99cf
...@@ -22,10 +22,11 @@ Why did the programmer quit his job? Because he didn't get arrays! ...@@ -22,10 +22,11 @@ Why did the programmer quit his job? Because he didn't get arrays!
Configuration management for AISBF. Configuration management for AISBF.
""" """
from typing import Dict, List, Optional from typing import Dict, List, Optional, Union
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
import json import json
import shutil import shutil
import os
from pathlib import Path from pathlib import Path
class ProviderModelConfig(BaseModel): class ProviderModelConfig(BaseModel):
...@@ -97,6 +98,12 @@ class AppConfig(BaseModel): ...@@ -97,6 +98,12 @@ class AppConfig(BaseModel):
class Config: class Config:
def __init__(self): def __init__(self):
self._custom_config_dir = None
# Check for custom config directory from environment variable
custom_dir = os.environ.get('AISBF_CONFIG_DIR')
if custom_dir:
self._custom_config_dir = Path(custom_dir)
self._ensure_config_directory() self._ensure_config_directory()
self._load_providers() self._load_providers()
self._load_rotations() self._load_rotations()
...@@ -106,6 +113,11 @@ class Config: ...@@ -106,6 +113,11 @@ class Config:
def _get_config_source_dir(self): def _get_config_source_dir(self):
"""Get the directory containing default config files""" """Get the directory containing default config files"""
# If custom config directory is set, use it first
if self._custom_config_dir and self._custom_config_dir.exists():
if (self._custom_config_dir / 'providers.json').exists():
return self._custom_config_dir
# Try installed location first # Try installed location first
installed_dirs = [ installed_dirs = [
Path('/usr/share/aisbf'), Path('/usr/share/aisbf'),
......
{ {
"server": { "server": {
"host": "0.0.0.0", "host": "0.0.0.0",
"port": 8000, "port": 17765,
"protocol": "http", "protocol": "http",
"ssl_certfile": null, "ssl_certfile": null,
"ssl_keyfile": null "ssl_keyfile": null
......
...@@ -28,18 +28,31 @@ from fastapi.middleware.cors import CORSMiddleware ...@@ -28,18 +28,31 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi.exceptions import RequestValidationError from fastapi.exceptions import RequestValidationError
from aisbf.models import ChatCompletionRequest, ChatCompletionResponse from aisbf.models import ChatCompletionRequest, ChatCompletionResponse
from aisbf.handlers import RequestHandler, RotationHandler, AutoselectHandler from aisbf.handlers import RequestHandler, RotationHandler, AutoselectHandler
from aisbf.config import config
from aisbf.database import initialize_database from aisbf.database import initialize_database
import time import time
import logging import logging
import sys import sys
import os import os
import argparse
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
from datetime import datetime, timedelta from datetime import datetime, timedelta
from collections import defaultdict from collections import defaultdict
from pathlib import Path from pathlib import Path
import json import json
# Global variable to store custom config directory
_custom_config_dir = None
def set_config_dir(config_dir: str):
"""Set custom config directory before importing config"""
global _custom_config_dir
_custom_config_dir = config_dir
os.environ['AISBF_CONFIG_DIR'] = config_dir
def get_config_dir():
"""Get custom config directory if set"""
return _custom_config_dir or os.environ.get('AISBF_CONFIG_DIR')
def generate_self_signed_cert(cert_file: Path, key_file: Path): def generate_self_signed_cert(cert_file: Path, key_file: Path):
"""Generate self-signed SSL certificate""" """Generate self-signed SSL certificate"""
try: try:
...@@ -109,10 +122,22 @@ def generate_self_signed_cert(cert_file: Path, key_file: Path): ...@@ -109,10 +122,22 @@ def generate_self_signed_cert(cert_file: Path, key_file: Path):
logger.error("cryptography library not installed. Install with: pip install cryptography") logger.error("cryptography library not installed. Install with: pip install cryptography")
raise raise
def load_server_config(): def load_server_config(custom_config_dir=None):
"""Load server configuration from aisbf.json""" """Load server configuration from aisbf.json"""
# Try user config first # If custom config directory is provided, try it first
config_path = Path.home() / '.aisbf' / 'aisbf.json' if custom_config_dir:
config_path = Path(custom_config_dir) / 'aisbf.json'
if config_path.exists():
pass # Use this path
else:
# Fall through to default locations
config_path = None
else:
config_path = None
# Try user config first if not found in custom dir
if not config_path or not config_path.exists():
config_path = Path.home() / '.aisbf' / 'aisbf.json'
if not config_path.exists(): if not config_path.exists():
# Try installed locations # Try installed locations
...@@ -162,7 +187,7 @@ def load_server_config(): ...@@ -162,7 +187,7 @@ def load_server_config():
return { return {
'host': server_config.get('host', '0.0.0.0'), 'host': server_config.get('host', '0.0.0.0'),
'port': server_config.get('port', 8000), 'port': server_config.get('port', 17765),
'protocol': protocol, 'protocol': protocol,
'ssl_certfile': ssl_certfile if protocol == 'https' else None, 'ssl_certfile': ssl_certfile if protocol == 'https' else None,
'ssl_keyfile': ssl_keyfile if protocol == 'https' else None, 'ssl_keyfile': ssl_keyfile if protocol == 'https' else None,
...@@ -176,7 +201,7 @@ def load_server_config(): ...@@ -176,7 +201,7 @@ def load_server_config():
# Return defaults # Return defaults
return { return {
'host': '0.0.0.0', 'host': '0.0.0.0',
'port': 8000, 'port': 17765,
'protocol': 'http', 'protocol': 'http',
'ssl_certfile': None, 'ssl_certfile': None,
'ssl_keyfile': None, 'ssl_keyfile': None,
...@@ -313,16 +338,17 @@ def setup_logging(): ...@@ -313,16 +338,17 @@ def setup_logging():
# Configure logging # Configure logging
logger = setup_logging() logger = setup_logging()
# Load server configuration # Note: config will be imported after parsing CLI args if --config is provided
server_config = load_server_config() # For now, we'll delay the import and initialization
# Initialize handlers
request_handler = RequestHandler()
rotation_handler = RotationHandler()
autoselect_handler = AutoselectHandler()
app = FastAPI(title="AI Proxy Server") app = FastAPI(title="AI Proxy Server")
# These will be initialized in main() after config is loaded
request_handler = None
rotation_handler = None
autoselect_handler = None
server_config = None
config = None
# Authentication middleware # Authentication middleware
@app.middleware("http") @app.middleware("http")
async def auth_middleware(request: Request, call_next): async def auth_middleware(request: Request, call_next):
...@@ -713,14 +739,96 @@ def main(): ...@@ -713,14 +739,96 @@ def main():
"""Main entry point for the AISBF server""" """Main entry point for the AISBF server"""
import uvicorn import uvicorn
# Parse command-line arguments
parser = argparse.ArgumentParser(
description='AISBF - AI Service Broker Framework',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
aisbf # Start with default settings
aisbf --host 127.0.0.1 --port 8080 # Custom host and port
aisbf --config /path/to/config # Use custom config directory
aisbf --https --ssl-cert cert.pem # Enable HTTPS with custom cert
"""
)
parser.add_argument('--config', type=str, help='Custom config directory path')
parser.add_argument('--host', type=str, help='Server host (default: 0.0.0.0)')
parser.add_argument('--port', type=int, help='Server port (default: 17765)')
parser.add_argument('--https', action='store_true', help='Enable HTTPS')
parser.add_argument('--ssl-cert', type=str, help='SSL certificate file path')
parser.add_argument('--ssl-key', type=str, help='SSL private key file path')
parser.add_argument('--no-auth', action='store_true', help='Disable authentication (override config)')
args = parser.parse_args()
# Set custom config directory if provided
if args.config:
set_config_dir(args.config)
logger.info(f"Using custom config directory: {args.config}")
# Import config after setting custom directory
global config, request_handler, rotation_handler, autoselect_handler, server_config
from aisbf.config import config
from aisbf.handlers import RequestHandler, RotationHandler, AutoselectHandler
# Initialize handlers
request_handler = RequestHandler()
rotation_handler = RotationHandler()
autoselect_handler = AutoselectHandler()
# Load server configuration # Load server configuration
server_config = load_server_config() server_config = load_server_config(args.config)
host = server_config['host']
port = server_config['port'] # CLI arguments take precedence over config file
protocol = server_config.get('protocol', 'http') host = args.host if args.host else server_config['host']
ssl_certfile = server_config.get('ssl_certfile') port = args.port if args.port else server_config['port']
ssl_keyfile = server_config.get('ssl_keyfile')
auth_enabled = server_config.get('auth_enabled', False) # Protocol handling
if args.https:
protocol = 'https'
ssl_certfile = args.ssl_cert if args.ssl_cert else server_config.get('ssl_certfile')
ssl_keyfile = args.ssl_key if args.ssl_key else server_config.get('ssl_keyfile')
# Auto-generate if not provided
if not ssl_certfile or not ssl_keyfile:
ssl_dir = Path.home() / '.aisbf' / 'ssl'
ssl_certfile = str(ssl_dir / 'cert.pem')
ssl_keyfile = str(ssl_dir / 'key.pem')
cert_path = Path(ssl_certfile).expanduser()
key_path = Path(ssl_keyfile).expanduser()
if not cert_path.exists() or not key_path.exists():
generate_self_signed_cert(cert_path, key_path)
else:
protocol = server_config.get('protocol', 'http')
ssl_certfile = server_config.get('ssl_certfile')
ssl_keyfile = server_config.get('ssl_keyfile')
# Handle HTTPS from config
if protocol == 'https':
if not ssl_certfile or not ssl_keyfile:
ssl_dir = Path.home() / '.aisbf' / 'ssl'
ssl_certfile = str(ssl_dir / 'cert.pem')
ssl_keyfile = str(ssl_dir / 'key.pem')
cert_path = Path(ssl_certfile).expanduser()
key_path = Path(ssl_keyfile).expanduser()
if not cert_path.exists() or not key_path.exists():
generate_self_signed_cert(cert_path, key_path)
# Authentication handling
auth_enabled = not args.no_auth and server_config.get('auth_enabled', False)
# Update global server_config with final values
server_config['host'] = host
server_config['port'] = port
server_config['protocol'] = protocol
server_config['ssl_certfile'] = ssl_certfile if protocol == 'https' else None
server_config['ssl_keyfile'] = ssl_keyfile if protocol == 'https' else None
server_config['auth_enabled'] = auth_enabled
# Log server configuration # Log server configuration
logger.info(f"=== AISBF Server Configuration ===") logger.info(f"=== AISBF Server Configuration ===")
...@@ -728,6 +836,8 @@ def main(): ...@@ -728,6 +836,8 @@ def main():
logger.info(f"Host: {host}") logger.info(f"Host: {host}")
logger.info(f"Port: {port}") logger.info(f"Port: {port}")
logger.info(f"Authentication: {'Enabled' if auth_enabled else 'Disabled'}") logger.info(f"Authentication: {'Enabled' if auth_enabled else 'Disabled'}")
if args.config:
logger.info(f"Config Directory: {args.config}")
if protocol == 'https': if protocol == 'https':
logger.info(f"SSL Certificate: {ssl_certfile}") logger.info(f"SSL Certificate: {ssl_certfile}")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment