Enhance user configuration management and logging
- Introduced user configuration command to set API key. - Updated README and documentation for user config and logging paths. - Refactored logging to support user-specific log files. - Added tests for user configuration and logging behavior.
This commit is contained in:
@@ -5,6 +5,7 @@ import sys
|
||||
|
||||
import uvicorn
|
||||
|
||||
import nexus_claude_api.config as config
|
||||
from nexus_claude_api.config import (
|
||||
DEFAULT_ENDPOINT_URL,
|
||||
DEFAULT_HOST,
|
||||
@@ -13,7 +14,7 @@ from nexus_claude_api.config import (
|
||||
DEFAULT_SMALL_MODEL,
|
||||
Settings,
|
||||
)
|
||||
from nexus_claude_api.logging_config import configure_logging
|
||||
from nexus_claude_api.logging_config import configure_logging, resolve_log_file
|
||||
from nexus_claude_api.server import create_app
|
||||
from nexus_claude_api.shell import generate_claude_code_powershell
|
||||
|
||||
@@ -31,11 +32,21 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
start.add_argument("--small-model", default=DEFAULT_SMALL_MODEL)
|
||||
start.add_argument("--claude-code", action="store_true")
|
||||
start.add_argument("--verbose", "-v", action="store_true")
|
||||
start.add_argument(
|
||||
"--dev",
|
||||
action="store_true",
|
||||
help="Use local development config and logs in the current directory.",
|
||||
)
|
||||
start.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Validate config and print helper output without starting the server.",
|
||||
)
|
||||
|
||||
config = subparsers.add_parser("config", help="Manage user configuration")
|
||||
config_subparsers = config.add_subparsers(dest="config_command")
|
||||
config_set = config_subparsers.add_parser("set", help="Write user configuration")
|
||||
config_set.add_argument("--api-key", required=True)
|
||||
return parser
|
||||
|
||||
|
||||
@@ -44,10 +55,21 @@ def main(argv: list[str] | None = None) -> int:
|
||||
args = parser.parse_args(argv)
|
||||
if args.command == "start":
|
||||
return run_start(args)
|
||||
if args.command == "config":
|
||||
return run_config(args, parser)
|
||||
parser.print_help()
|
||||
return 0
|
||||
|
||||
|
||||
def run_config(args: argparse.Namespace, parser: argparse.ArgumentParser) -> int:
|
||||
if args.config_command == "set":
|
||||
config_path = config.write_user_config({"api_key": args.api_key})
|
||||
print(f"Wrote user config to {config_path}")
|
||||
return 0
|
||||
parser.parse_args(["config", "--help"])
|
||||
return 0
|
||||
|
||||
|
||||
def run_start(args: argparse.Namespace) -> int:
|
||||
settings = Settings.from_values(
|
||||
host=args.host,
|
||||
@@ -57,12 +79,15 @@ def run_start(args: argparse.Namespace) -> int:
|
||||
model=args.model,
|
||||
small_model=args.small_model,
|
||||
verbose=args.verbose,
|
||||
dev=args.dev,
|
||||
)
|
||||
|
||||
if not settings.api_key:
|
||||
print(
|
||||
"Missing Nexus API key. Add nexus-claude-api.local.json, set "
|
||||
"NEXUS_API_KEY or AWS_BEARER_TOKEN_BEDROCK, or pass --api-key.",
|
||||
"Missing Nexus API key. Run "
|
||||
"`nexus-claude-api config set --api-key <key>`, set NEXUS_API_KEY "
|
||||
"or AWS_BEARER_TOKEN_BEDROCK, pass --api-key, or use --dev with "
|
||||
"nexus-claude-api.local.json.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 2
|
||||
@@ -74,7 +99,10 @@ def run_start(args: argparse.Namespace) -> int:
|
||||
if args.dry_run:
|
||||
return 0
|
||||
|
||||
log_file = configure_logging(verbose=settings.verbose)
|
||||
log_file = configure_logging(
|
||||
verbose=settings.verbose,
|
||||
log_file=resolve_log_file(dev=args.dev),
|
||||
)
|
||||
print(f"Writing detailed logs to {log_file}")
|
||||
app = create_app(settings)
|
||||
uvicorn.run(
|
||||
|
||||
@@ -13,6 +13,8 @@ DEFAULT_OPUS_MODEL = "claude-opus-4.6"
|
||||
DEFAULT_MODEL = DEFAULT_OPUS_MODEL
|
||||
DEFAULT_SMALL_MODEL = DEFAULT_OPUS_MODEL
|
||||
LOCAL_CONFIG_FILENAME = "nexus-claude-api.local.json"
|
||||
USER_CONFIG_DIR = Path.home() / ".config" / "nexus-claude-api"
|
||||
USER_CONFIG_FILE = USER_CONFIG_DIR / "config.json"
|
||||
|
||||
|
||||
MODEL_ID_MAP = {
|
||||
@@ -48,12 +50,13 @@ class Settings:
|
||||
small_model: str = DEFAULT_SMALL_MODEL,
|
||||
verbose: bool = False,
|
||||
require_api_key: bool = True,
|
||||
dev: bool = False,
|
||||
) -> "Settings":
|
||||
local_config = load_local_config()
|
||||
config = load_config(dev=dev)
|
||||
resolved_api_key = (
|
||||
api_key
|
||||
or local_config.get("api_key")
|
||||
or local_config.get("nexus_api_key")
|
||||
or config.get("api_key")
|
||||
or config.get("nexus_api_key")
|
||||
or os.environ.get("NEXUS_API_KEY")
|
||||
or os.environ.get("AWS_BEARER_TOKEN_BEDROCK")
|
||||
)
|
||||
@@ -78,6 +81,30 @@ def resolve_backend_model(model: str) -> str:
|
||||
|
||||
|
||||
def load_local_config(path: str | Path = LOCAL_CONFIG_FILENAME) -> dict[str, str]:
|
||||
return load_config_file(path)
|
||||
|
||||
|
||||
def load_user_config(path: str | Path | None = None) -> dict[str, str]:
|
||||
return load_config_file(path or USER_CONFIG_FILE)
|
||||
|
||||
|
||||
def load_config(*, dev: bool = False) -> dict[str, str]:
|
||||
if dev:
|
||||
return load_local_config()
|
||||
return load_user_config()
|
||||
|
||||
|
||||
def write_user_config(config: dict[str, str], path: str | Path | None = None) -> Path:
|
||||
config_path = Path(path or USER_CONFIG_FILE)
|
||||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
config_path.write_text(
|
||||
json.dumps(config, indent=2, sort_keys=True) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
return config_path
|
||||
|
||||
|
||||
def load_config_file(path: str | Path) -> dict[str, str]:
|
||||
config_path = Path(path)
|
||||
if not config_path.exists():
|
||||
return {}
|
||||
|
||||
@@ -3,9 +3,13 @@ from __future__ import annotations
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import nexus_claude_api.config as config
|
||||
|
||||
LOG_DIR = Path("logs")
|
||||
LOG_FILE = LOG_DIR / "nexus-claude-api.log"
|
||||
LOCAL_LOG_DIR = Path("logs")
|
||||
LOCAL_LOG_FILE = LOCAL_LOG_DIR / "nexus-claude-api.log"
|
||||
USER_LOG_DIR = config.USER_CONFIG_DIR / "logs"
|
||||
USER_LOG_FILE = USER_LOG_DIR / "nexus-claude-api.log"
|
||||
LOG_FILE = USER_LOG_FILE
|
||||
LOG_FORMAT = "%(asctime)s %(levelname)s %(name)s: %(message)s"
|
||||
|
||||
|
||||
@@ -24,3 +28,9 @@ def configure_logging(*, verbose: bool = False, log_file: Path = LOG_FILE) -> Pa
|
||||
app_logger.propagate = False
|
||||
|
||||
return log_file
|
||||
|
||||
|
||||
def resolve_log_file(*, dev: bool = False) -> Path:
|
||||
if dev:
|
||||
return LOCAL_LOG_FILE
|
||||
return config.USER_CONFIG_DIR / "logs" / "nexus-claude-api.log"
|
||||
|
||||
Reference in New Issue
Block a user