diff --git a/llama_stack/cli/stack/run.py b/llama_stack/cli/stack/run.py index 627ee829a5..79f604d1aa 100644 --- a/llama_stack/cli/stack/run.py +++ b/llama_stack/cli/stack/run.py @@ -5,6 +5,7 @@ # the root directory of this source tree. import argparse +import logging import os from pathlib import Path @@ -12,6 +13,8 @@ REPO_ROOT = Path(__file__).parent.parent.parent.parent +logger = logging.getLogger(__name__) + class StackRun(Subcommand): def __init__(self, subparsers: argparse._SubParsersAction): @@ -70,12 +73,10 @@ def _add_arguments(self): type=str, help="Image Type used during the build. This can be either conda or container or venv.", choices=["conda", "container", "venv"], - default="conda", ) def _run_stack_run_cmd(self, args: argparse.Namespace) -> None: import yaml - from termcolor import cprint from llama_stack.distribution.build import ImageType from llama_stack.distribution.configure import parse_and_maybe_upgrade_config @@ -85,10 +86,6 @@ def _run_stack_run_cmd(self, args: argparse.Namespace) -> None: ) from llama_stack.distribution.utils.exec import formulate_run_args, run_with_pty - if not args.config: - self.parser.error("Must specify a config file to run") - return - config_file = Path(args.config) has_yaml_suffix = args.config.endswith(".yaml") template_name = None @@ -115,11 +112,23 @@ def _run_stack_run_cmd(self, args: argparse.Namespace) -> None: self.parser.error( f"File {str(config_file)} does not exist.\n\nPlease run `llama stack build` to generate (and optionally edit) a run.yaml file" ) - return - print(f"Using run configuration: {config_file}") - config_dict = yaml.safe_load(config_file.read_text()) - config = parse_and_maybe_upgrade_config(config_dict) + if not config_file.is_file(): + self.parser.error( + f"Config file must be a valid file path, '{config_file}’ is not a file: type={type(config_file)}" + ) + + logger.info(f"Using run configuration: {config_file}") + + try: + config_dict = yaml.safe_load(config_file.read_text()) + except yaml.parser.ParserError as e: + self.parser.error(f"failed to load config file '{config_file}':\n {e}") + + try: + config = parse_and_maybe_upgrade_config(config_dict) + except AttributeError as e: + self.parser.error(f"failed to parse config file '{config_file}':\n {e}") run_args = formulate_run_args(args.image_type, args.image_name, config, template_name) @@ -129,18 +138,10 @@ def _run_stack_run_cmd(self, args: argparse.Namespace) -> None: for env_var in args.env: if "=" not in env_var: - cprint( - f"Environment variable '{env_var}' must be in KEY=VALUE format", - color="red", - ) - return + self.parser.error(f"Environment variable '{env_var}' must be in KEY=VALUE format") key, value = env_var.split("=", 1) # split on first = only if not key: - cprint( - f"Environment variable '{env_var}' has empty key", - color="red", - ) - return + self.parser.error(f"Environment variable '{env_var}' has empty key") run_args.extend(["--env", f"{key}={value}"]) if args.tls_keyfile and args.tls_certfile: diff --git a/llama_stack/distribution/server/server.py b/llama_stack/distribution/server/server.py index 8fd95498fd..d12340e084 100644 --- a/llama_stack/distribution/server/server.py +++ b/llama_stack/distribution/server/server.py @@ -142,7 +142,7 @@ def handle_signal(app, signum, _) -> None: not block the current execution. """ signame = signal.Signals(signum).name - print(f"Received signal {signame} ({signum}). Exiting gracefully...") + logger.info(f"Received signal {signame} ({signum}). Exiting gracefully...") async def shutdown(): try: @@ -184,9 +184,9 @@ async def shutdown(): @asynccontextmanager async def lifespan(app: FastAPI): - print("Starting up") + logger.info("Starting up") yield - print("Shutting down") + logger.info("Shutting down") for impl in app.__llama_stack_impls__.values(): await impl.shutdown() @@ -352,10 +352,10 @@ def main(): for env_pair in args.env: try: key, value = validate_env_pair(env_pair) - print(f"Setting CLI environment variable {key} => {value}") + logger.info(f"Setting CLI environment variable {key} => {value}") os.environ[key] = value except ValueError as e: - print(f"Error: {str(e)}") + logger.error(f"Error: {str(e)}") sys.exit(1) if args.yaml_config: @@ -363,12 +363,12 @@ def main(): config_file = Path(args.yaml_config) if not config_file.exists(): raise ValueError(f"Config file {config_file} does not exist") - print(f"Using config file: {config_file}") + logger.info(f"Using config file: {config_file}") elif args.template: config_file = Path(REPO_ROOT) / "llama_stack" / "templates" / args.template / "run.yaml" if not config_file.exists(): raise ValueError(f"Template {args.template} does not exist") - print(f"Using template {args.template} config file: {config_file}") + logger.info(f"Using template {args.template} config file: {config_file}") else: raise ValueError("Either --yaml-config or --template must be provided") @@ -376,9 +376,9 @@ def main(): config = replace_env_vars(yaml.safe_load(fp)) config = StackRunConfig(**config) - print("Run configuration:") + logger.info("Run configuration:") safe_config = redact_sensitive_fields(config.model_dump()) - print(yaml.dump(safe_config, indent=2)) + logger.info(yaml.dump(safe_config, indent=2)) app = FastAPI(lifespan=lifespan) app.add_middleware(TracingMiddleware) @@ -387,7 +387,8 @@ def main(): try: impls = asyncio.run(construct_stack(config)) - except InvalidProviderError: + except InvalidProviderError as e: + logger.error(f"Error: {str(e)}") sys.exit(1) if Api.telemetry in impls: @@ -432,7 +433,7 @@ def main(): ) ) - cprint(f"Serving API {api_str}", "white", attrs=["bold"]) + logger.info(f"Serving API {api_str}") for endpoint in endpoints: cprint(f" {endpoint.method.upper()} {endpoint.route}", "white") @@ -462,10 +463,10 @@ def main(): "ssl_keyfile": keyfile, "ssl_certfile": certfile, } - print(f"HTTPS enabled with certificates:\n Key: {keyfile}\n Cert: {certfile}") + logger.info(f"HTTPS enabled with certificates:\n Key: {keyfile}\n Cert: {certfile}") listen_host = ["::", "0.0.0.0"] if not args.disable_ipv6 else "0.0.0.0" - print(f"Listening on {listen_host}:{port}") + logger.info(f"Listening on {listen_host}:{port}") uvicorn_config = { "app": app,