|
17 | 17 | from termcolor import cprint |
18 | 18 |
|
19 | 19 | from ogx.cli.subcommand import Subcommand |
| 20 | +from ogx.core.datatypes import StackConfig |
| 21 | +from ogx.core.distribution import builtin_automatically_routed_apis, get_provider_registry |
| 22 | +from ogx.core.resolver import validate_and_prepare_providers |
| 23 | +from ogx.core.server.server import remove_disabled_providers |
20 | 24 | from ogx.core.stack import run_config_from_dynamic_config_spec |
| 25 | +from ogx.core.utils.config import redact_sensitive_fields |
21 | 26 | from ogx.core.utils.config_dirs import DISTRIBS_BASE_DIR, UI_LOGS_DIR |
22 | 27 | from ogx.core.utils.config_resolution import resolve_config_or_distro |
23 | 28 | from ogx.log import get_logger |
@@ -52,22 +57,25 @@ def add_run_arguments(parser: argparse.ArgumentParser) -> None: |
52 | 57 | default=None, |
53 | 58 | help="Run a stack with only a list of providers. This list is formatted like: api1=provider1,api1=provider2,api2=provider3. Where there can be multiple providers per API.", |
54 | 59 | ) |
| 60 | + parser.add_argument( |
| 61 | + "--dry-run", |
| 62 | + action="store_true", |
| 63 | + help="Validate the config without starting the server.", |
| 64 | + ) |
55 | 65 |
|
56 | 66 |
|
57 | 67 | def run_stack_cmd(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None: |
58 | 68 | import yaml |
59 | 69 |
|
60 | 70 | from ogx.core.configure import parse_and_maybe_upgrade_config |
61 | 71 |
|
62 | | - if args.enable_ui: |
| 72 | + if args.enable_ui and not args.dry_run: |
63 | 73 | env_port = os.getenv("OGX_PORT") |
64 | 74 | ui_port = args.port or (int(env_port) if env_port else None) or 8321 |
65 | 75 | _start_ui_development_server(ui_port) |
66 | 76 |
|
67 | 77 | if args.config: |
68 | 78 | try: |
69 | | - from ogx.core.utils.config_resolution import resolve_config_or_distro |
70 | | - |
71 | 79 | config_file = resolve_config_or_distro(args.config) |
72 | 80 | except ValueError as e: |
73 | 81 | parser.error(str(e)) |
@@ -108,9 +116,30 @@ def run_stack_cmd(args: argparse.Namespace, parser: argparse.ArgumentParser) -> |
108 | 116 | except AttributeError as e: |
109 | 117 | parser.error(f"failed to parse config file '{config_file}':\n {e}") |
110 | 118 |
|
| 119 | + if args.dry_run: |
| 120 | + if not config_file: |
| 121 | + parser.error("--dry-run requires a config file or --providers") |
| 122 | + _dry_run_validate(config, config_file) |
| 123 | + return |
| 124 | + |
111 | 125 | _uvicorn_run(config_file, args, parser) |
112 | 126 |
|
113 | 127 |
|
| 128 | +def _dry_run_validate(config: StackConfig, config_file: Path) -> None: |
| 129 | + routed_apis = builtin_automatically_routed_apis() |
| 130 | + validate_and_prepare_providers( |
| 131 | + run_config=config, |
| 132 | + provider_registry=get_provider_registry(), |
| 133 | + routing_table_apis={x.routing_table_api for x in routed_apis}, |
| 134 | + router_apis={x.router_api for x in routed_apis}, |
| 135 | + ) |
| 136 | + logger.info("Config validation passed", config_file=config_file) |
| 137 | + |
| 138 | + safe_config = redact_sensitive_fields(config.model_dump(mode="json")) |
| 139 | + clean_config = remove_disabled_providers(safe_config) |
| 140 | + print(yaml.dump(clean_config, indent=2, default_flow_style=False, sort_keys=False)) |
| 141 | + |
| 142 | + |
114 | 143 | def _uvicorn_run(config_file: Path | None, args: argparse.Namespace, parser: argparse.ArgumentParser) -> None: |
115 | 144 | if not config_file: |
116 | 145 | parser.error("Config file is required") |
|
0 commit comments