From f7a601c6bc92d01c3679071a43d03604420c28ad Mon Sep 17 00:00:00 2001 From: Luke Hinds Date: Wed, 22 Jan 2025 12:19:48 +0000 Subject: [PATCH 1/5] =?UTF-8?q?Add=20configurable=20LiteLLM=20logging=20co?= =?UTF-8?q?ntrol=20=F0=9F=99=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I could not take the noise anymore Add the ability to control LiteLLM logging through configuration: - Add --enable-litellm CLI flag - Add CODEGATE_ENABLE_LITELLM environment variable - Add external_loggers.litellm config file option - Set logger level to CRITICAL+1 when disabled to suppress all logging - Update documentation with new logging configuration options This change allows users to enable LiteLLM debug logging when needed while keeping it disabled by default to reduce noise. --- config.yaml.example | 18 ++++ docs/cli.md | 102 +++++++----------- docs/configuration.md | 178 +++++++++++++++++++------------ docs/logging.md | 45 +++++++- src/codegate/cli.py | 23 ++-- src/codegate/codegate_logging.py | 44 +++++++- src/codegate/config.py | 18 ++++ 7 files changed, 286 insertions(+), 142 deletions(-) diff --git a/config.yaml.example b/config.yaml.example index 05edcbc9..3e7d0914 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -62,3 +62,21 @@ chat_model_n_ctx: 32768 # Number of layers to offload to GPU. If -1, all layers are offloaded. chat_model_n_gpu_layers: -1 + +# External logger configuration +external_loggers: + litellm: false # Enable/disable LiteLLM logging (includes LiteLLM Proxy, Router, and core) + sqlalchemy: false # Enable/disable SQLAlchemy logging + uvicorn.error: false # Enable/disable Uvicorn error logging + aiosqlite: false # Enable/disable aiosqlite logging + +# Note: External logger configuration can be overridden by: +# 1. Environment variables: +# CODEGATE_ENABLE_LITELLM=true # Controls all LiteLLM loggers +# CODEGATE_ENABLE_SQLALCHEMY=true +# CODEGATE_ENABLE_UVICORN_ERROR=true +# CODEGATE_ENABLE_AIOSQLITE=true +# 2. CLI arguments: +# --enable-litellm # Enables LiteLLM logging +# --enable-sqlalchemy # Enables SQLAlchemy logging +# etc. diff --git a/docs/cli.md b/docs/cli.md index 83c3d6aa..825363e6 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -47,99 +47,63 @@ codegate serve [OPTIONS] - `--prompts FILE`: Path to YAML prompts file - Optional - Must be a valid YAML file - - Overrides default prompts and configuration file prompts -- `--vllm-url TEXT`: vLLM provider URL (default: `http://localhost:8000`) +- `--vllm-url TEXT`: vLLM provider URL - Optional - - Base URL for vLLM provider (/v1 path is added automatically) - - Overrides configuration file and environment variables + - Default: http://localhost:8000/v1 -- `--openai-url TEXT`: OpenAI provider URL (default: - `https://api.openai.com/v1`) +- `--openai-url TEXT`: OpenAI provider URL - Optional - - Base URL for OpenAI provider - - Overrides configuration file and environment variables + - Default: https://api.openai.com/v1 -- `--anthropic-url TEXT`: Anthropic provider URL (default: - `https://api.anthropic.com/v1`) +- `--anthropic-url TEXT`: Anthropic provider URL - Optional - - Base URL for Anthropic provider - - Overrides configuration file and environment variables + - Default: https://api.anthropic.com/v1 -- `--ollama-url TEXT`: Ollama provider URL (default: `http://localhost:11434`) +- `--ollama-url TEXT`: Ollama provider URL - Optional - - Base URL for Ollama provider (/api path is added automatically) - - Overrides configuration file and environment variables + - Default: http://localhost:11434/api -- `--model-base-path TEXT`: Base path for loading models needed for the system +- `--model-base-path TEXT`: Path to model base directory - Optional + - Default: ./codegate_volume/models -- `--embedding-model TEXT`: Name of the model used for embeddings +- `--embedding-model TEXT`: Name of embedding model - Optional + - Default: all-minilm-L6-v2-q5_k_m.gguf -- `--db-path TEXT`: Path to a SQLite DB. Will be created if it doesn't exist. - (default: `./codegate_volume/db/codegate.db`) - - Optional - - Overrides configuration file and environment variables - -### `show-prompts` - -Display the loaded system prompts: - -```bash -codegate show-prompts [OPTIONS] -``` - -#### Options - -- `--prompts FILE`: Path to YAML prompts file +- `--certs-dir TEXT`: Directory for certificate files - Optional - - Must be a valid YAML file - - If not provided, shows default prompts from `prompts/default.yaml` - -### `generate_certs` - -Generate certificates for the CodeGate server. + - Default: ./certs -```bash -codegate generate-certs [OPTIONS] -``` - -#### Options - -- `--certs-out-dir PATH`: Directory path where the certificates are generated - (default: ./codegate_volume/certs) +- `--ca-cert TEXT`: CA certificate file name - Optional - - Overrides configuration file and environment variables + - Default: ca.crt -- `--ca-cert-name TEXT`: Name that will be given to the created CA certificate - (default: ca.crt) +- `--ca-key TEXT`: CA key file name - Optional - - Overrides configuration file and environment variables + - Default: ca.key -- `--ca-key-name TEXT`: Name that will be given to the created CA key (default: - ca.key) +- `--server-cert TEXT`: Server certificate file name - Optional - - Overrides configuration file and environment variables + - Default: server.crt -- `--server-cert-name TEXT`: Name that will be given to the created server - certificate (default: server.crt) +- `--server-key TEXT`: Server key file name - Optional - - Overrides configuration file and environment variables + - Default: server.key -- `--server-key-name TEXT`: Name that will be given to the created server key - (default: server.key) +- `--db-path TEXT`: Path to main SQLite database file - Optional - - Overrides configuration file and environment variables + - Default: ./codegate_volume/db/codegate.db -- `--log-level [ERROR|WARNING|INFO|DEBUG]`: Set the log level (default: INFO) +- `--vec-db-path TEXT`: Path to vector SQLite database file - Optional - - Case-insensitive - - Overrides configuration file and environment variables + - Default: ./sqlite_data/vectordb.db -- `--log-format [JSON|TEXT]`: Set the log format (default: JSON) - - Optional - - Case-insensitive +- `--enable-litellm`: Enable LiteLLM logging + - Optional flag + - Default: false + - Enables logging for LiteLLM Proxy, Router, and core components - Overrides configuration file and environment variables ## Error handling @@ -175,6 +139,12 @@ Start server with custom logging: codegate serve --log-level DEBUG --log-format TEXT ``` +Start server with LiteLLM logging enabled: + +```bash +codegate serve --enable-litellm --log-level DEBUG +``` + Start server with configuration file: ```bash diff --git a/docs/configuration.md b/docs/configuration.md index 67058151..10d8e576 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,74 +1,67 @@ -# Configuration system +# Configuration -The configuration system in CodeGate is managed through the `Config` class in -`config.py`. It supports multiple configuration sources with a clear priority -order. - -## Configuration priority - -Configuration sources are evaluated in the following order, from highest to -lowest priority: - -1. CLI arguments -2. Environment variables -3. Configuration file (YAML) -4. Default values (including default prompts from `prompts/default.yaml`) - -Values from higher-priority sources take precedence over lower-priority values. - -## Default configuration values - -- Port: `8989` -- Proxy port: `8990` -- Host: `"localhost"` -- Log level: `"INFO"` -- Log format: `"JSON"` -- Prompts: default prompts from `prompts/default.yaml` -- Provider URLs: - - vLLM: `"http://localhost:8000"` - - OpenAI: `"https://api.openai.com/v1"` - - Anthropic: `"https://api.anthropic.com/v1"` - - Ollama: `"http://localhost:11434"` -- Certificate configuration: - - Certs directory: `"./certs"` - - CA certificate: `"ca.crt"` - - CA private key: `"ca.key"` - - Server certificate: `"server.crt"` - - Server private key: `"server.key"` +CodeGate's configuration system provides flexible configuration through multiple +methods with clear priority resolution. ## Configuration methods -### Configuration file +Configuration can be set through: -Load configuration from a YAML file: +1. CLI arguments (highest priority) +2. Environment variables +3. Configuration file +4. Default values (lowest priority) -```python -config = Config.from_file("config.yaml") -``` +## Configuration file -Example config.yaml: +The configuration file uses YAML format: ```yaml +# Network settings port: 8989 proxy_port: 8990 -host: localhost -log_level: INFO -log_format: JSON -provider_urls: - vllm: "https://vllm.example.com" - openai: "https://api.openai.com/v1" - anthropic: "https://api.anthropic.com/v1" - ollama: "http://localhost:11434" -certs_dir: "./certs" +host: "localhost" + +# Logging configuration +log_level: INFO # ERROR, WARNING, INFO, or DEBUG +log_format: JSON # JSON or TEXT + +# External logger configuration +external_loggers: + litellm: false # Enable/disable LiteLLM logging (includes LiteLLM Proxy, Router, and core) + sqlalchemy: false # Enable/disable SQLAlchemy logging + uvicorn.error: false # Enable/disable Uvicorn error logging + aiosqlite: false # Enable/disable aiosqlite logging + +# Model configuration +model_base_path: "./codegate_volume/models" +chat_model_n_ctx: 32768 +chat_model_n_gpu_layers: -1 +embedding_model: "all-minilm-L6-v2-q5_k_m.gguf" + +# Database configuration +db_path: "./codegate_volume/db/codegate.db" +vec_db_path: "./sqlite_data/vectordb.db" + +# Certificate configuration +certs_dir: "./codegate_volume/certs" ca_cert: "ca.crt" ca_key: "ca.key" server_cert: "server.crt" server_key: "server.key" +force_certs: false + +# Provider URLs +provider_urls: + vllm: "http://localhost:8000" + openai: "https://api.openai.com/v1" + anthropic: "https://api.anthropic.com/v1" + ollama: "http://localhost:11434" ``` -### Environment variables +## Environment variables -Environment variables are automatically loaded with these mappings: +Environment variables follow the pattern `CODEGATE_*`: - `CODEGATE_APP_PORT`: server port - `CODEGATE_APP_PROXY_PORT`: server proxy port @@ -85,6 +78,10 @@ Environment variables are automatically loaded with these mappings: - `CODEGATE_CA_KEY`: CA key file name - `CODEGATE_SERVER_CERT`: server certificate file name - `CODEGATE_SERVER_KEY`: server key file name +- `CODEGATE_ENABLE_LITELLM`: enable LiteLLM logging +- `CODEGATE_ENABLE_SQLALCHEMY`: enable SQLAlchemy logging +- `CODEGATE_ENABLE_UVICORN_ERROR`: enable Uvicorn error logging +- `CODEGATE_ENABLE_AIOSQLITE`: enable aiosqlite logging ```python config = Config.from_env() @@ -118,24 +115,54 @@ Network settings can be configured in several ways: codegate serve --port 8989 --proxy-port 8990 --host localhost ``` +### Logging configuration + +Logging can be configured through: + +1. Configuration file: + + ```yaml + log_level: DEBUG + log_format: TEXT + external_loggers: + litellm: true + sqlalchemy: false + uvicorn.error: false + aiosqlite: false + ``` + +2. Environment variables: + + ```bash + export CODEGATE_APP_LOG_LEVEL=DEBUG + export CODEGATE_LOG_FORMAT=TEXT + export CODEGATE_ENABLE_LITELLM=true + ``` + +3. CLI flags: + + ```bash + codegate serve --log-level DEBUG --log-format TEXT --enable-litellm + ``` + ### Provider URLs -Provider URLs can be configured in several ways: +Provider URLs can be configured through: 1. Configuration file: ```yaml provider_urls: - vllm: "https://vllm.example.com" # /v1 path is added automatically + vllm: "http://localhost:8000" openai: "https://api.openai.com/v1" anthropic: "https://api.anthropic.com/v1" - ollama: "http://localhost:11434" # /api path is added automatically + ollama: "http://localhost:11434" ``` 2. Environment variables: ```bash - export CODEGATE_PROVIDER_VLLM_URL=https://vllm.example.com + export CODEGATE_PROVIDER_VLLM_URL=http://localhost:8000 export CODEGATE_PROVIDER_OPENAI_URL=https://api.openai.com/v1 export CODEGATE_PROVIDER_ANTHROPIC_URL=https://api.anthropic.com/v1 export CODEGATE_PROVIDER_OLLAMA_URL=http://localhost:11434 @@ -144,19 +171,12 @@ Provider URLs can be configured in several ways: 3. CLI flags: ```bash - codegate serve --vllm-url https://vllm.example.com --ollama-url http://localhost:11434 + codegate serve --vllm-url http://localhost:8000 --openai-url https://api.openai.com/v1 ``` -Note: - -- For the vLLM provider, the `/v1` path is automatically appended to the base - URL if not present. -- For the Ollama provider, the `/api` path is automatically appended to the base - URL if not present. - ### Certificate configuration -Certificate files can be configured in several ways: +Certificate settings can be configured through: 1. Configuration file: @@ -200,6 +220,32 @@ Available log formats (case-insensitive): - `JSON` - `TEXT` +### External loggers + +External logger configuration controls logging for third-party components: + +1. Configuration file: + ```yaml + external_loggers: + litellm: false # LiteLLM logging (Proxy, Router, core) + sqlalchemy: false # SQLAlchemy logging + uvicorn.error: false # Uvicorn error logging + aiosqlite: false # aiosqlite logging + ``` + +2. Environment variables: + ```bash + export CODEGATE_ENABLE_LITELLM=true + export CODEGATE_ENABLE_SQLALCHEMY=true + export CODEGATE_ENABLE_UVICORN_ERROR=true + export CODEGATE_ENABLE_AIOSQLITE=true + ``` + +3. CLI flags: + ```bash + codegate serve --enable-litellm + ``` + ### Prompts configuration Prompts can be configured in several ways: diff --git a/docs/logging.md b/docs/logging.md index a88cb212..0666cac7 100644 --- a/docs/logging.md +++ b/docs/logging.md @@ -10,6 +10,39 @@ Logs are automatically routed based on their level: - **stdout**: INFO and DEBUG messages - **stderr**: ERROR, CRITICAL, and WARNING messages +## External Logger Configuration + +CodeGate provides control over external loggers through configuration: + +### LiteLLM Logging + +LiteLLM logging can be controlled through: + +1. CLI flag: + ```bash + codegate serve --enable-litellm + ``` + +2. Environment variable: + ```bash + export CODEGATE_ENABLE_LITELLM=true + ``` + +3. Configuration file: + ```yaml + external_loggers: + litellm: true # Enable/disable LiteLLM logging (includes LiteLLM Proxy, Router, and core) + sqlalchemy: false # Enable/disable SQLAlchemy logging + uvicorn.error: false # Enable/disable Uvicorn error logging + aiosqlite: false # Enable/disable aiosqlite logging + ``` + +The configuration follows the priority order: +1. CLI arguments (highest priority) +2. Environment variables +3. Config file +4. Default values (lowest priority) + ## Log formats ### JSON format @@ -45,6 +78,7 @@ YYYY-MM-DDThh:mm:ss.mmmZ - LEVEL - NAME - MESSAGE - **Exception support**: full exception and stack trace integration - **Dual output**: separate handlers for error and non-error logs - **Configurable levels**: support for ERROR, WARNING, INFO, and DEBUG levels +- **External logger control**: fine-grained control over third-party logging ## Usage examples @@ -89,14 +123,15 @@ The logging system can be configured through: 1. CLI arguments: ```bash - codegate serve --log-level DEBUG --log-format TEXT + codegate serve --log-level DEBUG --log-format TEXT --enable-litellm ``` 2. Environment variables: ```bash - export APP_LOG_LEVEL=DEBUG + export CODEGATE_APP_LOG_LEVEL=DEBUG export CODEGATE_LOG_FORMAT=TEXT + export CODEGATE_ENABLE_LITELLM=true ``` 3. Configuration file: @@ -104,6 +139,8 @@ The logging system can be configured through: ```yaml log_level: DEBUG log_format: TEXT + external_loggers: + litellm: true ``` ## Best practices @@ -129,3 +166,7 @@ The logging system can be configured through: better log aggregation and analysis. 4. Enable `DEBUG` level logging during development for maximum visibility. + +5. Configure external loggers based on your needs: + - Enable LiteLLM logging when debugging LLM-related issues + - Keep external loggers disabled in production unless needed for troubleshooting diff --git a/src/codegate/cli.py b/src/codegate/cli.py index 7bcd035d..f1ee8a2c 100644 --- a/src/codegate/cli.py +++ b/src/codegate/cli.py @@ -246,6 +246,12 @@ def show_prompts(prompts: Optional[Path]) -> None: default=None, help="Path to the vector SQLite database file (default: ./sqlite_data/vectordb.db)", ) +@click.option( + "--enable-litellm", + is_flag=True, + default=False, + help="Enable LiteLLM logging (includes LiteLLM Proxy, Router, and core)", +) def serve( port: Optional[int], proxy_port: Optional[int], @@ -267,11 +273,12 @@ def serve( ca_key: Optional[str], server_cert: Optional[str], server_key: Optional[str], + enable_litellm: bool, ) -> None: """Start the codegate server.""" try: - # Create provider URLs dict from CLI options - cli_provider_urls: Dict[str, str] = {} + # Create provider URLs dictionary from CLI arguments + cli_provider_urls = {} if vllm_url: cli_provider_urls["vllm"] = vllm_url if openai_url: @@ -281,6 +288,9 @@ def serve( if ollama_url: cli_provider_urls["ollama"] = ollama_url + # Create external loggers dictionary from CLI arguments + cli_external_loggers = {"litellm": enable_litellm} + # Load configuration with priority resolution cfg = Config.load( config_path=config, @@ -290,7 +300,8 @@ def serve( cli_host=host, cli_log_level=log_level, cli_log_format=log_format, - cli_provider_urls=cli_provider_urls, + cli_provider_urls=cli_provider_urls if cli_provider_urls else None, + cli_external_loggers=cli_external_loggers, model_base_path=model_base_path, embedding_model=embedding_model, certs_dir=certs_dir, @@ -302,8 +313,8 @@ def serve( vec_db_path=vec_db_path, ) - # Set up logging first - setup_logging(cfg.log_level, cfg.log_format) + # Initialize logging + setup_logging(cfg.log_level, cfg.log_format, cfg.external_loggers) logger = structlog.get_logger("codegate").bind(origin="cli") init_db_sync(cfg.db_path) @@ -505,7 +516,7 @@ def generate_certs( cli_log_level=log_level, cli_log_format=log_format, ) - setup_logging(cfg.log_level, cfg.log_format) + setup_logging(cfg.log_level, cfg.log_format, cfg.external_loggers) logger = structlog.get_logger("codegate").bind(origin="cli") ca = CertificateAuthority.get_instance() diff --git a/src/codegate/codegate_logging.py b/src/codegate/codegate_logging.py index 36d0d351..44fe1380 100644 --- a/src/codegate/codegate_logging.py +++ b/src/codegate/codegate_logging.py @@ -3,7 +3,7 @@ import sys from datetime import datetime from enum import Enum -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Optional import structlog @@ -48,6 +48,29 @@ def _missing_(cls, value: str) -> Optional["LogFormat"]: ) +# Define all LiteLLM logger names +LITELLM_LOGGERS = [ + "LiteLLM Proxy", + "LiteLLM Router", + "LiteLLM" +] + + +def configure_litellm_logging(enabled: bool = False, level: LogLevel = LogLevel.INFO) -> None: + """Configure LiteLLM logging. + + Args: + enabled: Whether to enable LiteLLM logging + level: Log level to use if enabled + """ + for logger_name in LITELLM_LOGGERS: + logger = logging.getLogger(logger_name) + if not enabled: + logger.setLevel(logging.CRITICAL + 1) # Effectively disables all logging + else: + logger.setLevel(getattr(logging, level.value)) + + def add_origin(logger, log_method, event_dict): # Add 'origin' if it's bound to the logger but not explicitly in the event dict if "origin" not in event_dict and hasattr(logger, "_context"): @@ -58,13 +81,17 @@ def add_origin(logger, log_method, event_dict): def setup_logging( - log_level: Optional[LogLevel] = None, log_format: Optional[LogFormat] = None + log_level: Optional[LogLevel] = None, + log_format: Optional[LogFormat] = None, + external_loggers: Optional[Dict[str, bool]] = None ) -> logging.Logger: """Configure the logging system. Args: log_level: The logging level to use. Defaults to INFO if not specified. log_format: The log format to use. Defaults to JSON if not specified. + external_loggers: Dictionary of external logger names and whether they should be enabled. + e.g. {"litellm": False, "sqlalchemy": False, "uvicorn.error": False} This configures two handlers: - stderr_handler: For ERROR, CRITICAL, and WARNING messages @@ -74,6 +101,19 @@ def setup_logging( log_level = LogLevel.INFO if log_format is None: log_format = LogFormat.JSON + if external_loggers is None: + external_loggers = { + "litellm": False, + "sqlalchemy": False, + "uvicorn.error": False, + "aiosqlite": False + } + + # Configure LiteLLM logging based on external_loggers setting + configure_litellm_logging( + enabled=external_loggers.get("litellm", False), + level=log_level + ) # The configuration was taken from structlog documentation # https://www.structlog.org/en/stable/standard-library.html diff --git a/src/codegate/config.py b/src/codegate/config.py index 3f99fd04..3ba1b433 100644 --- a/src/codegate/config.py +++ b/src/codegate/config.py @@ -38,6 +38,14 @@ class Config: log_format: LogFormat = LogFormat.JSON prompts: PromptConfig = field(default_factory=PromptConfig) + # External logger configuration + external_loggers: Dict[str, bool] = field(default_factory=lambda: { + "litellm": False, + "sqlalchemy": False, + "uvicorn.error": False, + "aiosqlite": False + }) + model_base_path: str = "./codegate_volume/models" chat_model_n_ctx: int = 32768 chat_model_n_gpu_layers: int = -1 @@ -222,6 +230,7 @@ def load( cli_log_level: Optional[str] = None, cli_log_format: Optional[str] = None, cli_provider_urls: Optional[Dict[str, str]] = None, + cli_external_loggers: Optional[Dict[str, bool]] = None, model_base_path: Optional[str] = None, embedding_model: Optional[str] = None, certs_dir: Optional[str] = None, @@ -250,6 +259,7 @@ def load( cli_log_level: Optional CLI log level override cli_log_format: Optional CLI log format override cli_provider_urls: Optional dict of provider URLs from CLI + cli_external_loggers: Optional dict of external logger configuration from CLI model_base_path: Optional path to model base directory embedding_model: Optional name of the model to use for embeddings certs_dir: Optional path to certificates directory @@ -317,6 +327,12 @@ def load( for provider, url in env_config.provider_urls.items(): config.provider_urls[provider] = url + # Override external logger configuration from environment + for logger_name, enabled in env_config.external_loggers.items(): + env_var = f"CODEGATE_ENABLE_{logger_name.upper().replace('.', '_')}" + if env_var in os.environ: + config.external_loggers[logger_name] = enabled + # Override with CLI arguments if cli_port is not None: config.port = cli_port @@ -352,6 +368,8 @@ def load( config.vec_db_path = vec_db_path if force_certs is not None: config.force_certs = force_certs + if cli_external_loggers is not None: + config.external_loggers.update(cli_external_loggers) # Set the __config class attribute Config.__config = config From 66cdc60d4f8a0a9f92853b4ab894014bb4663bce Mon Sep 17 00:00:00 2001 From: Luke Hinds Date: Wed, 22 Jan 2025 12:31:56 +0000 Subject: [PATCH 2/5] Fix docs --- docs/cli.md | 102 ++++++++++++++++-------- docs/configuration.md | 181 +++++++++++++++++++----------------------- docs/logging.md | 2 +- 3 files changed, 150 insertions(+), 135 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index 825363e6..b0e0fdd4 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -47,58 +47,100 @@ codegate serve [OPTIONS] - `--prompts FILE`: Path to YAML prompts file - Optional - Must be a valid YAML file + - Overrides default prompts and configuration file prompts -- `--vllm-url TEXT`: vLLM provider URL +- `--vllm-url TEXT`: vLLM provider URL (default: `http://localhost:8000`) - Optional - - Default: http://localhost:8000/v1 + - Base URL for vLLM provider (/v1 path is added automatically) + - Overrides configuration file and environment variables -- `--openai-url TEXT`: OpenAI provider URL +- `--openai-url TEXT`: OpenAI provider URL (default: + `https://api.openai.com/v1`) - Optional - - Default: https://api.openai.com/v1 + - Base URL for OpenAI provider + - Overrides configuration file and environment variables + +- `--anthropic-url TEXT`: Anthropic provider URL (default: + `https://api.anthropic.com/v1`) + - Optional + - Base URL for Anthropic provider + - Overrides configuration file and environment variables -- `--anthropic-url TEXT`: Anthropic provider URL +- `--ollama-url TEXT`: Ollama provider URL (default: `http://localhost:11434`) - Optional - - Default: https://api.anthropic.com/v1 + - Base URL for Ollama provider (/api path is added automatically) + - Overrides configuration file and environment variables -- `--ollama-url TEXT`: Ollama provider URL +- `--model-base-path TEXT`: Base path for loading models needed for the system - Optional - - Default: http://localhost:11434/api -- `--model-base-path TEXT`: Path to model base directory +- `--embedding-model TEXT`: Name of the model used for embeddings - Optional - - Default: ./codegate_volume/models -- `--embedding-model TEXT`: Name of embedding model +- `--db-path TEXT`: Path to a SQLite DB. Will be created if it doesn't exist. + (default: `./codegate_volume/db/codegate.db`) - Optional - - Default: all-minilm-L6-v2-q5_k_m.gguf + - Overrides configuration file and environment variables + +### `show-prompts` + +Display the loaded system prompts: + +```bash +codegate show-prompts [OPTIONS] +``` + +#### Options + +- `--prompts FILE`: Path to YAML prompts file + - Optional + - Must be a valid YAML file + - If not provided, shows default prompts from `prompts/default.yaml` + +### `generate_certs` -- `--certs-dir TEXT`: Directory for certificate files +Generate certificates for the CodeGate server. + +```bash +codegate generate-certs [OPTIONS] +``` + +#### Options + +- `--certs-out-dir PATH`: Directory path where the certificates are generated + (default: ./codegate_volume/certs) - Optional - - Default: ./certs + - Overrides configuration file and environment variables -- `--ca-cert TEXT`: CA certificate file name +- `--ca-cert-name TEXT`: Name that will be given to the created CA certificate + (default: ca.crt) - Optional - - Default: ca.crt + - Overrides configuration file and environment variables -- `--ca-key TEXT`: CA key file name +- `--ca-key-name TEXT`: Name that will be given to the created CA key (default: + ca.key) - Optional - - Default: ca.key + - Overrides configuration file and environment variables -- `--server-cert TEXT`: Server certificate file name +- `--server-cert-name TEXT`: Name that will be given to the created server + certificate (default: server.crt) - Optional - - Default: server.crt + - Overrides configuration file and environment variables -- `--server-key TEXT`: Server key file name +- `--server-key-name TEXT`: Name that will be given to the created server key + (default: server.key) - Optional - - Default: server.key + - Overrides configuration file and environment variables -- `--db-path TEXT`: Path to main SQLite database file +- `--log-level [ERROR|WARNING|INFO|DEBUG]`: Set the log level (default: INFO) - Optional - - Default: ./codegate_volume/db/codegate.db + - Case-insensitive + - Overrides configuration file and environment variables -- `--vec-db-path TEXT`: Path to vector SQLite database file +- `--log-format [JSON|TEXT]`: Set the log format (default: JSON) - Optional - - Default: ./sqlite_data/vectordb.db + - Case-insensitive + - Overrides configuration file and environment variables - `--enable-litellm`: Enable LiteLLM logging - Optional flag @@ -139,12 +181,6 @@ Start server with custom logging: codegate serve --log-level DEBUG --log-format TEXT ``` -Start server with LiteLLM logging enabled: - -```bash -codegate serve --enable-litellm --log-level DEBUG -``` - Start server with configuration file: ```bash @@ -187,4 +223,4 @@ Generate certificates with default settings: codegate generate-certs ``` - + \ No newline at end of file diff --git a/docs/configuration.md b/docs/configuration.md index 10d8e576..6f4c16f4 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,67 +1,81 @@ -# Configuration +# Configuration system -CodeGate's configuration system provides flexible configuration through multiple -methods with clear priority resolution. +The configuration system in CodeGate is managed through the `Config` class in +`config.py`. It supports multiple configuration sources with a clear priority +order. -## Configuration methods +## Configuration priority -Configuration can be set through: +Configuration sources are evaluated in the following order, from highest to +lowest priority: -1. CLI arguments (highest priority) +1. CLI arguments 2. Environment variables -3. Configuration file -4. Default values (lowest priority) +3. Configuration file (YAML) +4. Default values (including default prompts from `prompts/default.yaml`) + +Values from higher-priority sources take precedence over lower-priority values. + +## Default configuration values + +- Port: `8989` +- Proxy port: `8990` +- Host: `"localhost"` +- Log level: `"INFO"` +- Log format: `"JSON"` +- Prompts: default prompts from `prompts/default.yaml` +- Provider URLs: + - vLLM: `"http://localhost:8000"` + - OpenAI: `"https://api.openai.com/v1"` + - Anthropic: `"https://api.anthropic.com/v1"` + - Ollama: `"http://localhost:11434"` +- Certificate configuration: + - Certs directory: `"./certs"` + - CA certificate: `"ca.crt"` + - CA private key: `"ca.key"` + - Server certificate: `"server.crt"` + - Server private key: `"server.key"` + +- External logger configuration + - external_loggers: + - litellm: `false` + - sqlalchemy: `false` + - uvicorn.error: `false` + - aiosqlite: `false` + +## Configuration methods + +### Configuration file -## Configuration file +Load configuration from a YAML file: + +```python +config = Config.from_file("config.yaml") +``` -The configuration file uses YAML format: +Example config.yaml: ```yaml -# Network settings port: 8989 proxy_port: 8990 -host: "localhost" - -# Logging configuration -log_level: INFO # ERROR, WARNING, INFO, or DEBUG -log_format: JSON # JSON or TEXT - -# External logger configuration -external_loggers: - litellm: false # Enable/disable LiteLLM logging (includes LiteLLM Proxy, Router, and core) - sqlalchemy: false # Enable/disable SQLAlchemy logging - uvicorn.error: false # Enable/disable Uvicorn error logging - aiosqlite: false # Enable/disable aiosqlite logging - -# Model configuration -model_base_path: "./codegate_volume/models" -chat_model_n_ctx: 32768 -chat_model_n_gpu_layers: -1 -embedding_model: "all-minilm-L6-v2-q5_k_m.gguf" - -# Database configuration -db_path: "./codegate_volume/db/codegate.db" -vec_db_path: "./sqlite_data/vectordb.db" - -# Certificate configuration -certs_dir: "./codegate_volume/certs" -ca_cert: "ca.crt" -ca_key: "ca.key" -server_cert: "server.crt" -server_key: "server.key" -force_certs: false - -# Provider URLs +host: localhost +log_level: INFO +log_format: JSON provider_urls: - vllm: "http://localhost:8000" + vllm: "https://vllm.example.com" openai: "https://api.openai.com/v1" anthropic: "https://api.anthropic.com/v1" ollama: "http://localhost:11434" +certs_dir: "./certs" +ca_cert: "ca.crt" +ca_key: "ca.key" +server_cert: "server.crt" +server_key: "server.key" ``` -## Environment variables +### Environment variables -Environment variables follow the pattern `CODEGATE_*`: +Environment variables are automatically loaded with these mappings: - `CODEGATE_APP_PORT`: server port - `CODEGATE_APP_PROXY_PORT`: server proxy port @@ -115,54 +129,24 @@ Network settings can be configured in several ways: codegate serve --port 8989 --proxy-port 8990 --host localhost ``` -### Logging configuration - -Logging can be configured through: - -1. Configuration file: - - ```yaml - log_level: DEBUG - log_format: TEXT - external_loggers: - litellm: true - sqlalchemy: false - uvicorn.error: false - aiosqlite: false - ``` - -2. Environment variables: - - ```bash - export CODEGATE_APP_LOG_LEVEL=DEBUG - export CODEGATE_LOG_FORMAT=TEXT - export CODEGATE_ENABLE_LITELLM=true - ``` - -3. CLI flags: - - ```bash - codegate serve --log-level DEBUG --log-format TEXT --enable-litellm - ``` - ### Provider URLs -Provider URLs can be configured through: +Provider URLs can be configured in several ways: 1. Configuration file: ```yaml provider_urls: - vllm: "http://localhost:8000" + vllm: "https://vllm.example.com" # /v1 path is added automatically openai: "https://api.openai.com/v1" anthropic: "https://api.anthropic.com/v1" - ollama: "http://localhost:11434" + ollama: "http://localhost:11434" # /api path is added automatically ``` 2. Environment variables: ```bash - export CODEGATE_PROVIDER_VLLM_URL=http://localhost:8000 + export CODEGATE_PROVIDER_VLLM_URL=https://vllm.example.com export CODEGATE_PROVIDER_OPENAI_URL=https://api.openai.com/v1 export CODEGATE_PROVIDER_ANTHROPIC_URL=https://api.anthropic.com/v1 export CODEGATE_PROVIDER_OLLAMA_URL=http://localhost:11434 @@ -171,12 +155,19 @@ Provider URLs can be configured through: 3. CLI flags: ```bash - codegate serve --vllm-url http://localhost:8000 --openai-url https://api.openai.com/v1 + codegate serve --vllm-url https://vllm.example.com --ollama-url http://localhost:11434 ``` +Note: + +- For the vLLM provider, the `/v1` path is automatically appended to the base + URL if not present. +- For the Ollama provider, the `/api` path is automatically appended to the base + URL if not present. + ### Certificate configuration -Certificate settings can be configured through: +Certificate files can be configured in several ways: 1. Configuration file: @@ -220,30 +211,18 @@ Available log formats (case-insensitive): - `JSON` - `TEXT` -### External loggers +### External logger configuration -External logger configuration controls logging for third-party components: +External loggers can be configured in several ways: 1. Configuration file: + ```yaml external_loggers: - litellm: false # LiteLLM logging (Proxy, Router, core) - sqlalchemy: false # SQLAlchemy logging - uvicorn.error: false # Uvicorn error logging - aiosqlite: false # aiosqlite logging - ``` - -2. Environment variables: - ```bash - export CODEGATE_ENABLE_LITELLM=true - export CODEGATE_ENABLE_SQLALCHEMY=true - export CODEGATE_ENABLE_UVICORN_ERROR=true - export CODEGATE_ENABLE_AIOSQLITE=true - ``` - -3. CLI flags: - ```bash - codegate serve --enable-litellm + litellm: false + sqlalchemy: false + uvicorn.error: false + aiosqlite: false ``` ### Prompts configuration diff --git a/docs/logging.md b/docs/logging.md index 0666cac7..7f348f73 100644 --- a/docs/logging.md +++ b/docs/logging.md @@ -123,7 +123,7 @@ The logging system can be configured through: 1. CLI arguments: ```bash - codegate serve --log-level DEBUG --log-format TEXT --enable-litellm + codegate serve --log-level DEBUG --log-format TEXT --enable-litellm # to enable LiteLLM debug ``` 2. Environment variables: From 6e84fb6eb3754dfd1c3be103d306e944c641662b Mon Sep 17 00:00:00 2001 From: Luke Hinds Date: Wed, 22 Jan 2025 12:59:31 +0000 Subject: [PATCH 3/5] Fix tests --- src/codegate/codegate_logging.py | 24 +++++++++- src/codegate/config.py | 40 +++++++++++----- tests/test_config.py | 81 ++++++++++++++++++++++++++++++++ tests/test_logging.py | 39 +++++++++++++++ tests/test_server.py | 35 +++++++++++--- 5 files changed, 199 insertions(+), 20 deletions(-) diff --git a/src/codegate/codegate_logging.py b/src/codegate/codegate_logging.py index 44fe1380..36408231 100644 --- a/src/codegate/codegate_logging.py +++ b/src/codegate/codegate_logging.py @@ -63,12 +63,34 @@ def configure_litellm_logging(enabled: bool = False, level: LogLevel = LogLevel. enabled: Whether to enable LiteLLM logging level: Log level to use if enabled """ + # Configure the main litellm logger + logger = logging.getLogger("litellm") + logger.disabled = not enabled + if not enabled: + logger.setLevel(logging.CRITICAL + 1) # Effectively disables all logging + else: + logger.setLevel(getattr(logging, level.value)) + logger.propagate = False + # Clear any existing handlers + logger.handlers.clear() + # Add a handler to ensure logs are properly routed + handler = logging.StreamHandler() + handler.setLevel(getattr(logging, level.value)) + logger.addHandler(handler) + + # Also configure the specific LiteLLM loggers for logger_name in LITELLM_LOGGERS: logger = logging.getLogger(logger_name) + logger.disabled = not enabled if not enabled: - logger.setLevel(logging.CRITICAL + 1) # Effectively disables all logging + logger.setLevel(logging.CRITICAL + 1) else: logger.setLevel(getattr(logging, level.value)) + logger.propagate = False + logger.handlers.clear() + handler = logging.StreamHandler() + handler.setLevel(getattr(logging, level.value)) + logger.addHandler(handler) def add_origin(logger, log_method, event_dict): diff --git a/src/codegate/config.py b/src/codegate/config.py index 3ba1b433..95c524dd 100644 --- a/src/codegate/config.py +++ b/src/codegate/config.py @@ -137,6 +137,14 @@ def from_file(cls, config_path: Union[str, Path]) -> "Config": if "provider_urls" in config_data: provider_urls.update(config_data.pop("provider_urls")) + # Get default external loggers + default_external_loggers = { + "litellm": False, + "sqlalchemy": False, + "uvicorn.error": False, + "aiosqlite": False + } + return cls( port=config_data.get("port", cls.port), proxy_port=config_data.get("proxy_port", cls.proxy_port), @@ -159,6 +167,7 @@ def from_file(cls, config_path: Union[str, Path]) -> "Config": force_certs=config_data.get("force_certs", cls.force_certs), prompts=prompts_config, provider_urls=provider_urls, + external_loggers=config_data.get("external_loggers", default_external_loggers), ) except yaml.YAMLError as e: raise ConfigurationError(f"Failed to parse config file: {e}") @@ -170,12 +179,15 @@ def from_env(cls) -> "Config": """Load configuration from environment variables. Returns: - Config: Configuration instance + Config: Configuration instance with values from environment variables. + + Raises: + ConfigurationError: If an environment variable has an invalid value. """ try: - # Start with default prompts - config = cls(prompts=cls._load_default_prompts()) + config = cls() + # Load basic configuration if "CODEGATE_APP_PORT" in os.environ: config.port = int(os.environ["CODEGATE_APP_PORT"]) if "CODEGATE_APP_PROXY_PORT" in os.environ: @@ -183,15 +195,15 @@ def from_env(cls) -> "Config": if "CODEGATE_APP_HOST" in os.environ: config.host = os.environ["CODEGATE_APP_HOST"] if "CODEGATE_APP_LOG_LEVEL" in os.environ: - config.log_level = LogLevel(os.environ["CODEGATE_APP_LOG_LEVEL"]) + config.log_level = LogLevel(os.environ["CODEGATE_APP_LOG_LEVEL"].upper()) if "CODEGATE_LOG_FORMAT" in os.environ: - config.log_format = LogFormat(os.environ["CODEGATE_LOG_FORMAT"]) + config.log_format = LogFormat(os.environ["CODEGATE_LOG_FORMAT"].upper()) if "CODEGATE_PROMPTS_FILE" in os.environ: - config.prompts = PromptConfig.from_file( - os.environ["CODEGATE_PROMPTS_FILE"] - ) # noqa: E501 - - # Load certificate configuration from environment + config.prompts = PromptConfig.from_file(os.environ["CODEGATE_PROMPTS_FILE"]) + if "CODEGATE_MODEL_BASE_PATH" in os.environ: + config.model_base_path = os.environ["CODEGATE_MODEL_BASE_PATH"] + if "CODEGATE_EMBEDDING_MODEL" in os.environ: + config.embedding_model = os.environ["CODEGATE_EMBEDDING_MODEL"] if "CODEGATE_CERTS_DIR" in os.environ: config.certs_dir = os.environ["CODEGATE_CERTS_DIR"] if "CODEGATE_CA_CERT" in os.environ: @@ -215,6 +227,12 @@ def from_env(cls) -> "Config": if env_var in os.environ: config.provider_urls[provider] = os.environ[env_var] + # Load external logger configuration from environment variables + for logger_name in config.external_loggers.keys(): + env_var = f"CODEGATE_ENABLE_{logger_name.upper().replace('.', '_')}" + if env_var in os.environ: + config.external_loggers[logger_name] = os.environ[env_var].lower() == "true" + return config except ValueError as e: raise ConfigurationError(f"Invalid environment variable value: {e}") @@ -316,8 +334,6 @@ def load( config.server_cert = env_config.server_cert if "CODEGATE_SERVER_KEY" in os.environ: config.server_key = env_config.server_key - if "CODEGATE_FORCE_CERTS" in os.environ: - config.force_certs = env_config.force_certs if "CODEGATE_DB_PATH" in os.environ: config.db_path = env_config.db_path if "CODEGATE_VEC_DB_PATH" in os.environ: diff --git a/tests/test_config.py b/tests/test_config.py index 94037393..bb401328 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -240,3 +240,84 @@ def test_env_var_priority(config_file_with_format: Path) -> None: assert config.log_format == LogFormat.JSON # env var overrides file finally: del os.environ["CODEGATE_LOG_FORMAT"] + + +def test_external_loggers_from_env() -> None: + """Test loading external logger configuration from environment variables.""" + os.environ.update( + { + "CODEGATE_ENABLE_LITELLM": "true", + "CODEGATE_ENABLE_SQLALCHEMY": "true", + "CODEGATE_ENABLE_UVICORN_ERROR": "false", + "CODEGATE_ENABLE_AIOSQLITE": "false", + } + ) + try: + config = Config.from_env() + assert config.external_loggers["litellm"] is True + assert config.external_loggers["sqlalchemy"] is True + assert config.external_loggers["uvicorn.error"] is False + assert config.external_loggers["aiosqlite"] is False + finally: + for key in [ + "CODEGATE_ENABLE_LITELLM", + "CODEGATE_ENABLE_SQLALCHEMY", + "CODEGATE_ENABLE_UVICORN_ERROR", + "CODEGATE_ENABLE_AIOSQLITE", + ]: + os.environ.pop(key, None) + + +def test_external_loggers_from_config_file(tmp_path: Path) -> None: + """Test loading external logger configuration from config file.""" + config_file = tmp_path / "config.yaml" + config_data = { + "external_loggers": { + "litellm": True, + "sqlalchemy": False, + "uvicorn.error": True, + "aiosqlite": False, + } + } + with open(config_file, "w") as f: + yaml.dump(config_data, f) + + config = Config.from_file(config_file) + assert config.external_loggers["litellm"] is True + assert config.external_loggers["sqlalchemy"] is False + assert config.external_loggers["uvicorn.error"] is True + assert config.external_loggers["aiosqlite"] is False + + +def test_external_loggers_defaults() -> None: + """Test default values for external loggers.""" + config = Config() + assert config.external_loggers["litellm"] is False + assert config.external_loggers["sqlalchemy"] is False + assert config.external_loggers["uvicorn.error"] is False + assert config.external_loggers["aiosqlite"] is False + + +def test_external_loggers_env_override_config(tmp_path: Path) -> None: + """Test environment variables override config file for external loggers.""" + config_file = tmp_path / "config.yaml" + config_data = { + "external_loggers": { + "litellm": False, + "sqlalchemy": False, + "uvicorn.error": False, + "aiosqlite": False, + } + } + with open(config_file, "w") as f: + yaml.dump(config_data, f) + + os.environ["CODEGATE_ENABLE_LITELLM"] = "true" + try: + config = Config.load(config_path=config_file) + assert config.external_loggers["litellm"] is True # env var overrides file + assert config.external_loggers["sqlalchemy"] is False # from file + assert config.external_loggers["uvicorn.error"] is False # from file + assert config.external_loggers["aiosqlite"] is False # from file + finally: + del os.environ["CODEGATE_ENABLE_LITELLM"] diff --git a/tests/test_logging.py b/tests/test_logging.py index 81bf3011..d0ca422c 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -1,4 +1,5 @@ import logging +import sys from io import StringIO import structlog @@ -36,3 +37,41 @@ def test_logging_stream_output(): log_output.seek(0) formatted_log = log_output.getvalue().strip() assert "Debug message" in formatted_log + + +def test_external_logger_configuration(): + # Test enabling litellm logging + setup_logging( + log_level=LogLevel.DEBUG, + log_format=LogFormat.TEXT, + external_loggers={"litellm": True} + ) + litellm_logger = logging.getLogger("litellm") + assert not litellm_logger.disabled + assert litellm_logger.level == logging.DEBUG + + # Test disabling litellm logging + setup_logging( + log_level=LogLevel.DEBUG, + log_format=LogFormat.TEXT, + external_loggers={"litellm": False} + ) + litellm_logger = logging.getLogger("litellm") + assert litellm_logger.disabled + assert litellm_logger.level > logging.CRITICAL + + +def test_external_logger_defaults(): + # Test default behavior (all external loggers disabled) + setup_logging(log_level=LogLevel.DEBUG, log_format=LogFormat.TEXT) + + # Check all external loggers are disabled by default + litellm_logger = logging.getLogger("litellm") + sqlalchemy_logger = logging.getLogger("sqlalchemy") + uvicorn_logger = logging.getLogger("uvicorn.error") + aiosqlite_logger = logging.getLogger("aiosqlite") + + assert litellm_logger.disabled + assert sqlalchemy_logger.disabled + assert uvicorn_logger.disabled + assert aiosqlite_logger.disabled diff --git a/tests/test_server.py b/tests/test_server.py index 8e07c0ee..c747b7be 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -219,7 +219,11 @@ def test_serve_default_options(cli_runner): assert result.exit_code == 0 # Check if the logging setup was called with expected defaults - mock_setup_logging.assert_called_once_with(LogLevel.INFO, LogFormat.JSON) + mock_setup_logging.assert_called_once_with( + LogLevel.INFO, + LogFormat.JSON, + {"litellm": False, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False} + ) # Validate run_servers was called once mock_run.assert_called_once() @@ -254,6 +258,7 @@ def test_serve_custom_options(cli_runner): "custom-server.crt", "--server-key", "custom-server.key", + "--enable-litellm", ], ) @@ -261,7 +266,11 @@ def test_serve_custom_options(cli_runner): assert result.exit_code == 0 # Assert logging setup was called with the provided log level and format - mock_setup_logging.assert_called_once_with(LogLevel.DEBUG, LogFormat.TEXT) + mock_setup_logging.assert_called_once_with( + LogLevel.DEBUG, + LogFormat.TEXT, + {"litellm": True, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False} + ) # Validate run_servers was called once mock_run.assert_called_once() @@ -328,7 +337,11 @@ def test_serve_with_config_file(cli_runner, temp_config_file): # Assertions to ensure the CLI ran successfully assert result.exit_code == 0 - mock_setup_logging.assert_called_once_with(LogLevel.DEBUG, LogFormat.JSON) + mock_setup_logging.assert_called_once_with( + LogLevel.DEBUG, + LogFormat.JSON, + {"litellm": False, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False} + ) # Validate that run_servers was called with the expected configuration mock_run.assert_called_once() @@ -397,7 +410,11 @@ def test_serve_priority_resolution(cli_runner: CliRunner, temp_config_file: Path assert result.exit_code == 0 # Ensure logging setup was called with the highest priority settings (CLI arguments) - mock_setup_logging.assert_called_once_with("ERROR", "TEXT") + mock_setup_logging.assert_called_once_with( + LogLevel.ERROR, + LogFormat.TEXT, + {"litellm": False, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False} + ) # Verify that the run_servers was called with the overridden settings config_arg = mock_run.call_args[0][0] # Assuming Config is the first positional arg @@ -405,8 +422,8 @@ def test_serve_priority_resolution(cli_runner: CliRunner, temp_config_file: Path expected_values = { "port": 8080, "host": "example.com", - "log_level": "ERROR", - "log_format": "TEXT", + "log_level": LogLevel.ERROR, + "log_format": LogFormat.TEXT, "certs_dir": "./cli-certs", "ca_cert": "cli-ca.crt", "ca_key": "cli-ca.key", @@ -449,7 +466,11 @@ def test_serve_certificate_options(cli_runner: CliRunner) -> None: assert result.exit_code == 0 # Ensure logging setup was called with expected arguments - mock_setup_logging.assert_called_once_with("INFO", "JSON") + mock_setup_logging.assert_called_once_with( + LogLevel.INFO, + LogFormat.JSON, + {"litellm": False, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False} + ) # Verify that run_servers was called with the provided certificate options config_arg = mock_run.call_args[0][0] # Assuming Config is the first positional arg From ff032a145aed37226e0628a3abf97849746e22e8 Mon Sep 17 00:00:00 2001 From: Luke Hinds Date: Wed, 22 Jan 2025 13:01:21 +0000 Subject: [PATCH 4/5] Fix tests --- src/codegate/cli.py | 2 +- src/codegate/codegate_logging.py | 23 ++++++++--------------- src/codegate/config.py | 16 +++++++++------- tests/test_logging.py | 11 +++-------- tests/test_server.py | 20 ++++++++++---------- 5 files changed, 31 insertions(+), 41 deletions(-) diff --git a/src/codegate/cli.py b/src/codegate/cli.py index f1ee8a2c..785186b6 100644 --- a/src/codegate/cli.py +++ b/src/codegate/cli.py @@ -4,7 +4,7 @@ import signal import sys from pathlib import Path -from typing import Dict, Optional +from typing import Optional import click import structlog diff --git a/src/codegate/codegate_logging.py b/src/codegate/codegate_logging.py index 36408231..67c7efdb 100644 --- a/src/codegate/codegate_logging.py +++ b/src/codegate/codegate_logging.py @@ -3,7 +3,7 @@ import sys from datetime import datetime from enum import Enum -from typing import Any, Dict, List, Optional +from typing import Any, Dict, Optional import structlog @@ -49,16 +49,12 @@ def _missing_(cls, value: str) -> Optional["LogFormat"]: # Define all LiteLLM logger names -LITELLM_LOGGERS = [ - "LiteLLM Proxy", - "LiteLLM Router", - "LiteLLM" -] +LITELLM_LOGGERS = ["LiteLLM Proxy", "LiteLLM Router", "LiteLLM"] def configure_litellm_logging(enabled: bool = False, level: LogLevel = LogLevel.INFO) -> None: """Configure LiteLLM logging. - + Args: enabled: Whether to enable LiteLLM logging level: Log level to use if enabled @@ -103,9 +99,9 @@ def add_origin(logger, log_method, event_dict): def setup_logging( - log_level: Optional[LogLevel] = None, + log_level: Optional[LogLevel] = None, log_format: Optional[LogFormat] = None, - external_loggers: Optional[Dict[str, bool]] = None + external_loggers: Optional[Dict[str, bool]] = None, ) -> logging.Logger: """Configure the logging system. @@ -126,16 +122,13 @@ def setup_logging( if external_loggers is None: external_loggers = { "litellm": False, - "sqlalchemy": False, + "sqlalchemy": False, "uvicorn.error": False, - "aiosqlite": False + "aiosqlite": False, } # Configure LiteLLM logging based on external_loggers setting - configure_litellm_logging( - enabled=external_loggers.get("litellm", False), - level=log_level - ) + configure_litellm_logging(enabled=external_loggers.get("litellm", False), level=log_level) # The configuration was taken from structlog documentation # https://www.structlog.org/en/stable/standard-library.html diff --git a/src/codegate/config.py b/src/codegate/config.py index 95c524dd..f07ac948 100644 --- a/src/codegate/config.py +++ b/src/codegate/config.py @@ -39,12 +39,14 @@ class Config: prompts: PromptConfig = field(default_factory=PromptConfig) # External logger configuration - external_loggers: Dict[str, bool] = field(default_factory=lambda: { - "litellm": False, - "sqlalchemy": False, - "uvicorn.error": False, - "aiosqlite": False - }) + external_loggers: Dict[str, bool] = field( + default_factory=lambda: { + "litellm": False, + "sqlalchemy": False, + "uvicorn.error": False, + "aiosqlite": False, + } + ) model_base_path: str = "./codegate_volume/models" chat_model_n_ctx: int = 32768 @@ -142,7 +144,7 @@ def from_file(cls, config_path: Union[str, Path]) -> "Config": "litellm": False, "sqlalchemy": False, "uvicorn.error": False, - "aiosqlite": False + "aiosqlite": False, } return cls( diff --git a/tests/test_logging.py b/tests/test_logging.py index d0ca422c..9436d261 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -1,5 +1,4 @@ import logging -import sys from io import StringIO import structlog @@ -42,9 +41,7 @@ def test_logging_stream_output(): def test_external_logger_configuration(): # Test enabling litellm logging setup_logging( - log_level=LogLevel.DEBUG, - log_format=LogFormat.TEXT, - external_loggers={"litellm": True} + log_level=LogLevel.DEBUG, log_format=LogFormat.TEXT, external_loggers={"litellm": True} ) litellm_logger = logging.getLogger("litellm") assert not litellm_logger.disabled @@ -52,9 +49,7 @@ def test_external_logger_configuration(): # Test disabling litellm logging setup_logging( - log_level=LogLevel.DEBUG, - log_format=LogFormat.TEXT, - external_loggers={"litellm": False} + log_level=LogLevel.DEBUG, log_format=LogFormat.TEXT, external_loggers={"litellm": False} ) litellm_logger = logging.getLogger("litellm") assert litellm_logger.disabled @@ -64,7 +59,7 @@ def test_external_logger_configuration(): def test_external_logger_defaults(): # Test default behavior (all external loggers disabled) setup_logging(log_level=LogLevel.DEBUG, log_format=LogFormat.TEXT) - + # Check all external loggers are disabled by default litellm_logger = logging.getLogger("litellm") sqlalchemy_logger = logging.getLogger("sqlalchemy") diff --git a/tests/test_server.py b/tests/test_server.py index c747b7be..e820e7b3 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -220,9 +220,9 @@ def test_serve_default_options(cli_runner): # Check if the logging setup was called with expected defaults mock_setup_logging.assert_called_once_with( - LogLevel.INFO, + LogLevel.INFO, LogFormat.JSON, - {"litellm": False, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False} + {"litellm": False, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False}, ) # Validate run_servers was called once @@ -267,9 +267,9 @@ def test_serve_custom_options(cli_runner): # Assert logging setup was called with the provided log level and format mock_setup_logging.assert_called_once_with( - LogLevel.DEBUG, + LogLevel.DEBUG, LogFormat.TEXT, - {"litellm": True, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False} + {"litellm": True, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False}, ) # Validate run_servers was called once @@ -338,9 +338,9 @@ def test_serve_with_config_file(cli_runner, temp_config_file): # Assertions to ensure the CLI ran successfully assert result.exit_code == 0 mock_setup_logging.assert_called_once_with( - LogLevel.DEBUG, + LogLevel.DEBUG, LogFormat.JSON, - {"litellm": False, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False} + {"litellm": False, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False}, ) # Validate that run_servers was called with the expected configuration @@ -411,9 +411,9 @@ def test_serve_priority_resolution(cli_runner: CliRunner, temp_config_file: Path # Ensure logging setup was called with the highest priority settings (CLI arguments) mock_setup_logging.assert_called_once_with( - LogLevel.ERROR, + LogLevel.ERROR, LogFormat.TEXT, - {"litellm": False, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False} + {"litellm": False, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False}, ) # Verify that the run_servers was called with the overridden settings @@ -467,9 +467,9 @@ def test_serve_certificate_options(cli_runner: CliRunner) -> None: # Ensure logging setup was called with expected arguments mock_setup_logging.assert_called_once_with( - LogLevel.INFO, + LogLevel.INFO, LogFormat.JSON, - {"litellm": False, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False} + {"litellm": False, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False}, ) # Verify that run_servers was called with the provided certificate options From b9853c453c09c1d313632bb8800228ab144aa55c Mon Sep 17 00:00:00 2001 From: Luke Hinds Date: Fri, 24 Jan 2025 20:41:06 +0000 Subject: [PATCH 5/5] Remove from CLI --- config.yaml.example | 5 +---- docs/cli.md | 6 ------ docs/logging.md | 19 ++++--------------- src/codegate/cli.py | 10 ---------- tests/test_server.py | 8 -------- 5 files changed, 5 insertions(+), 43 deletions(-) diff --git a/config.yaml.example b/config.yaml.example index 3e7d0914..975223fa 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -76,7 +76,4 @@ external_loggers: # CODEGATE_ENABLE_SQLALCHEMY=true # CODEGATE_ENABLE_UVICORN_ERROR=true # CODEGATE_ENABLE_AIOSQLITE=true -# 2. CLI arguments: -# --enable-litellm # Enables LiteLLM logging -# --enable-sqlalchemy # Enables SQLAlchemy logging -# etc. + diff --git a/docs/cli.md b/docs/cli.md index b0e0fdd4..4dfef422 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -142,12 +142,6 @@ codegate generate-certs [OPTIONS] - Case-insensitive - Overrides configuration file and environment variables -- `--enable-litellm`: Enable LiteLLM logging - - Optional flag - - Default: false - - Enables logging for LiteLLM Proxy, Router, and core components - - Overrides configuration file and environment variables - ## Error handling The CLI provides user-friendly error messages for: diff --git a/docs/logging.md b/docs/logging.md index 7f348f73..954800e0 100644 --- a/docs/logging.md +++ b/docs/logging.md @@ -18,17 +18,12 @@ CodeGate provides control over external loggers through configuration: LiteLLM logging can be controlled through: -1. CLI flag: - ```bash - codegate serve --enable-litellm - ``` - -2. Environment variable: +1. Environment variable: ```bash export CODEGATE_ENABLE_LITELLM=true ``` -3. Configuration file: +2. Configuration file: ```yaml external_loggers: litellm: true # Enable/disable LiteLLM logging (includes LiteLLM Proxy, Router, and core) @@ -120,13 +115,7 @@ except Exception as e: The logging system can be configured through: -1. CLI arguments: - - ```bash - codegate serve --log-level DEBUG --log-format TEXT --enable-litellm # to enable LiteLLM debug - ``` - -2. Environment variables: +1. Environment variables: ```bash export CODEGATE_APP_LOG_LEVEL=DEBUG @@ -134,7 +123,7 @@ The logging system can be configured through: export CODEGATE_ENABLE_LITELLM=true ``` -3. Configuration file: +2. Configuration file: ```yaml log_level: DEBUG diff --git a/src/codegate/cli.py b/src/codegate/cli.py index 785186b6..8a428ab9 100644 --- a/src/codegate/cli.py +++ b/src/codegate/cli.py @@ -246,12 +246,6 @@ def show_prompts(prompts: Optional[Path]) -> None: default=None, help="Path to the vector SQLite database file (default: ./sqlite_data/vectordb.db)", ) -@click.option( - "--enable-litellm", - is_flag=True, - default=False, - help="Enable LiteLLM logging (includes LiteLLM Proxy, Router, and core)", -) def serve( port: Optional[int], proxy_port: Optional[int], @@ -273,7 +267,6 @@ def serve( ca_key: Optional[str], server_cert: Optional[str], server_key: Optional[str], - enable_litellm: bool, ) -> None: """Start the codegate server.""" try: @@ -288,8 +281,6 @@ def serve( if ollama_url: cli_provider_urls["ollama"] = ollama_url - # Create external loggers dictionary from CLI arguments - cli_external_loggers = {"litellm": enable_litellm} # Load configuration with priority resolution cfg = Config.load( @@ -301,7 +292,6 @@ def serve( cli_log_level=log_level, cli_log_format=log_format, cli_provider_urls=cli_provider_urls if cli_provider_urls else None, - cli_external_loggers=cli_external_loggers, model_base_path=model_base_path, embedding_model=embedding_model, certs_dir=certs_dir, diff --git a/tests/test_server.py b/tests/test_server.py index e820e7b3..97d24e64 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -258,20 +258,12 @@ def test_serve_custom_options(cli_runner): "custom-server.crt", "--server-key", "custom-server.key", - "--enable-litellm", ], ) # Check the command executed successfully assert result.exit_code == 0 - # Assert logging setup was called with the provided log level and format - mock_setup_logging.assert_called_once_with( - LogLevel.DEBUG, - LogFormat.TEXT, - {"litellm": True, "sqlalchemy": False, "uvicorn.error": False, "aiosqlite": False}, - ) - # Validate run_servers was called once mock_run.assert_called_once() # Retrieve the actual Config object passed to run_servers