from __future__ import annotations import argparse import json from collections.abc import Sequence from pathlib import Path from typing import Any from .gateway import build_models_response, route_chat_payload from .platform import ( build_opencode_provider_config, build_registry_analytics, catalog_models, compare_models, route_scenario_matrix, ) from .registry import RoutingRequest, load_model_registry from .router import SovereignModelRouter def _load_json_mapping(path: Path) -> dict[str, Any]: payload = json.loads(path.read_text(encoding="utf-8-sig")) if not isinstance(payload, dict): raise ValueError(f"Expected mapping in {path}") return payload def _route(args: argparse.Namespace) -> int: models = load_model_registry(args.registry) payload = _load_json_mapping(args.request) decision = SovereignModelRouter(models).route(RoutingRequest.from_payload(payload)) print(json.dumps(decision.to_dict(), indent=2, sort_keys=True)) return 0 if decision.selected_model is not None else 2 def _models(args: argparse.Namespace) -> int: models = load_model_registry(args.registry) print(json.dumps(build_models_response(models), indent=2, sort_keys=True)) return 0 def _chat_dry_run(args: argparse.Namespace) -> int: models = load_model_registry(args.registry) payload = _load_json_mapping(args.request) result = route_chat_payload(payload, models) print(json.dumps(result, indent=2, sort_keys=True)) selected = result["route_decision"]["selected_model"] return 0 if selected is not None else 2 def _catalog(args: argparse.Namespace) -> int: models = load_model_registry(args.registry) result = catalog_models( models, { "search": args.search, "task_type": args.task, "runtime": args.runtime, "modality": args.modality, "jurisdiction": args.jurisdiction, "min_context_tokens": args.min_context_tokens, "requires_json": args.requires_json, "requires_tools": args.requires_tools, "requires_local": args.requires_local, "limit": args.limit, }, ) print(json.dumps(result, indent=2, sort_keys=True)) return 0 def _compare(args: argparse.Namespace) -> int: models = load_model_registry(args.registry) model_ids = [item.strip() for value in args.model for item in value.split(",") if item.strip()] request = _load_json_mapping(args.request) if args.request else None result = compare_models(models, model_ids, request) print(json.dumps(result, indent=2, sort_keys=True)) return 0 if result["ok"] else 2 def _matrix(args: argparse.Namespace) -> int: models = load_model_registry(args.registry) result = route_scenario_matrix(models) print(json.dumps(result, indent=2, sort_keys=True)) return 0 if result["ok"] or args.allow_blocked_exit_zero else 2 def _analytics(args: argparse.Namespace) -> int: print(json.dumps(build_registry_analytics(load_model_registry(args.registry)), indent=2, sort_keys=True)) return 0 def _opencode_config(args: argparse.Namespace) -> int: models = load_model_registry(args.registry) result = build_opencode_provider_config(models, base_url=args.base_url) print(json.dumps(result, indent=2, sort_keys=True)) return 0 def _ui(args: argparse.Namespace) -> int: from .ui import run_ui return run_ui( registry_path=args.registry, host=args.host, port=args.port, open_browser=args.open, smoke=args.smoke, ) def _serve(args: argparse.Namespace) -> int: from .server import serve_gateway return serve_gateway( registry_path=args.registry, config_path=args.config, host=args.host, port=args.port, open_browser=args.open, smoke=args.smoke, ) def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( prog="lumynax-marama-route", description="Route requests across LumynaX sovereign model releases.", ) subparsers = parser.add_subparsers(dest="command") route = subparsers.add_parser("route", help="Select a LumynaX model for a request.") route.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.") route.add_argument("--request", type=Path, required=True, help="Routing request JSON.") route.set_defaults(handler=_route) models = subparsers.add_parser( "models", help="Emit an OpenAI-compatible /v1/models response.", ) models.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.") models.set_defaults(handler=_models) chat = subparsers.add_parser( "chat-dry-run", help="Route an OpenAI-compatible chat request without invoking a backend.", ) chat.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.") chat.add_argument("--request", type=Path, required=True, help="OpenAI chat request JSON.") chat.set_defaults(handler=_chat_dry_run) catalog = subparsers.add_parser( "catalog", help="Search and filter the MaramaRoute model catalog.", ) catalog.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.") catalog.add_argument("--search", default="") catalog.add_argument("--task", default="") catalog.add_argument("--runtime", default="") catalog.add_argument("--modality", default="") catalog.add_argument("--jurisdiction", default="NZ") catalog.add_argument("--min-context-tokens", type=int, default=0) catalog.add_argument("--requires-json", action=argparse.BooleanOptionalAction, default=False) catalog.add_argument("--requires-tools", action=argparse.BooleanOptionalAction, default=False) catalog.add_argument("--requires-local", action=argparse.BooleanOptionalAction, default=False) catalog.add_argument("--limit", type=int, default=25) catalog.set_defaults(handler=_catalog) compare = subparsers.add_parser( "compare", help="Compare routed fit for selected model ids.", ) compare.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.") compare.add_argument("--model", action="append", required=True, help="Model id, repeatable or comma-separated.") compare.add_argument("--request", type=Path, default=None, help="Optional routing request JSON.") compare.set_defaults(handler=_compare) matrix = subparsers.add_parser( "matrix", help="Run the built-in sovereign routing scenario matrix.", ) matrix.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.") matrix.add_argument("--allow-blocked-exit-zero", action=argparse.BooleanOptionalAction, default=False) matrix.set_defaults(handler=_matrix) analytics = subparsers.add_parser("analytics", help="Summarise registry coverage.") analytics.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.") analytics.set_defaults(handler=_analytics) opencode = subparsers.add_parser( "opencode-config", help="Emit an OpenCode-compatible MaramaRoute provider config.", ) opencode.add_argument("--registry", type=Path, required=True, help="MaramaRoute model registry JSON.") opencode.add_argument("--base-url", default="http://127.0.0.1:8787/v1") opencode.set_defaults(handler=_opencode_config) ui = subparsers.add_parser( "ui", help="Launch the local MaramaRoute browser platform.", ) ui.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.") ui.add_argument("--host", type=str, default="127.0.0.1") ui.add_argument("--port", type=int, default=8787) ui.add_argument("--open", action=argparse.BooleanOptionalAction, default=False) ui.add_argument("--smoke", action=argparse.BooleanOptionalAction, default=False) ui.set_defaults(handler=_ui) serve = subparsers.add_parser( "serve", help="Run the local OpenAI-compatible MaramaRoute gateway and browser console.", ) serve.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.") serve.add_argument("--config", type=Path, default=None, help="Gateway backend config JSON.") serve.add_argument("--host", type=str, default="127.0.0.1") serve.add_argument("--port", type=int, default=8787) serve.add_argument("--open", action=argparse.BooleanOptionalAction, default=False) serve.add_argument("--smoke", action=argparse.BooleanOptionalAction, default=False) serve.set_defaults(handler=_serve) return parser def main(argv: Sequence[str] | None = None) -> int: parser = build_parser() args = parser.parse_args(argv) handler = getattr(args, "handler", None) if handler is None: parser.print_help() return 0 return int(handler(args)) if __name__ == "__main__": raise SystemExit(main())